WikiEditor
Panoramica
WikiEditor scrive e mostra una pagina wiki in HTML (v-model). Oltre alla formattazione di base offre blocchi di codice evidenziati, tabelle, immagini, callout e sezioni richiudibili; il modulo lo estende con i propri comandi / e i propri blocchi, e ne riceve link e commenti come eventi.
Lo stesso componente serve in modifica e in lettura (readonly): in lettura la barra degli strumenti sparisce, i link si aprono con un clic e, se il modulo lo prevede, si può comunque commentare.
Anteprima
Titoli, elenco, callout, blocco di codice, tabella e sezione richiudibile. Scrivi / all'inizio di una riga per il menu dei comandi; con il cursore in una tabella, in un blocco di codice o in un callout compare la seconda riga della barra.
Quick start
<script setup lang="ts">
import { ref } from 'vue'
import { WikiEditor, type ComandoPowerbox, type CommentoAncorato } from '@pzeta/vue-wiki'
const contenuto = ref('')
const commenti = ref<CommentoAncorato[]>([])
const comandi: ComandoPowerbox[] = [
{
id: 'indice',
etichetta: 'Indice',
descrizione: 'Articoli annidati del padre',
icona: 'pi pi-list-check',
inserisci: ({ inserisciBlocco }) => inserisciBlocco('indice', { idarticolo: 12 }),
},
]
const caricaAllegato = async (file: File) => {
const allegato = await allegatiService.carica(file) // servizio del modulo
return { src: allegato.url, id: String(allegato.idallegato) }
}
const cercaArticoli = async (ricerca: string) =>
(await articoliService.cerca(ricerca)).map((a) => ({
etichetta: a.titolo,
href: `/knowledge/articolo/${a.idarticolo}`,
}))
</script>
<template>
<WikiEditor
v-model="contenuto"
:readonly="!puoModificare"
:comandi="comandi"
:carica-immagine="caricaAllegato"
:risolvi-link="cercaArticoli"
:commenti="commenti"
:max-length="100000"
@commenta="apriFormCommento"
@apri-link="(href) => router.push(href)"
@apri-commento="evidenziaCommento"
>
<template #blocco-personalizzato="{ tipo, dati }">
<IndiceFigli v-if="tipo === 'indice'" :id-articolo="Number(dati.idarticolo)" />
</template>
</WikiEditor>
</template>
Props
| Prop | Tipo | Default | Descrizione |
|---|---|---|---|
modelValue | string | '' | Contenuto HTML (v-model). Un documento vuoto è '', non <p></p> |
readonly | boolean | false | Area non modificabile e senza barra degli strumenti. Si può cambiare a runtime: non emette update:modelValue |
comandi | ComandoPowerbox[] | [] | Comandi / del modulo, dopo quelli dell'editor. Vedi Comandi e blocchi |
caricaImmagine | (file: File) => Promise<ImmagineCaricata> | — | Carica un'immagine scelta, incollata o trascinata e ne restituisce { src, id }. Senza, il comando Immagine non c'è e le immagini incollate sono scartate |
risolviLink | (ricerca: string) => Promise<LinkRisolto[]> | — | Risultati del dialogo del link mentre si scrive (per esempio gli articoli). Senza, il dialogo accetta solo indirizzi |
commenti | CommentoAncorato[] | — | Commenti del contenuto. Con un array, anche vuoto, la barra sulla selezione offre «Commenta» e compare il margine dei commenti. Vedi Commenti ancorati |
maxLength | number | 0 | Oltre questo numero di caratteri compare un avviso e l'area diventa aria-invalid; la scrittura non viene bloccata. 0 = nessun limite |
placeholder | string | «Scrivi, oppure digita / per i comandi» | Testo mostrato a documento vuoto, in modifica |
ariaLabel | string | «Contenuto» | Nome accessibile dell'area di testo |
emitDebounce | number | 200 | Ritardo (ms) di update:modelValue dall'ultima modifica |
Eventi
| Evento | Payload | Quando |
|---|---|---|
update:modelValue | string | Il contenuto cambia. Mai in sola lettura, mai per un valore arrivato da modelValue |
commenta | SelezioneCommento ({ testo, ancora, html }) | «Commenta» sulla selezione: l'ancora è già applicata al testo |
apriLink | string (href) | Clic su un link in sola lettura, o Ctrl/⌘+clic in modifica |
apriCommento | string (id) | Clic su un indicatore nel margine dei commenti |
Nel template gli eventi si scrivono in kebab-case: @apri-link, @apri-commento.
apriLink con l'indirizzo, già controllato (solo http, https, mailto, tel e relativi). Un link a un articolo si apre col router, uno esterno con window.open.Slot
| Slot | Scope | Descrizione |
|---|---|---|
toolbar-extra | { editor } | Pulsanti aggiuntivi in fondo alla barra degli strumenti |
blocco-personalizzato | BloccoPersonalizzatoProps ({ tipo, dati, readonly, aggiorna, elimina }) | Disegna i blocchi inseriti dai comandi del modulo |
<WikiEditor v-model="contenuto">
<template #toolbar-extra="{ editor }">
<button type="button" class="px-2 text-sm" @click="editor.commands.clearContent(true)">
Svuota
</button>
</template>
</WikiEditor>
Metodi esposti
Con un template ref (ref="wiki"):
| Metodo | Descrizione |
|---|---|
editor | Istanza Tiptap (undefined prima del montaggio) |
focus() | Porta il focus nell'editor |
inserisciBlocco(tipo, dati?) | Inserisce un blocco personalizzato al cursore |
rimuoviAncora(ancora) | Toglie un'ancora di commento dal contenuto (commento annullato o eliminato) |
mostraAncora(ancora) | Porta in vista il testo di un'ancora; false se non è nel contenuto |
Barra degli strumenti
Compare solo in modifica. Da sinistra:
| Gruppo | Comandi |
|---|---|
| Tipo di blocco | Testo, Titolo 1–6 |
| Testo | Grassetto, corsivo, sottolineato, barrato, codice in linea, link |
| Blocchi | Elenco puntato, elenco numerato, citazione, blocco di codice, callout (inserisce o toglie), sezione richiudibile, tabella, immagine (con caricaImmagine), separatore |
| Cronologia | Annulla, ripeti |
| Slot | toolbar-extra |
Sotto compare una seconda riga con i comandi del blocco in cui si trova il cursore:
| Blocco | Comandi |
|---|---|
| Tabella | Riga sopra, riga sotto, colonna a sinistra, colonna a destra, riga di intestazione, elimina riga, elimina colonna, elimina tabella |
| Blocco di codice | Linguaggio: rilevamento automatico, Bash, C#, CSS, Go, HTML / XML, Java, JavaScript, JSON, Markdown, PHP, Testo, Python, Rust, SQL, TypeScript, YAML |
| Callout | Tipo (Informazione, Successo, Avviso, Pericolo), rimuovi il callout |
Barra sulla selezione
Selezionando del testo compare sopra la selezione una barra con grassetto, corsivo, sottolineato, barrato, codice in linea, link e — se c'è la prop commenti — Commenta. In sola lettura resta solo «Commenta».
Non compare per una selezione vuota o di soli spazi, per un blocco selezionato per intero (immagine, blocco del modulo) e dentro un blocco di codice, dove né formattazione né ancore sono ammesse.
Menu dei comandi /
Digitando / all'inizio di una riga o dopo uno spazio si apre il menu, filtrato mentre si scrive (maiuscole e accenti non contano; ogni parola digitata deve comparire nell'etichetta, nella descrizione o nelle parole chiave). Frecce per scegliere, Invio per eseguire, Esc per chiudere. Non si apre in sola lettura né dentro un blocco di codice.
| Comando | Parole chiave | Effetto |
|---|---|---|
| Testo | — | Paragrafo |
| Titolo 1–6 | h1…h6, heading | Intestazione del livello |
| Elenco puntato / numerato | ul, ol, lista | Elenco |
| Citazione | quote, appunti | Citazione |
| Blocco di codice | code, snippet | Codice evidenziato |
| Tabella | table, griglia | Tabella 3 × 3 con intestazione |
| Immagine | image, foto | Scelta del file (solo con caricaImmagine) |
| Link | url, collegamento | Dialogo del link |
| Callout | nota, avviso, admonition | Callout informativo attorno al blocco corrente |
| Sezione richiudibile | toggle, accordion, dettagli | Sezione attorno al blocco corrente |
| Separatore | hr, linea | Linea orizzontale |
Seguono i comandi del modulo (comandi): vedi Comandi e blocchi.
Link
- Dialogo: dalla barra, dal menu
/o da un comando del modulo (chiediLink()). Con del testo selezionato chiede solo l'indirizzo, senza selezione anche il testo; su un link esistente permette di modificarlo o rimuoverlo. - Ricerca: con
risolviLink, ciò che si scrive nel campo e non sembra un indirizzo (non inizia con uno schema,/,#,.owww.) diventa una ricerca, 250 ms dopo l'ultima battuta; scegliere un risultato inserisce il link. Una ricerca più recente scarta le risposte di quelle prima, e i risultati con indirizzi non sicuri non compaiono. - Indirizzi:
www.esempio.itdiventahttps://www.esempio.it; uno schema non ammesso mostra «Indirizzo non valido». - Scrittura: un indirizzo digitato o incollato diventa link da solo.
- Clic: in modifica un clic posiziona il cursore e Ctrl/⌘+clic emette
apriLink; in sola lettura il clic emetteapriLink.
Immagini
Con caricaImmagine un'immagine si aggiunge dal pulsante o dal comando Immagine, incollandola o trascinandola nel testo (va dove la si rilascia). caricaImmagine riceve il File e restituisce { src, id }: l'immagine viene inserita come blocco con alt uguale al nome del file e data-id uguale all'id. Più file si caricano uno dopo l'altro.
Durante il caricamento sotto l'editor compare «Caricamento dell'immagine…»; se la promessa fallisce, o src non è un indirizzo ammesso (http, https, blob o relativo), compare un avviso (role="alert") che sparisce alla modifica successiva.
Prova: immagini e link
Qui caricaImmagine non carica nulla: restituisce un indirizzo blob: del file, che resta nel browser. risolviLink cerca in un elenco fisso di tre articoli.
Callout e sezioni richiudibili
- Callout: avvolge i blocchi selezionati in un riquadro colorato, di tipo
info(predefinito),success,warnodanger. Il tipo si cambia dalla seconda riga della barra. - Sezione richiudibile: avvolge i blocchi in una sezione con titolo. Nell'editor il titolo è un campo (Invio porta all'inizio del contenuto) e la freccia apre o chiude la sezione: in modifica lo stato viene salvato, in sola lettura no. Nel contenuto salvato è un
<details>nativo, che si apre anche mostrato senza editor.
Scorciatoie da tastiera
Sono quelle di Tiptap (Mod = Ctrl, ⌘ su macOS):
| Tasti | Azione |
|---|---|
Mod+B / Mod+I / Mod+U | Grassetto / corsivo / sottolineato |
Mod+Shift+S | Barrato |
Mod+E | Codice in linea |
Mod+Alt+1 … Mod+Alt+6 | Titolo 1–6 |
Mod+Shift+8 / Mod+Shift+7 | Elenco puntato / numerato |
Mod+Shift+B | Citazione |
Mod+Alt+C | Blocco di codice |
Shift+Invio | A capo nello stesso paragrafo |
Mod+Z / Mod+Shift+Z (o Mod+Y) | Annulla / ripeti |
/ | Menu dei comandi |
Accessibilità
- L'area di testo è
role="textbox"conaria-multiline,aria-label(propariaLabel),aria-readonlyin sola lettura earia-invalidoltremaxLength. - Le barre sono
role="toolbar"con un nome; i pulsanti hannoaria-labele, se si attivano,aria-pressed. - Il menu
/è unlistboxconaria-activedescendantsulla voce selezionata. - La freccia della sezione richiudibile ha
aria-expanded; il titolo è un campo con etichetta. - Il dialogo del link porta il focus sul primo campo; gli indicatori nel margine hanno un nome («Apri il commento»).
- Gli avvisi di caricamento sono
role="status"erole="alert"; il contatore oltre il limite è annunciato (aria-live).
Note
- v-model ed eco: il valore emesso e rimandato dal parent come
modelValueè riconosciuto e non reimposta il contenuto (il cursore non salta). Un valore diverso arrivato da fuori sostituisce il contenuto e annulla una modifica non ancora emessa. - Larghezza: la barra va a capo e le tabelle larghe scorrono dentro il proprio contenitore: l'editor non allarga la pagina.
- Tema scuro: tutte le classi hanno la variante
dark:; il blocco di codice è scuro in entrambi i temi.