@benjaminor-dev/quasar-app-extension-dialog-file-preview
v0.2.2
Published
Dialog file preview for Quasar — images, PDF, video, audio and text via useDialogFilePreview()
Downloads
1,177
Maintainers
Readme
Dialog File Preview para Quasar
Extensión Quasar para previsualizar archivos en un diálogo modal desde cualquier parte de tu app, con una API imperativa basada en composable.
Tabla de contenidos
- Resumen
- Qué es y alcance
- Features principales
- Requisitos y compatibilidad
- Instalación
- Qué agrega la extensión al host
- Uso rápido
- API:
useDialogFilePreview() - Opciones de sesión (
PreviewSessionOptions) - Fuentes de archivo aceptadas
- Tipos de archivo soportados
- Tipos TypeScript exportados
- Utilidades MIME
- Integración opcional con Form Builder
- Entrypoints públicos
- Troubleshooting
Resumen
Flujo recomendado:
- Instalar la extensión en tu app Quasar (
quasar ext add). - Asegurar que el boot de Pinia del host corre antes que el boot de esta extensión.
- Importar
useDialogFilePreview()donde necesites abrir la vista previa. - Llamar
preview.show(archivo)— el diálogo se monta globalmente; no hace falta añadir componentes a tus plantillas.
Qué es y alcance
Dialog File Preview abre un modal a pantalla casi completa con:
- Visor según el tipo MIME (imagen, PDF con controles, video, audio, texto).
- Navegación anterior / siguiente cuando pasas varios archivos (contador en la toolbar).
- Botones de imprimir y descarga del archivo actual (según tipo y opciones de sesión; visibles cuando el visor terminó de cargar).
- Modo fallback (sin visor embebido) para tipos no soportados, con opción de descargar.
- Overlay de carga centralizado en el área del visor (preparación de fuentes + carga del visor activo).
- Opciones de sesión al abrir (
showPrint,showDownload,restrictInteraction, etc.) como segundo argumento deshow().
No es un componente embebible para pegar en una página: la API pública es el composable useDialogFilePreview(), que controla un diálogo global.
Features principales
- API imperativa:
show(),hide(),canPreview(). - Acepta
File,Blob, URL (string) u objeto{ source, name?, mimeType? }. - Lista de archivos en una sola llamada:
show([file1, file2]). - Opciones de sesión opcionales en
show(input, options)(toolbar, título, restricción de copia). - Detección automática del visor según MIME (con heurísticas por nombre/URL y sondeo remoto al abrir).
- Indicador de carga unificado en el viewport (
QInnerLoading, «Cargando archivo...») mientras se normalizan las fuentes y mientras el visor activo termina de cargar. - El diálogo se abre de inmediato al llamar
show(); no hace falta esperar fuera del modal a que termine el sondeo MIME. - PDF con pdf.js (
vue-pdf-embed): paginación, zoom, impresión y carga remota víafetch→ blob. - Impresión integrada para PDF, imágenes y texto (no video/audio).
- Descarga e impresión en toolbar solo cuando el visor terminó de cargar (
ready). - Toolbar responsive: título centrado, galería
‹ N / total ›y acciones imprimir/descargar/cierre. - Gestión interna de URLs
blob:(creación y liberación al cerrar). - Estilos del diálogo incluidos vía CSS de la extensión.
Requisitos y compatibilidad
| Ítem | Valor |
| --- | --- |
| Node | >= 20.0.0 |
| Quasar | ^2.6.0 |
| Vue | ^3.4.18 |
| Pinia | ^2.0.11 | ^3.0.0 |
| Formato del paquete | ES modules |
| CLI recomendado | @quasar/app-vite ^2.x o ^3.x |
| PDF en el paquete | vue-pdf-embed / pdf.js incluidos (sin dependencia extra en el host) |
El boot de Pinia del host debe ejecutarse antes del boot de esta extensión.
Instalación
Agregar la extensión en tu app Quasar:
quasar ext add @benjaminor-dev/dialog-file-previewRemover:
quasar ext remove @benjaminor-dev/dialog-file-previewTras quasar ext add, la extensión registra boots y CSS en tu app automáticamente. No necesitas pasos adicionales en una instalación normal desde npm.
Qué agrega la extensión al host
Al instalar o invocar, la extensión registra en quasar.config:
| Recurso | Ruta npm |
| --- | --- |
| Boot del diálogo | ~@benjaminor-dev/quasar-app-extension-dialog-file-preview/boot/dialog |
| Estilos | ~@benjaminor-dev/quasar-app-extension-dialog-file-preview/main.css |
Orden recomendado de boots:
- Boot Pinia del host (por ejemplo
src/boot/pinia.ts). - Boot dialog de esta extensión (lo registra la extensión automáticamente).
- Resto de boots de tu app.
El boot monta el diálogo en el documento y conecta la store de Pinia del host. No necesitas importar DialogFilePreview en App.vue.
Uso rápido
<script setup lang="ts">import { useDialogFilePreview } from "@benjaminor-dev/quasar-app-extension-dialog-file-preview";
const preview = useDialogFilePreview();
function onViewPdf(file: File) {
if (preview.canPreview(file)) {
void preview.show(file);
}
}
</script>
<template>
<q-btn label="Ver PDF" @click="onViewPdf(selectedFile)" />
</template>Varios archivos (galería o adjuntos):
void preview.show([imageFile, pdfFile, anotherImage]);URL remota con nombre explícito:
void preview.show({
source:
"https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf",
name: "tracemonkey-pldi-09.pdf",
mimeType: "application/pdf",
});API: useDialogFilePreview()
import { useDialogFilePreview } from "@benjaminor-dev/quasar-app-extension-dialog-file-preview";
const preview = useDialogFilePreview();Métodos
| Método | Descripción |
| --- | --- |
| show(input, options?) | Abre el diálogo de inmediato y devuelve una Promise que resuelve cuando terminó la normalización/sondeo MIME de las fuentes. El segundo argumento options configura la sesión (toolbar, título, restricciones). Mientras tanto, y hasta que el visor emita estado listo, el viewport muestra «Cargando archivo...». |
| hide() | Cierra el diálogo y libera URLs blob: creadas por la extensión. |
| next() | Siguiente archivo (si hay varios). |
| previous() | Archivo anterior. |
| downloadCurrent() | Descarga el archivo visible (desde File/Blob o enlace si solo hay URL). |
| canPreview(source) | true si el MIME tiene visor en el diálogo. |
| canPreviewAll(sources) | true si todos los ítems del arreglo son previsualizables. |
Estado reactivo (solo lectura)
| Ref | Descripción |
| --- | --- |
| visible | Si el diálogo está abierto. |
| current | Metadatos del ítem actual (PreviewItem) o null. |
| hasPrevious / hasNext | Navegación en listas multi-archivo. |
| hasMultiple | Hay más de un archivo en la sesión actual. |
Usa canPreview() para mostrar u ocultar botones «Ver» en tu UI sin abrir el diálogo.
Comportamiento al abrir (show)
- El diálogo se hace visible al instante.
- Un único
QInnerLoadingcubre el viewport mientras:- se enriquecen las fuentes (
File/Blob/URL → ítems con MIME y URL de visualización), y - el visor del ítem actual (imagen, PDF, texto, etc.) reporta estado de carga.
- se enriquecen las fuentes (
- Los botones imprimir y descargar en la toolbar aparecen solo cuando el visor emite estado listo (
ready) y la opción de sesión correspondiente está activa (showPrint/showDownload). - Imprimir solo se ofrece para PDF, imágenes y texto; video y audio no muestran el botón aunque
showPrintseatrue. - Al cambiar de archivo en una galería (
next/previous), el loader vuelve hasta que el nuevo visor esté listo.
El estado interno de preparación no se expone en useDialogFilePreview(); basta con visible y la UX del overlay.
Opciones de sesión (PreviewSessionOptions)
Segundo argumento opcional de show(). Aplica a toda la sesión del diálogo (incluida la galería multi-archivo).
import type { PreviewSessionOptions } from "@benjaminor-dev/quasar-app-extension-dialog-file-preview";
void preview.show(confidentialPdf, {
showPrint: false,
showDownload: true,
restrictInteraction: true,
title: "Contrato confidencial",
});
void preview.show([scan1, scan2], {
showGalleryNav: true,
showPdfZoom: false,
});| Opción | Default | Descripción |
| --- | --- | --- |
| showPrint | true | Muestra el botón imprimir cuando el tipo lo permite (PDF, imagen, texto). |
| showDownload | true | Muestra el botón descargar en la toolbar. |
| showGalleryNav | true | Muestra navegación anterior/siguiente si hay varios archivos. |
| showPdfZoom | true | Muestra «Zoom: N%» en la toolbar para PDF. |
| title | nombre del archivo | Título centrado en la toolbar. |
| restrictInteraction | false | Bloquea copiar, cortar, seleccionar, menú contextual y atajos comunes en el visor. No evita capturas de pantalla ni herramientas avanzadas del navegador. |
Si omites options, se usan los valores por defecto de la tabla.
Fuentes de archivo aceptadas
Tipo unificado PreviewSourceInput:
| Forma | Ejemplo |
| --- | --- |
| File | Archivo de <input type="file"> o new File(...) |
| Blob | Blob de API o canvas |
| string | URL https://... o blob:... |
| Objeto descriptor | Ver abajo |
// File o Blob directo
void preview.show(file);
void preview.show([imageFile, pdfFile]);
// URL con metadatos explícitos (recomendado sin extensión clara)
void preview.show({
source: "https://ejemplo.com/documento",
name: "contrato.pdf",
mimeType: "application/pdf",
});
// Descriptor con File
void preview.show({
source: selectedFile,
name: "anexo-renombrado.pdf",
});| Situación | Qué hacer |
| --- | --- |
| File / Blob | Nombre y MIME se infieren del objeto |
| URL sin extensión | Pasa name y mimeType en el descriptor |
| URL remota al abrir | Puede sondearse (HEAD/GET parcial) para inferir MIME |
| canPreview() devuelve false | Evalúa de forma síncrona; con mimeType explícito o tras sondeo, show() puede abrir igual |
| URLs blob: externas | La extensión no las revoca; solo libera las que crea desde File/Blob |
Tipos de archivo soportados
| Categoría | MIME / prefijos | Visor en el diálogo | Imprimible |
| --- | --- | --- | --- |
| Imágenes | image/* | Imagen centrada con object-fit: contain (sin scroll en el viewport) | Sí |
| PDF | application/pdf | pdf.js (vue-pdf-embed): páginas, zoom, capas de texto/anotaciones | Sí |
| Video | video/mp4, video/webm, video/ogg | <video controls> | No |
| Audio | audio/mpeg, audio/wav, audio/ogg, audio/webm | <audio controls> con icono central y ecualizador al reproducir | No |
| Texto | text/plain, text/csv, application/json, application/xml, text/xml | Texto monoespaciado con scroll | Sí |
| Otros | — | Mensaje + botón descargar | No |
Si el MIME no tiene visor, show() igual puede abrir el diálogo en modo fallback para permitir la descarga.
Tipos TypeScript exportados
Desde el entrypoint principal:
import type {
PreviewSourceInput,
PreviewItemInput,
PreviewItem,
PreviewKind,
PreviewSessionOptions,
} from "@benjaminor-dev/quasar-app-extension-dialog-file-preview";| Tipo | Uso |
| --- | --- |
| PreviewSourceInput | Primer argumento de show() y argumento de canPreview() |
| PreviewSessionOptions | Segundo argumento opcional de show() |
| PreviewItemInput | Descriptor con metadatos opcionales |
| PreviewItem | Ítem normalizado del estado reactivo |
| PreviewKind | "image" \| "pdf" \| "video" \| "audio" \| "text" \| "unknown" |
type PreviewItemInput = {
source: File | Blob | string;
name?: string;
mimeType?: string;
};Utilidades MIME
También se exportan para validar en tu UI sin abrir el diálogo:
import {
isPreviewableMime,
PREVIEWABLE_MIME_PREFIXES,
} from "@benjaminor-dev/quasar-app-extension-dialog-file-preview";
if (isPreviewableMime(file.type)) {
// mostrar botón de vista previa
}Integración opcional con Form Builder
Si usas @benjaminor-dev/quasar-app-extension-form-builder, instala también esta extensión:
quasar ext add @benjaminor-dev/dialog-file-previewOrden de boots en el host: Pinia → Form Builder → Dialog File Preview.
InputFile e InputFileMultiple detectan la extensión automáticamente cuando showPreview no es false (default true): botones ver/descargar en el append o en la tabla de gestión, sin llamar useDialogFilePreview() en tu pantalla. Form Builder no depende de este paquete; sin la extensión, esos campos usan el clear nativo de Quasar.
Uso manual desde un callback o botón custom:
<script setup lang="ts">import { useDialogFilePreview } from "@benjaminor-dev/quasar-app-extension-dialog-file-preview";
const preview = useDialogFilePreview();
function onPreviewFile(file: File) {
void preview.show(file);
}
</script>El boot también expone la API vía provide para integraciones de librerías hermanas (misma instancia que useDialogFilePreview()).
Entrypoints públicos
| Import | Contenido |
| --- | --- |
| @benjaminor-dev/quasar-app-extension-dialog-file-preview | useDialogFilePreview, tipos (PreviewSessionOptions, etc.), isPreviewableMime, PREVIEWABLE_MIME_PREFIXES |
| @benjaminor-dev/quasar-app-extension-dialog-file-preview/boot/dialog | Boot Quasar (registrado automáticamente por la extensión) |
| @benjaminor-dev/quasar-app-extension-dialog-file-preview/main.css | Estilos del diálogo (registrado automáticamente) |
Troubleshooting
Boots o estilos no aparecen en quasar.config
Si tras instalar desde npm no ves el boot dialog ni main.css de la extensión en la config de tu app, re-invócala en el host:
npx quasar ext invoke @benjaminor-dev/dialog-file-previewError: Pinia no inicializada / store no disponible
- Verifica que exista un boot de Pinia en el host (
app.use(pinia)o equivalente). - En
quasar.config, el boot de Pinia debe ir antes que el bootdialogde esta extensión.
useDialogFilePreview() fuera de setup
Llámalo dentro de setup, otro composable o <script setup>. Si necesitas usarlo fuera del contexto de componente, asegúrate de que el boot de la extensión ya se ejecutó (app montada).
El botón «Ver» no debería mostrarse
Usa preview.canPreview(file) o isPreviewableMime(file.type) antes de renderizar la acción.
canPreview() devuelve false en una URL sin extensión
canPreview() no hace sondeo remoto. Si la URL no tiene extensión ni heurística conocida, pasa mimeType en el descriptor o llama show() directamente.
PDF o texto desde URL externa no carga
- CORS: el servidor debe permitir
fetchdesde tu origen (PDF remoto se descarga como blob antes de renderizar). - Pasa
mimeTypeexplícito si la URL no tiene extensión. - Verifica que la URL responda 200 (enlaces rotos muestran el error en el visor).
- Para dominios sin CORS, descarga en tu backend y pasa un
File/Bloblocal.
Ejemplo de URL pública que funciona en pruebas:
preview.show({
source:
"https://mozilla.github.io/pdf.js/web/compressed.tracemonkey-pldi-09.pdf",
name: "tracemonkey-pldi-09.pdf",
mimeType: "application/pdf",
});El botón de descarga no aparece al abrir
Es esperado al inicio: la descarga en la toolbar solo se habilita cuando el visor emite estado listo (ready) y showDownload no es false. Mientras prepara fuentes o carga PDF/imagen/texto verás «Cargando archivo...» en el viewport. Si el loader no desaparece, revisa errores de red/CORS en la consola.
No aparece el botón imprimir
- Comprueba que
showPrintno seafalseen las opciones de sesión. - Solo PDF, imágenes y texto son imprimibles; en video/audio el botón no se muestra.
- Igual que la descarga, requiere estado listo (
ready) del visor.
restrictInteraction no bloquea todo
La opción reduce copia, selección y menú contextual en el visor, pero no sustituye DRM ni impide capturas de pantalla. Un usuario con conocimientos técnicos puede seguir accediendo al contenido.
El diálogo abre vacío un instante o tarda en mostrar el archivo
show() abre el modal de inmediato y normaliza las fuentes en segundo plano. Con URLs remotas o varios archivos, el overlay puede permanecer unos segundos; no indica fallo por sí solo.
La vista previa de imagen/PDF funciona en local pero no en producción
Revisa que la extensión esté instalada/invocada en el build de producción y que main.css de la extensión esté en la config generada.
Varios archivos: no aparece navegación
Pasa un arreglo a show([...]). La barra anterior/siguiente solo se muestra cuando hay más de un ítem y showGalleryNav no es false.
Desinstalar
quasar ext remove @benjaminor-dev/dialog-file-previewMIT © Benjamín Olvera R.
