Avanzamento

Usa una barra determinata ogni volta che sai quanto manca — è l’unica che dica davvero qualcosa a chi legge. Ometti value e la barra si anima invece, il che dice “sto ancora lavorando” e niente di più.

Determinata

stack({ gap: 'lg' },
  progress({ value: 25 }),
  progress({ value: 60 }),
  progress({ value: 100 }),
)

Indeterminata

progress()

Una barra senza label è marcata aria-hidden — un ruolo progressbar senza nome accessibile non dice nulla a uno screen reader, quindi una barra senza etichetta è trattata come decorazione. Metti un’etichetta a tutto ciò che chi legge deve seguire.

Etichette

Un’etichetta dice che cosa sta succedendo; showValue aggiunge la percentuale a destra.

Rendering delle pagine72%
Ottimizzazione delle immagini50%
In attesa del deploy
stack({ gap: 'lg' },
  progress({ value: 72, label: 'Rendering delle pagine', showValue: true }),
  progress({ value: 30, max: 60, label: 'Ottimizzazione delle immagini', showValue: true }),
  progress({ label: 'In attesa del deploy' }),
)

Colori e altezza

Superato80%
Degradato45%
In fallimento20%
stack({ gap: 'lg' },
  progress({ value: 80, color: 'success', label: 'Superato', showValue: true }),
  progress({ value: 45, color: 'warning', label: 'Degradato', showValue: true }),
  progress({ value: 20, color: 'danger', label: 'In fallimento', showValue: true }),
  progress({ value: 60, color: 'neutral', height: 'xs' }),
  progress({ value: 60, color: 'primary', height: '1rem' }),
)

Una scala diversa da 100

max ti lascia passare i numeri grezzi — pagine costruite su pagine totali — invece di dover calcolare prima una percentuale.

118 di 169 pagine70%
progress({ value: 118, max: 169, label: '118 di 169 pagine', showValue: true })

Muoverla dal browser

Una barra è HTML renderizzato dal server: la percentuale è una proprietà personalizzata sul riempimento e un numero in aria-valuenow, e niente nella pagina cambia l’una o l’altro da solo. Dai alla barra un id e setProgress li muove insieme — il riempimento, il valore annunciato e la percentuale accanto all’etichetta.

import { setProgress } from 'sitelo/ui/client'

const request = new XMLHttpRequest()

request.upload.addEventListener('progress', (event) => {
  setProgress('upload', event.loaded, { max: event.total })
})

Il massimo viene ricordato, quindi le chiamate successive sono solo un valore. Oppure raggiungi il modulo come lo raggiungono i componenti, e salta del tutto il bundle:

button({ onclick: "import('/su/progress.js').then(m=>m.set('upload',100))" }, 'Finito')

Passare null — o qualunque cosa non sia un numero finito — restituisce la barra all’animazione indeterminata, così un lavoro che smette di riportare numeri non ha bisogno di un caso speciale. getProgress() rilegge il valore corrente, sulla scala della barra stessa.

Provalo

Questa pagina carica il runtime, quindi i pulsanti qui sotto muovono davvero la barra.

Caricamento0%
stack({ gap: 'md' },
  progress({ id: 'demo-progress', value: 0, label: 'Caricamento', showValue: true }),
  stack({ direction: 'row', gap: 'sm', wrap: true },
    button({ size: 'sm', variant: 'outline', onclick: "import('/su/progress.js').then(m=>m.set('demo-progress',0))" }, 'Azzera'),
    button({ size: 'sm', variant: 'outline', onclick: "import('/su/progress.js').then(m=>m.set('demo-progress',35))" }, '35%'),
    button({ size: 'sm', variant: 'outline', onclick: "import('/su/progress.js').then(m=>m.set('demo-progress',80))" }, '80%'),
    button({ size: 'sm', variant: 'outline', onclick: "import('/su/progress.js').then(m=>m.set('demo-progress',100))" }, 'Fatto'),
    button({ size: 'sm', variant: 'ghost', onclick: "import('/su/progress.js').then(m=>m.set('demo-progress',null))" }, 'Ignoto'),
  ),
)

Anche una barra senza etichetta viene mossa, ma resta aria-hidden — è stata renderizzata senza nome di proposito, e annunciarne ora un valore metterebbe nell’albero di accessibilità una progressbar senza nome.

Rotella

Non esiste un componente rotella — la rotella è un’icona, e spin è ciò che la fa girare. Come ogni icona è dimensionata in em, quindi si accorda al testo accanto a cui sta senza che le si dica una dimensione.

stack({ direction: 'row', gap: 'lg', align: 'center' },
  icon('spinner', { spin: true, size: 'sm' }),
  icon('spinner', { spin: true }),
  icon('spinner', { spin: true, size: 'lg' }),
)

La rotella in contesto

Dai a una rotella isolata una label così viene annunciata. Una dentro un pulsante non ne ha bisogno — il pulsante dice già cosa sta facendo.

Recupero dell’ultima build…

stack({ gap: 'md' },
  stack({ direction: 'row', gap: 'sm', align: 'center' },
    icon('spinner', { spin: true, label: 'Caricamento' }),
    text({ variant: 'small', tone: 'muted' }, 'Recupero dell’ultima build…'),
  ),
  stack({ direction: 'row', gap: 'sm' },
    button({ loading: true }, 'Pubblicazione'),
    button({ variant: 'outline', loading: true }, 'Controllo dei link'),
  ),
)

Props

progress() — esportata anche come progressBar:

PropTipoPredefinitoDescrizione
valuenumber—A che punto è. Omettilo per l’animazione indeterminata.
maxnumber100Quale valore conta come completo.
color'primary' | 'neutral' | 'success' | 'warning' | 'danger''primary'Colore del riempimento.
labelChild—Testo sopra la barra; anche il suo nome accessibile.
showValuebooleanfalseMostra la percentuale accanto all’etichetta.
heightSpace'0.5rem'Spessore della barra.

setProgress() da sitelo/ui/client:

PropTipoPredefinitoDescrizione
targetElement | string—La barra, o l’id di una. Se nessun elemento ha quell’id si prova come selettore.
valuenumber | null—Dove spostarla. null la riporta all’animazione indeterminata.
options.maxnumber100Cosa conta come completo. Ricordato per le chiamate successive.

La rotella non ha props proprie — è icon('spinner', { spin: true }), e accetta quello che accetta icon().