Data

GanttTimeline

Gantt generico con righe (opzionalmente raggruppate) e barre posizionate su un asse temporale o lineare continuo, con tacche, colori per stato e indicatore "adesso" configurabili.

Import

import { GanttTimeline } from '@pzeta/vue-components'

Esempio Base

Props

PropTipoDefaultDescrizione
rowsGanttRow[](required)Elenco delle righe da renderizzare; ogni riga ha un'etichetta e una o piu' barre
domainStartGanttValue(required)Inizio del dominio dell'asse (ISO datetime/date per scala time, numero per scala linear)
domainEndGanttValue(required)Fine del dominio dell'asse
scale'time' | 'linear''time'Interpretazione dei valori: time (datetime convertito in epoch) o linear (numerico)
ticksnumber | GanttTick[]12Numero di tacche equidistanti oppure elenco esplicito di tacche con posizione/label
tickFormat(value: number) => stringundefinedFormattatore dell'etichetta della tacca; riceve il valore numerico del dominio
coloriPerStatoRecord<string, string>{}Mappa stato => colore CSS, usata quando la barra non ha color esplicito
raggruppaPerstringundefinedNome del campo della riga su cui raggruppare; renderizza intestazioni di gruppo
showNowIndicatorbooleanfalseMostra la linea verticale rossa dell'ora/data corrente (solo scala time)
rowLabelWidthstring'11rem'Larghezza (CSS) della colonna etichette riga
rowHeightstring'2rem'Altezza (CSS) di ogni riga/barra
minChartWidthstring'640px'Larghezza minima dell'area grafico; oltre questa soglia scatta lo scroll orizzontale
localestring'it-IT'Locale Intl.DateTimeFormat per la formattazione di default delle tacche temporali
emptyTextstring'Nessun dato da mostrare'Testo mostrato quando rows e' vuoto

GanttRow

Proprieta'TipoDescrizione
idstring | numberIdentificatore univoco della riga
labelstringEtichetta mostrata nella colonna di sinistra
barsGanttBar[]Barre posizionate sulla track della riga
gruppostringChiave di raggruppamento (usata se raggruppaPer e' valorizzato)
[key]unknownQualsiasi altro campo: disponibile nello slot #rowLabel e per il raggruppamento

GanttBar

Proprieta'TipoDescrizione
idstring | numberIdentificatore opzionale (fallback all'indice come key)
startGanttValueInizio della barra
endGanttValue | nullFine della barra; null = ancora in corso (estesa fino ad "adesso" su scala time, alla fine del dominio su scala linear)
statostringChiave di stato usata per risolvere il colore via coloriPerStato
colorstringColore CSS esplicito; ha precedenza su coloriPerStato
labelstringEtichetta opzionale mostrata dentro la barra
[key]unknownQualsiasi altro campo: disponibile nello slot #bar

GanttTick

Proprieta'TipoDescrizione
positionnumberPosizione frazionaria sull'asse, da 0 a 1
labelstringEtichetta della tacca

Slots

SlotPropsDescrizione
cornerHeader-Contenuto dell'angolo in alto a sinistra (sopra la colonna etichette)
groupHeader{ label: string }Override dell'intestazione di un gruppo (quando raggruppaPer e' attivo)
rowLabel{ row: GanttRow }Rendering custom dell'etichetta riga nella colonna fissa di sinistra
bar{ bar: GanttBar, row: GanttRow }Contenuto custom all'interno di una barra
empty-Override del messaggio mostrato quando rows e' vuoto

Emits

EventoPayloadDescrizione
select{ row: GanttRow, bar: GanttBar, originalEvent: MouseEvent }Emesso al click su una barra

Esempi

Asse mensile con righe raggruppate

Asse a giorni del mese, righe raggruppate per reparto tramite raggruppaPer, una barra per valutazione colorata per stato. Il click su una barra emette select.

Scala lineare con slot custom

Con scale="linear" i valori sono interpretati come numeri puri (es. percentuali, quantita'). Gli slot #rowLabel e #bar permettono di personalizzare etichette e contenuto delle barre.

<script setup>
const rows = [
  { id: 1, label: 'Fase analisi', bars: [{ start: 0, end: 40, color: '#3b82f6', label: '40%' }] },
  { id: 2, label: 'Fase sviluppo', bars: [{ start: 20, end: 90, color: '#22c55e', label: '70%' }] },
  { id: 3, label: 'Fase rilascio', bars: [{ start: 80, end: 100, color: '#f59e0b', label: '20%' }] },
]
const tickPercento = (value) => `${Math.round(value)}%`
</script>
<template>
  <GanttTimeline
    :rows="rows"
    scale="linear"
    :domain-start="0"
    :domain-end="100"
    :ticks="5"
    :tick-format="tickPercento"
  >
    <template #rowLabel="{ row }">
      <span class="font-semibold text-gray-700">{{ row.label }}</span>
    </template>
    <template #bar="{ bar }">
      <span class="truncate">{{ bar.label }}</span>
    </template>
  </GanttTimeline>
</template>

Note Comportamento

  • I valori (start, end, domainStart, domainEnd) sono convertiti in numero: su scala time tramite new Date(value).getTime(), su scala linear tramite Number(value); i valori non parsabili diventano 0
  • Una barra con end: null e' considerata "in corso": su scala time viene estesa fino al minimo tra "adesso" e fine dominio, su scala linear fino alla fine del dominio
  • Le barre vengono ritagliate (clamp) ai limiti del dominio; una barra completamente fuori dominio o di larghezza nulla non viene renderizzata
  • Il colore di una barra si risolve nell'ordine: color esplicito, poi coloriPerStato[stato], infine il grigio di default #9ca3af
  • Con raggruppaPer le righe sono ordinate per ordine di prima apparizione del gruppo e rese contigue sotto l'intestazione del rispettivo gruppo
  • L'indicatore "adesso" (showNowIndicator) e' attivo solo su scala time e si aggiorna ogni 60 secondi; viene mostrato solo se il momento corrente cade dentro il dominio
  • Il contenitore ha overflow-x: auto e l'area grafico una min-width pari a minChartWidth: su viewport stretti compare lo scroll orizzontale

Accessibilita'

  • Ogni barra e' un <button type="button"> nativo, quindi raggiungibile da tastiera con anello di focus (focus:ring-2)
  • Le etichette delle barre lunghe vengono troncate; per casi complessi valutare uno slot #bar con contenuto descrittivo

TypeScript

import type {
  GanttTimelineProps,
  GanttRow,
  GanttBar,
  GanttBarSelectPayload,
} from '@pzeta/vue-components'

interface SessioneRow extends GanttRow {
  idDipendente: number
}

interface SessioneBar extends GanttBar {
  stato: 'completa' | 'attiva' | 'errore'
}

const onSelect = (payload: GanttBarSelectPayload) => {
  const bar = payload.bar as SessioneBar
  console.log('riga', payload.row.label, 'stato', bar.stato)
}

Quando usarlo

  • Timeline orarie intraday (es. presenze, sessioni di lavoro) con indicatore dell'ora corrente
  • Timeline a giorni/mesi (es. valutazioni, attivita' pianificate) con righe raggruppate per reparto/categoria
  • Avanzamento su scala lineare/numerica (es. percentuali, range continui) con scale="linear"

Per casi diversi:

  • Griglia risorsa x giorno della settimana (planner/turni) → WeekScheduler
  • Cronologia di eventi singola, non gridata per riga → Timeline
  • Vista mese con eventi del giorno → EventCalendar