npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@wemake4u/form-controls

v2.0.2

Published

Angular UI components (data entry, layout and presentation)

Readme

@wemake4u/form-controls

Componenti UI Angular (data entry, layout e presentazione) basati sul Sirio web kit.

Installazione

npm install @wemake4u/form-controls

Requisiti: Angular 22 (@angular/core, @angular/common, @angular/forms) e il Sirio web kit 9.1.0.

Sirio web kit (prerequisito, non incluso)

La libreria non include e non distribuisce il Sirio web kit: genera il markup e le classi del kit, ma CSS, font e immagini li deve fornire l'applicazione. Versione richiesta: sirio-kit-web 9.1.0 (ultima versione rilasciata).

Sola lettura: aspetto dei campi

Nel kit un campo in sola lettura ha lo stesso aspetto di un campo disabilitato (sfondo e bordo grigi). Con readonlyAppearance i campi in sola lettura possono avere invece lo sfondo bianco e il bordo chiaro del kit, così si distinguono dai disabilitati (che restano grigi):

| Valore | Aspetto in sola lettura | | --- | --- | | filled (default) | come il kit: sfondo e bordo grigi, uguale al disabilitato (anche la select, che il kit non copre) | | outline | sfondo bianco, bordo chiaro del kit (#e3e5e8), testo nel colore normale del campo |

// Default per tutta l'applicazione (app.config.ts) o per un componente (providers)
providers: [provideSirioReadonlyAppearance('outline')]
<!-- Override per il singolo controllo -->
<wm-sirio-select label="Provincia" readonly readonlyAppearance="outline" ... />

Vale per textbox, textarea, number, date, time, datetime, select (anche searchable), autocomplete, searchbar e slider (campo numerico); prefissi e suffissi dei campi seguono lo stesso aspetto. Con outline restano invariati i bordi degli stati di validazione (errore, valido, avviso) e del focus da tastiera. Checkbox, radio, checklist e file upload non hanno un campo con sfondo: l'impostazione non li modifica.

Select con ricerca: pulsante di azzeramento

Di default la select con searchable non mostra il pulsante "X" del kit: il valore si cambia scegliendo un'altra opzione, o si azzera da codice. Con clearable il pulsante viene mostrato e svuota ricerca e selezione.

<wm-sirio-select label="Comune" searchable clearable [options]="comuni" formControlName="comune" />

Select: opzioni in caricamento

Con loading la select mostra che le opzioni stanno arrivando (es. da un servizio): il campo chiuso mostra loadingText ("Caricamento in corso") al posto del placeholder con lo spinner del kit, la lista contiene solo la riga "Caricamento in corso" con lo spinner, il combobox ha aria-busy e il messaggio è annunciato agli screen reader.

<wm-sirio-select label="Provincia" [loading]="province() === null" [options]="province() ?? []" formControlName="prov" />

Data grid

wm-sirio-data-grid genera la tabella del kit (table.sirio-table); filtri, ordinamento e paginazione dei dati sono elaborati da TanStack Table (@tanstack/angular-table 9, dipendenza installata con la libreria; l'istanza è esposta come table per usi avanzati). La griglia ha ordinamento anche su più colonne, paginazione (wm-sirio-pagination), selezione delle righe, colonne ridimensionabili e riordinabili, righe espandibili (dettaglio e dati ad albero), raggruppamento con totali e i layout del kit. Le colonne si dichiarano con wm-sirio-grid-column; celle e intestazioni personalizzate con i template wmSirioGridCell e wmSirioGridHeader, la riga vuota con wmSirioGridEmpty, il dettaglio delle righe con wmSirioGridDetail. Import unico: imports: [SIRIO_DATA_GRID].

<wm-sirio-data-grid caption="Pratiche" [data]="pratiche" rowId="id" selection="multiple" [(selected)]="scelte">
  <wm-sirio-grid-column field="numero" header="Numero" sortable filter="text" />
  <wm-sirio-grid-column field="importo" header="Importo" align="end" sortable [format]="euro" filter="number" />
  <wm-sirio-grid-column field="stato" header="Stato">
    <ng-template wmSirioGridCell let-row><span [wmSirioTag]="tipo(row)">{{ row.stato }}</span></ng-template>
  </wm-sirio-grid-column>
</wm-sirio-data-grid>
  • Dati in memoria ([data]): la griglia ordina (confronto numerico, per data o testo con le regole della lingua di LOCALE_ID) e pagina.

  • Formati predefiniti: format accetta una funzione (valore, riga) => testo oppure il nome di un formato, con le opzioni in formatOptions (tutti seguono LOCALE_ID; empty = testo dei valori vuoti): number (decimals, minDecimals/maxDecimals, grouping), currency (currency, default EUR), percentage (decimals, scale: 'percent' se il valore è già 15), date (precision: day, month, year), dateTime e time (seconds), boolean (trueLabel, falseLabel, default Sì/No), map (values: oggetto o SirioOption[]). Numeri e date si leggono anche da testo ('89.9', '2026-01-31', '2026-01-31T14:30'). I formati numerici sono allineati a destra (se align non è indicato), numeri e date usano il font del kit e si ordinano per valore; con map il filtro select elenca tutti i codici con la loro etichetta. provideSirioGridFormats({ defaults, formatDefaults, formats }) imposta le opzioni di default e registra formati propri per nome (o sostituisce quelli predefiniti).

  • Formati di LOCALE_ID: senza format, i numeri delle celle usano il separatore decimale della lingua (senza separatore delle migliaia, così codici e anni restano invariati) e i valori Date il formato data; conteggi, numeri e date nei chip dei filtri e nei risultati seguono LOCALE_ID (es. it-IT: "1234,5", "31/01/2026", "10.000").

  • Font dei numeri del kit: le celle numeriche e di data (colonne con filtro number/date, o con valori numero o Date), i numeri di riga, i conteggi e i totali dei gruppi usano la classe del kit font-mono (Roboto Mono, come campi numerici, date e paginazione del kit); mono sulla colonna lo forza (true) o lo toglie (false).

  • Intestazioni su una riga: il titolo è tagliato con i puntini (testo completo nel tooltip), l'indice di ordinamento e l'imbuto del filtro restano visibili. filterButton="hover" mostra l'imbuto solo al passaggio del mouse, con il focus o quando il filtro è attivo (come ag-grid); filterButton="none" toglie i controlli di filtro dalla griglia (anche il pannello del layout a card): i filtri si impostano solo da codice ([(filters)], setFilter); default always.

  • Riga di filtri (filterRow): una riga sotto le intestazioni, come il floating filter di ag-grid. Ogni colonna con filtro ha un campo per la condizione semplice (testo: contiene, numero e data: uguale, select e booleano: un valore; con filterOperators l'operatore scelto) e l'imbuto con il popover degli operatori; una condizione che non sta nel campo (es. "compreso tra", "vuoto", più valori) è mostrata come riepilogo. Esc svuota il filtro della colonna. Il campo si chiama "Filtra per " (filterLabel), l'imbuto "Altre condizioni per " (filterMoreLabel). Con la riga di filtri i chip dei filtri non sono mostrati; nel layout a card resta il pannello "Filtri".

  • Testo su una riga: i testi lunghi vanno a capo (default); [wrap]="false" sulla colonna tiene il testo su una riga (la colonna si allarga, o il testo è tagliato con i puntini se la colonna ha una larghezza). Lo stesso field può comparire in più colonne (es. una data mostrata come data e come ora), ognuna con il proprio filtro: in filters la prima colonna usa il field, le altre field~2, field~3 (o il loro filterKey).

  • Colonna larga quanto il contenuto: fit sulla colonna (es. una colonna di azioni): non prende parte dello spazio libero della tabella, le celle restano su una riga e il titolo non viene tagliato.

  • Dati da un FormArray ([formArray]): un FormGroup per riga; la griglia mostra getRawValue() di ogni gruppo e segue le modifiche del FormArray (valori, righe aggiunte o tolte da codice). Le righe si modificano in una finestra di dialogo del kit con il form di <ng-template wmSirioGridEditor let-group>, aperta da "Modifica" nella colonna azioni (fissa a destra) o con il doppio clic (editOnDoubleClick). Il form lavora su una copia della riga creata da rowFactory(valore) (senza rowFactory: copia generica con gli stessi controlli, valori e validatori): "Annulla" scarta tutto, "Salva" segna i campi come toccati, attende i validatori asincroni e applica i valori solo se la copia è valida (la riga diventa dirty). saveHandler / deleteHandler (funzioni che restituiscono un valore, una Promise o un Observable) controllano la modifica prima di applicarla, ad esempio con una chiamata al server: false o un errore la bloccano e il messaggio d'errore compare nella finestra. addable aggiunge "Aggiungi" (nuova riga da rowFactory(null), o addRow(valoriIniziali), aggiunta in fondo), deletable "Elimina" sulle righe (con conferma, confirmDelete) ed "Elimina selezionati" con la selezione multipla; rowEditable / rowDeletable scelgono le righe. Con il FormArray disabilitato le righe si aprono in sola lettura ("Visualizza"). Dopo markAllAsTouched() le righe non valide hanno il marcatore e l'icona di errore, gli errori del FormArray compaiono sotto la griglia (errorMessages; per "almeno una riga" usare Validators.required, perché Validators.minLength ignora un array vuoto). Eventi (rowSave) e (rowDelete); metodi editRow, addRow, deleteRow, deleteSelected, groupOf(riga). Senza rowId la selezione segue il FormGroup della riga. Non si usa insieme a [dataSource].

    <wm-sirio-data-grid [formArray]="righe" [rowFactory]="creaRiga" addable deletable (rowSave)="salvata($event)">
      <wm-sirio-grid-column field="descrizione" header="Descrizione" />
      <wm-sirio-grid-column field="importo" header="Importo" format="currency" />
      <ng-template wmSirioGridEditor let-group let-isNew="isNew">
        <div [formGroup]="group">
          <wm-sirio-textbox formControlName="descrizione" label="Descrizione" />
          <wm-sirio-number formControlName="importo" label="Importo" />
        </div>
      </ng-template>
    </wm-sirio-data-grid>
  • Dati lato server ([dataSource]): una funzione che riceve { page, pageSize, offset, sort, filters } e restituisce { rows, total } (Observable, Promise o valore). Viene richiamata a ogni cambio di pagina o ordinamento e con reload(); le risposte superate vengono ignorate (un Observable precedente viene annullato).

    • Totale facoltativo: senza total il totale si ricava dalle pagine, come lo scroll infinito di ag-grid. Una pagina piena significa che ce ne sono altre: "Successiva" resta attiva, la paginazione mostra "di 30+ elementi" ("oltre 30" per gli screen reader, testo moreLabel) e il conteggio dei risultati "oltre 30". Una pagina più corta è l'ultima e fissa il totale. Cambiando ordinamento, filtri o righe per pagina si riparte da capo. wm-sirio-pagination ha lo stesso comportamento con hasMore.
    • Errori: oltre a (loadError), la tabella mostra loadErrorText ("Non è stato possibile caricare i dati.") con il pulsante "Riprova" (retryLabel), che richiama il data source; la paginazione resta per tornare indietro. Con <ng-template wmSirioGridError let-error let-retry="retry"> il contenuto è personalizzato.
  • Filtri per colonna (filter sulla colonna): pulsante nell'intestazione che apre il popover del kit con i controlli della libreria. Tipi: text (contiene, senza distinzione di maiuscole e accenti), number e date (intervallo Da / A), select (con filterOptions, anche filterMultiple; senza filterOptions la scelta è tra i valori presenti nella colonna, con il numero di righe, calcolati con gli altri filtri applicati), boolean (Sì / No). I filtri attivi compaiono sopra la tabella come chip del kit, rimovibili uno a uno o tutti insieme; nel layout a card sono in un pannello "Filtri". Stato two-way [(filters)] (per chiave di colonna, es. { comune: 'Roma', importo: { from: 1000 } }), filtro personalizzato con filterPredicate, ritardo dei filtri di testo con filterDebounce (300 ms). Con [dataSource] i filtri attivi arrivano nella query (query.filters).

  • Operatori dei filtri: con filterOperators sulla griglia (o sulla colonna: true, false o l'elenco degli operatori ammessi) il popover ha una scelta "Condizione" sopra il valore e il filtro diventa { operator, value, to? } (SirioGridFilterCondition). Testo: contiene, non contiene, uguale a, diverso da, inizia con, finisce con; numero: uguale a, diverso da, maggiore di, maggiore o uguale a, minore di, minore o uguale a, compreso tra; data: uguale a, diverso da, prima del, dopo il, compreso tra; elenco: è uno di, non è uno di; per tutti vuoto e non vuoto. Chip come "Importo: compreso tra 10.000 e 30.000"; etichette in filterOperatorLabels, filterConditionLabel, filterValueLabel, filterAndLabel. Un valore semplice (es. uno stato salvato) viene letto come condizione equivalente (testo → contiene, intervallo → compreso tra, elenco → è uno di).

  • Ordinamento: [(sort)] è l'elenco dei criteri in ordine di priorità ([{ field: 'comune', direction: 'asc' }]). Con sortMode="multiple" Maiusc+clic (o Maiusc+Invio) aggiunge una colonna ai criteri, mostrando la priorità nell'intestazione; il clic semplice sostituisce l'ordinamento.

  • Colonne ridimensionabili (resizable): maniglia sul bordo destro delle intestazioni per il mouse (trascinamento, doppio clic per ripristinare); da tastiera Alt+frecce sull'intestazione, come ag-grid; minWidth / maxWidth e resizable="false" sulla colonna; larghezze two-way [(columnWidths)] (px per chiave di colonna).

  • Colonne riordinabili (reorderable): trascinamento delle intestazioni o Shift+frecce su un'intestazione, con annuncio della nuova posizione; ordine two-way [(columnOrder)]. "Ripristina colonne" nella barra sopra la tabella.

  • Righe espandibili: con <ng-template wmSirioGridDetail let-row> ogni riga ha un pulsante che apre un pannello di dettaglio sotto di essa (rowExpandable per sceglierle); con subRows (proprietà o funzione) i dati sono un albero, con rientro per livello. Stato two-way [(expanded)], expandAll(), "Espandi tutto" / "Comprimi tutto".

  • Numero di riga (rowNumbers): colonna "N." all'inizio, numero continuo tra le pagine nell'ordine e con i filtri correnti (lato server dall'offset della query); le righe di gruppo non sono numerate, i figli degli alberi sì per livello (1, 1.1, 1.2). È l'intestazione di riga (role="rowheader"); testi rowNumberHeader e rowNumberLabel.

  • Intestazioni raggruppate: group sulla colonna con i nomi dei gruppi dal livello più alto, separati da / (group="Richiedente/Residenza"); una riga d'intestazione per livello (scope="colgroup"), le colonne vicine con gli stessi gruppi condividono la cella, un gruppo si divide se le sue colonne non sono vicine o stanno in blocchi fissi diversi. Con stickyHeader le righe restano impilate.

  • Colonne flex: flex sulla colonna (flex="2", flex senza valore = 1) come in CSS / ag-grid: le colonne con flex si dividono in proporzione la larghezza lasciata libera dalle altre (che tengono width o la larghezza del contenuto), entro minWidth / maxWidth; si ricalcola quando cambia la larghezza della griglia. Una colonna flex ridimensionata a mano tiene la sua larghezza fino al doppio clic sulla maniglia o a "Ripristina colonne".

  • Colonne fisse: fixed="left" o fixed="right" sulla colonna la tiene ferma durante lo scroll orizzontale; più colonne per lato, mostrate su quel lato della tabella (le colonne si spostano solo all'interno del proprio blocco). Selezione, espansione e numero di riga restano fissi con le colonne di sinistra. fixedFirstColumn / fixedLastColumn sono scorciatoie per la prima e l'ultima colonna. Attive nel contenitore responsive, non nel layout a card; al bordo del blocco fisso compare l'indicatore di scroll del kit.

  • Classi e stili: rowClass / rowStyle sulla griglia, cellClass / cellStyle sulla colonna (valori fissi o funzioni: della riga e del suo indice per le righe, della riga e del valore per le celle), headerClass / headerStyle sull'intestazione della colonna. Le classi sono come ngClass (testo, elenco o { classe: condizione }), gli stili come ngStyle (testo CSS o oggetto, anche 'width.px': 80). Uno sfondo di rowStyle / cellStyle vale anche sulle righe a strisce e sulle colonne fisse; la selezione copre lo sfondo della riga, non quello della cella. Per gli sfondi è meglio usare gli stili: le classi del kit sirio-bg-* sulle righe non battono le strisce del kit.

    <wm-sirio-data-grid [data]="pratiche" [rowClass]="classeRiga" [rowStyle]="stileRiga">
      <wm-sirio-grid-column field="importo" header="Importo" format="currency" headerClass="sirio-text-primary"
                            [cellClass]="importoNegativo" />
    </wm-sirio-data-grid>
    classeRiga = (riga: Pratica) => ({ 'sirio-text-disabled': riga.stato === 'Chiusa' });
    stileRiga = (riga: Pratica) => (riga.scaduta ? { 'background-color': '#f7e4ea' } : null);
    importoNegativo = (_riga: Pratica, valore: unknown) => ({ 'sirio-text-danger': Number(valore) < 0 });
  • Raggruppamento (solo con [data]): colonne groupable e "Raggruppa per" sopra la tabella ([(grouping)]); le righe di gruppo mostrano valore e numero di righe, le altre colonne il valore di aggregate (sum, mean, min, max, count). Con gruppi o alberi la paginazione conta le righe di primo livello.

  • Paginazione: pageable, [(page)], [(pageSize)], pageSizeOptions; aspetto di wm-sirio-pagination con paginationVariant (full, numbers con i numeri di pagina e i puntini, oppure compact, una barra sottile con "Pagina 1 di 6" e i pulsanti prima / precedente / successiva / ultima), paginationSiblings e paginationShowItems.

  • Stato two-way: [(sort)], [(page)], [(pageSize)], [(filters)], [(grouping)], [(expanded)], [(columnWidths)], [(columnOrder)], [(selected)] (righe confrontate per rowId).

  • Salvataggio dello stato: getState() restituisce lo stato scelto dall'utente come JSON (SirioGridState: ordinamento, filtri, pagina e righe per pagina, ordine e larghezze delle colonne, raggruppamento, righe espanse, rowId delle righe selezionate), setState(stato) lo ripristina (anche in parte; i filtri si applicano subito senza tornare alla prima pagina, criteri di colonne non più presenti vengono ignorati), resetState() torna allo stato iniziale; (stateChange) e il signal state seguono ogni modifica, per il salvataggio automatico.

  • Selezione: selection="single" (radio) o "multiple" (checkbox del kit con "seleziona tutte"; selectAll sceglie le righe prese: page quelle della pagina, default, filtered tutte quelle che passano i filtri su tutte le pagine, come selectAll: 'filtered' di ag-grid, all tutte; con [dataSource] sempre la pagina caricata); selectOnRowClick seleziona anche al clic sulla riga; con [selectionColumn]="false" niente colonna di checkbox: si seleziona con il clic sulla riga (in multiple ogni clic aggiunge o toglie la riga) o con Spazio/Invio sulla riga con il focus; rowSelectable (funzione della riga, come isRowSelectable di ag-grid) indica le righe selezionabili: le altre hanno la casella disabilitata (nascosta con hideUnselectable), sono escluse da "seleziona tutte" e dalla selezione con clic o tastiera; (rowClick) per il dettaglio. Con la selezione singola l'intestazione della colonna di selezione è letta dagli screen reader come selectionHeaderLabel ("Selezione"). I campi dei filtri non si registrano nel form che contiene la griglia.

  • Altezza: senza impostazioni la griglia è alta quanto il contenuto (tutte le righe); height (come style.height) la fissa, minHeight e maxHeight la limitano. Con un'altezza impostata le righe scorrono dentro la griglia con l'intestazione sempre visibile, mentre "Raggruppa per", chip dei filtri e paginazione restano fuori dallo scroll e contano nell'altezza. Con stickyHeader e senza altezza vale l'altezza del kit (450px per le righe).

  • Aspetto del kit: variant (default, dark, striped), size (xs, sm, md, lg), contenitore responsive con gli indicatori di scroll, stickyHeader, fixedFirstColumn, fixedLastColumn, cardOnMobile (righe a card fino a 768px, con l'intestazione come etichetta di ogni cella).

  • Accessibilità: caption (nascosta di default; dà anche il nome al paginatore, "Paginazione della tabella ", per distinguere più griglie nella stessa pagina), aria-sort e tastiera (Invio/Spazio) sulle intestazioni ordinabili, aria-selected, aria-expanded sui pulsanti di espansione, aria-level per alberi e gruppi, aria-busy durante il caricamento; numero di risultati, criteri di ordinamento e spostamenti di colonna annunciati.

  • Navigazione da tastiera (keyboardNavigation, default true, come ag-grid e il pattern ARIA "grid"): la griglia è una sola tappa del Tab (si entra sulla prima intestazione, poi sull'ultima cella visitata); le frecce si spostano tra intestazioni e celle, Home/Fine all'inizio e alla fine della riga, Ctrl+Home/Ctrl+Fine alla prima e all'ultima cella; Tab esce dalla griglia. Una cella con pulsanti o campi (es. le azioni, un template di cella) passa il focus ai suoi controlli, uno per tappa. La cella raggiunta viene portata in vista fuori dalle colonne fisse e dall'intestazione fissa; Spazio/Invio su una cella seleziona la riga quando non c'è la colonna di selezione. I campi della riga dei filtri restano tappe del Tab (usano le frecce); con la riga dei filtri la griglia ha due tappe, intestazioni e righe: Tab va dall'intestazione ai filtri e dai filtri alle righe, poi esce (Shift+Tab al contrario). Sull'intestazione, come ag-grid: Invio/Spazio ordina (Shift+Invio ordinamento multiplo), Alt+frecce cambia la larghezza, Shift+frecce sposta la colonna, Ctrl+Invio apre il filtro; Alt+frecce non arriva mai al browser (Alt+← è "Indietro" e farebbe lasciare la pagina). La tabella ha role="grid" (treegrid con alberi e raggruppamenti, con aria-level sulle righe) e aria-multiselectable con la selezione multipla. Con [keyboardNavigation]="false" è una tabella semplice: tappe del Tab solo le intestazioni interattive e i controlli.

  • Pannelli dei filtri: il calendario dei filtri data e l'elenco dei filtri a scelta si aprono sopra la pagina, sotto il campo (sopra quando sotto non c'è spazio), senza essere tagliati dal contenitore scorrevole della griglia. Vale per wm-sirio-date e wm-sirio-select dentro qualsiasi contenitore che ritaglia il contenuto.

Browser supportati

Gli stessi di Angular 22: Chrome ed Edge 119+, Firefox 119+, Safari 17+ (macOS e iOS), ultime versioni di Chrome e Firefox per Android. Internet Explorer ed Edge Legacy non sono supportati.