PdfZoneEditor
Import
import { PdfZoneEditor } from '@pzeta/vue-components'
I tipi (PdfZone, PdfZoneDropEvent, PdfZoneOwner, PdfZoneField…) si importano dalla radice del pacchetto dalla 1.24.1: nella 1.24.0 non erano esportati.
Prerequisiti: pdf.js
Il PDF è reso con pdf.js. pdfjs-dist è una peer
dependency opzionale della libreria, come chart.js e qrcode: si installa solo dove si usa
l'editor, e viene importato al primo documento aperto, quindi chi non usa il componente non lo
scarica.
npm install pdfjs-dist # versione 5 o successiva
pdf.js lavora in un worker, che va indicato in uno dei due modi:
// Una volta per tutta l'applicazione
import { GlobalWorkerOptions } from 'pdfjs-dist'
import workerSrc from 'pdfjs-dist/build/pdf.worker.min.mjs?url'
GlobalWorkerOptions.workerSrc = workerSrc
<!-- Oppure per singolo editor -->
<PdfZoneEditor :src="pdf" :worker-src="workerSrc" />
Senza worker l'editor non apre il documento e mostra al suo posto «Manca il worker di pdf.js:
imposta workerSrc», emettendo error.
Esempio Base
Per l'anteprima servono pdfjs-dist e il worker, che questo sito non carica: l'esempio è solo
codice. Una demo funzionante è nella showcase della libreria (/data/pdf-zone-editor).
<script setup lang="ts">
import { ref } from 'vue'
import workerSrc from 'pdfjs-dist/build/pdf.worker.min.mjs?url'
import { PdfZoneEditor, type PdfZone, type PdfZoneDropEvent } from '@pzeta/vue-components'
const props = defineProps<{ pdf: Blob }>()
const pagina = ref(1)
const attiva = ref<string | null>(null)
const zone = ref<PdfZone[]>([
{ key: 'firma-1', page: 1, x: 9, y: 80, width: 32, height: 7, type: 'firma', label: 'Firma', color: 0, required: true },
])
// La zona la crea il chiamante: chiave, proprietario e dimensioni sono suoi.
function aggiungi({ field, page, x, y }: PdfZoneDropEvent) {
const key = crypto.randomUUID()
zone.value = [
...zone.value,
{ key, page, x: Math.min(x, 75), y: Math.min(y, 93), width: 25, height: 7, type: field.type, label: field.label, color: 0 },
]
attiva.value = key
}
</script>
<template>
<PdfZoneEditor
v-model:zones="zone"
v-model:page="pagina"
:src="props.pdf"
:worker-src="workerSrc"
:owners="[{ id: 1, name: 'Mario Rossi', colorIndex: 0 }, { id: 2, name: 'Anna Verdi', colorIndex: 1 }]"
:field-palette="[
{ type: 'firma', label: 'Firma' },
{ type: 'iniziali', label: 'Iniziali' },
{ type: 'data', label: 'Data' },
]"
:active-zone="attiva"
@select="attiva = $event"
@drop="aggiungi"
/>
</template>
Coordinate
x, y, width e height di una zona sono percentuali della pagina (0–100), con
l'origine in alto a sinistra. Restano valide a qualunque scala: la pagina viene ridisegnata
quando cambia la larghezza del contenitore, e le zone la seguono. Le zone sono sempre tenute
dentro la pagina e non scendono sotto minZoneSize.
Props
| Prop | Tipo | Default | Descrizione |
|---|---|---|---|
src | Blob | ArrayBuffer | Uint8Array | string | null | — | Il PDF: Blob/File, dati binari o URL |
zones | PdfZone[] | [] | Zone di tutte le pagine (v-model:zones) |
owners | PdfZoneOwner[] | [] | Proprietari delle zone, per la legenda dei colori |
fieldPalette | PdfZoneField[] | [] | Campi trascinabili sulla pagina; senza voci la palette non compare |
activeZone | string | null | null | Zona evidenziata; se sta su un'altra pagina l'editor ci si porta |
page | number | 1 | Pagina mostrata, da 1 (v-model:page) |
readonly | boolean | false | Sola lettura: le zone si selezionano ma non si spostano, niente palette |
workerSrc | string | — | URL del worker di pdf.js; in alternativa GlobalWorkerOptions.workerSrc |
minZoneSize | number | 2 | Dimensione minima di una zona, in percentuale |
ariaLabel | string | «Documento PDF» | Nome accessibile dell'editor |
PdfZone
interface PdfZone {
key: string // identificativo univoco
page: number // pagina, da 1
x: number // bordo sinistro, % della larghezza
y: number // bordo superiore, % dell'altezza
width: number // % della larghezza
height: number // % dell'altezza
type: string // tipo di campo (firma, iniziali, testo, data…), libero
label: string // etichetta mostrata e letta dagli screen reader
color?: number // indice del colore (di solito quello del proprietario)
required?: boolean // asterisco e lettura «obbligatorio»
filled?: boolean // spunta e lettura «compilato»
}
PdfZoneOwner e PdfZoneField
interface PdfZoneOwner {
id: number | string
name: string
colorIndex: number // indice del colore delle sue zone
}
interface PdfZoneField {
type: string
label: string
}
color e colorIndex scelgono una tinta fra le palette semantiche della libreria (primary,
success, warn, danger, info, help, secondary, contrast, poi da capo): bordo
-500 e sfondo trasparente della stessa tinta, più pieno per le zone compilate.
Emits
| Evento | Payload | Descrizione |
|---|---|---|
update:zones | PdfZone[] | Zone dopo uno spostamento, un ridimensionamento o un'eliminazione |
update:page | number | Pagina dopo la navigazione |
select | string | Chiave della zona cliccata o attivata da tastiera |
drop | PdfZoneDropEvent | Un campo della palette rilasciato sulla pagina (o premuto) |
load | { pages: number } | Documento aperto, col numero di pagine |
error | Error | Errore di apertura o di resa del PDF |
interface PdfZoneDropEvent {
field: PdfZoneField
page: number
x: number // punto di rilascio, % della pagina
y: number
}
Un campo trascinato dalla palette dà il punto di rilascio; premuto (col mouse o da tastiera)
va al centro della pagina corrente (x: 50, y: 50). L'editor non crea la zona: la crea il
chiamante, che ne decide chiave, proprietario e dimensioni.
Slot
| Slot | Scope | Descrizione |
|---|---|---|
toolbar | { page: number; pages: number; goToPage: (page: number) => void } | Sostituisce navigazione e legenda |
palette | { fields: PdfZoneField[]; addField: (field: PdfZoneField) => void } | Sostituisce la palette dei campi |
zone | { zone: PdfZone; active: boolean } | Contenuto di ogni zona (default: etichetta, spunta, asterisco) |
Interazione
- Puntatore: la zona si sposta trascinandola e si ridimensiona dalla maniglia in basso a destra; un clic senza movimento la seleziona soltanto. Sul tocco la pagina non scorre mentre si trascina una zona.
- Tastiera (con la zona a fuoco): frecce spostano di 1% (10% con Ctrl), Maiusc+frecce ridimensionano, Canc/Backspace elimina, Invio/Spazio seleziona.
- Palette: campi trascinabili sulla pagina (drag & drop HTML5) o premibili.
Accessibilità
- Ogni zona è un pulsante raggiungibile col Tab, con nome «Firma, pagina 1, obbligatorio» (o
«compilato») e
aria-pressedsulla zona attiva; le istruzioni da tastiera sono collegate conaria-describedby. - Spostamenti, ridimensionamenti ed eliminazioni sono annunciati in una live region («Firma: posizione 20%, 30%»).
- I pulsanti della palette si chiamano «Aggiungi Firma»; l'indicatore «Pagina 1 di 2» è una
live region. Il canvas della pagina è decorativo (
aria-hidden). - Stringhe nella sezione i18n
pdfZoneEditordei cinque dizionari.
TypeScript
import type {
PdfZoneEditorProps,
PdfZoneEditorEmits,
PdfZone,
PdfZoneOwner,
PdfZoneField,
PdfZoneDropEvent,
} from '@pzeta/vue-components'
PdfViewerEmbedded
Visualizzatore PDF inline basato su iframe nativo, pensato per split-view, con toolbar di download/apri in nuova scheda e slot per empty state.
PickList
Componente a doppia lista per il trasferimento di elementi da source a target (e viceversa), con pulsanti di spostamento, riordinamento interno e selezione multipla.