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

@vevedh/nfz-qgrid

v0.1.0-rc.5

Published

Nuxt 4 layer providing a reusable Quasar QTable-based data grid for Vue 3, Quasar 2, UnoCSS and NFZ applications.

Readme

NFZ QGrid Layer

@vevedh/nfz-qgrid est un layer Nuxt 4 qui expose un composant de grille autonome basé sur Quasar QTable.

Objectif : fournir une grille métier réutilisable, typée, responsive et compatible avec les applications Nuxt 4 + Quasar 2 + UnoCSS + NFZ/Feathers, sans coupler le composant à un backend particulier.

Fonctionnalités

  • recherche globale ;
  • filtres par colonne ;
  • filtres avancés avec règles and/or ;
  • vues enregistrées en localStorage ;
  • redimensionnement et réordonnancement des colonnes ;
  • sélection simple, multiple ou désactivée ;
  • édition inline via QPopupEdit avec QDate, QTime, QSelect, QInput, QToggle ;
  • inférence stricte des dates pour éviter qu’un texte avec chiffres ouvre QDate ;
  • rendu badge automatique en lecture pour les colonnes list / select ;
  • éditeur JSON pratique avec validation, formatage, compactage et reset ;
  • slots de cellules, actions de ligne et état vide ;
  • exports JSON, CSV, Excel HTML et PDF navigateur ;
  • import JSON/XLSX par événement ;
  • garde-fous d’import : taille fichier, nombre de lignes, taille cellule, clés dangereuses filtrées ;
  • responsive avec scroll horizontal confiné.
  • compatibilité TypeScript strict renforcée pour la normalisation des dates françaises DD/MM/YYYY.

État de la version 0.1.0-rc.5

Cette version ne modifie pas le runtime du composant. Elle enrichit la documentation développeur avec un guide complet de la toolbar, ajoute des captures d’écran dans la documentation VitePress et poursuit la mémoire de patch introduite en 0.1.0-rc.4. Le package conserve les validations de release candidate : fixture Nuxt 4 consommatrice, imports publics, tests unitaires, documentation FR/EN et inférence stricte des éditeurs.

Documentation toolbar et captures d’écran

La documentation développeur contient maintenant un guide dédié aux boutons de la toolbar :

  • recherche globale ;
  • mode dense ;
  • affichage/masquage des filtres colonnes ;
  • configuration des colonnes ;
  • libellés personnalisés ;
  • filtres CRUD ;
  • vues ;
  • import JSON/XLSX ;
  • export JSON/CSV/Excel/PDF ;
  • plein écran.

Pages ajoutées :

docs/guide/toolbar-actions.md
docs/en/guide/toolbar-actions.md

Les captures d’écran sont stockées dans :

docs/public/screenshots/

Elles sont utilisables directement dans VitePress avec des chemins publics comme :

![Toolbar](/screenshots/qgrid-toolbar-overview.png)

Mémoire de patch mainteneur

Depuis 0.1.0-rc.4, chaque archive contient :

patch-memory/README.md
patch-memory/000-index.md
patch-memory/TEMPLATE.md
patch-memory/entries/

Avant chaque nouveau patch, consulter patch-memory/000-index.md, puis la dernière entrée dans patch-memory/entries/, avant de modifier le code. Après chaque patch, ajouter une nouvelle entrée avec l'analyse, la résolution, les fichiers touchés, les impacts API/UI/DX et les commandes de validation.

Cette mémoire complète PROMPT_CONTEXT.md, PATCHLOG.md, JOURNAL.md et CHANGELOG.md sans remplacer la documentation publique.

Helpers publics testables

Depuis 0.1.0-rc.1, les helpers critiques sont extraits du composant Vue pour être testés et réutilisés.

import {
  inferNfzQGridColumnTypeFromValues,
  resolveNfzQGridColumnEditor,
  coerceNfzQGridEditedValue
} from '@vevedh/nfz-qgrid/utils/editors'

import {
  sanitizeNfzQGridImportedRows,
  escapeNfzQGridHtml,
  safeNfzQGridExportFileName
} from '@vevedh/nfz-qgrid/utils/sanitize'

Ces helpers couvrent l’inférence stricte des éditeurs, la normalisation date/heure, la coercition des valeurs éditées, la limitation des textes importés, l’échappement HTML des exports et la protection contre les clés dangereuses.

Tests et fixture consommatrice

La validation locale complète inclut maintenant une application Nuxt 4 propre qui consomme le layer depuis tests/fixtures/nuxt-consumer.

bun run test
bun run consumer:prepare
bun run consumer:typecheck
bun run check

bun run check exécute le typecheck du playground, les tests unitaires, le typecheck de la fixture consommatrice et le build VitePress.

Publication RC

Pour publier cette version comme release candidate (0.1.0-rc.5) :

bun run publish:rc

La publication stable devra ensuite utiliser 0.1.0 et le tag npm latest, uniquement après validation terrain dans une application consommatrice réelle.

Installation

bun add @vevedh/nfz-qgrid nuxt-quasar-ui quasar @unocss/nuxt unocss

L’import XLSX utilise la dépendance optionnelle xlsx :

bun add xlsx

Configuration Nuxt 4

// nuxt.config.ts
export default defineNuxtConfig({
  extends: ['@vevedh/nfz-qgrid'],

  modules: [
    'nuxt-quasar-ui',
    '@unocss/nuxt'
  ],

  compatibilityDate: '2026-05-17',

  quasar: {
    lang: 'fr',
    iconSet: 'material-icons',
    plugins: ['Dialog', 'Notify', 'Loading'],
    extras: {
      fontIcons: ['material-icons']
    },
    components: {
      autoImport: true
    }
  },

  typescript: {
    strict: true
  }
})

Utilisation minimale

<script setup lang="ts">
import { ref } from 'vue'
import type { NfzQGridColumn } from '@vevedh/nfz-qgrid/types'

type UserRow = {
  id: string
  name: string
  email: string
  group: 'DSI' | 'DRH' | 'OTHER'
}

const rows = ref<UserRow[]>([
  { id: 'u-001', name: 'UTILISATEUR DEMO 01', email: '[email protected]', group: 'DSI' }
])

const columns: NfzQGridColumn<UserRow>[] = [
  { name: 'name', label: 'Nom', field: 'name', sortable: true, align: 'left', type: 'string', editor: 'text' },
  { name: 'email', label: 'Email', field: 'email', sortable: true, align: 'left', type: 'string', editor: 'text' },
  { name: 'group', label: 'Groupe', field: 'group', sortable: true, align: 'left' }
]

const selectedKeys = ref<string[]>([])
</script>

<template>
  <NfzQGridStandalone
    v-model:selected-keys="selectedKeys"
    :rows="rows"
    :columns="columns"
    row-key="id"
    storage-key="users-grid"
  />
</template>

Note DX sur l’inférence automatique

Pour les colonnes métier, privilégiez une déclaration explicite :

{ name: 'agent', field: 'agent', type: 'string', editor: 'text' }
{ name: 'createdAt', field: 'createdAt', type: 'date', editor: 'date' }

Depuis 0.1.0-beta.7, l’inférence date/heure est stricte. Une valeur texte comme AGENT DEMO 01 reste une chaîne et ouvre un QInput, pas QDate.

Exemple Nuxt 4 + NFZ/Feathers

Le composant ne dépend pas directement du client NFZ. Dans une application Nuxt 4 + NFZ, chargez les données dans un store ou un composable, puis transmettez-les à la grille.

<script setup lang="ts">
import { onMounted, ref } from 'vue'
import type { NfzQGridColumn } from '@vevedh/nfz-qgrid/types'

type UserRow = {
  _id: string
  email: string
  groups?: string[]
}

const api = useAdminFeathers()
const rows = ref<UserRow[]>([])
const loading = ref(false)

const columns: NfzQGridColumn<UserRow>[] = [
  { name: 'email', label: 'Email', field: 'email', sortable: true },
  {
    name: 'groups',
    label: 'Groupes',
    field: row => Array.isArray(row.groups) ? row.groups.join(', ') : '',
    sortable: true
  }
]

async function refreshUsers(): Promise<void> {
  loading.value = true

  try {
    const result = await api.service('users').find({ query: { $limit: 50 } })
    rows.value = Array.isArray(result) ? result : result.data
  }
  finally {
    loading.value = false
  }
}

onMounted(refreshUsers)
</script>

<template>
  <NfzQGridStandalone
    :rows="rows"
    :columns="columns"
    :loading="loading"
    row-key="_id"
    storage-key="admin-users-grid"
    :import-enabled="false"
  />
</template>

La sécurité métier doit rester côté backend Feathers/NFZ : hooks, policies, RBAC, validation Zod, limitation des queries et audit.

Édition inline date / heure / liste

Pour les colonnes de type date, heure ou liste contrôlée, déclarez explicitement type, editor et, pour les listes, options. Le composant utilise alors QDate, QTime ou QSelect dans la popup d'édition.

<script setup lang="ts">
import { ref } from 'vue'
import type { NfzQGridCellEditPayload, NfzQGridColumn } from '@vevedh/nfz-qgrid/types'

type Row = {
  id: string
  label: string
  createdAt: string
  reminderTime: string
  status: 'draft' | 'pending' | 'done' | 'rejected'
}

const rows = ref<Row[]>([
  { id: 'row-001', label: 'Ligne 01', createdAt: '2026-01-10', reminderTime: '08:30', status: 'pending' }
])

const statusOptions = [
  { label: 'Brouillon', value: 'draft', color: 'grey', icon: 'edit_note' },
  { label: 'En attente', value: 'pending', color: 'warning', icon: 'hourglass_empty' },
  { label: 'Terminé', value: 'done', color: 'positive', icon: 'check_circle' },
  { label: 'Rejeté', value: 'rejected', color: 'negative', icon: 'cancel' }
]

const columns: NfzQGridColumn<Row>[] = [
  { name: 'label', label: 'Libellé', field: 'label', sortable: true, editor: 'text' },
  { name: 'createdAt', label: 'Date', field: 'createdAt', sortable: true, type: 'date', editor: 'date' },
  { name: 'reminderTime', label: 'Heure', field: 'reminderTime', sortable: true, type: 'time', editor: 'time' },
  { name: 'status', label: 'Statut', field: 'status', sortable: true, type: 'list', editor: 'select', options: statusOptions }
]

function onCellEdit(payload: NfzQGridCellEditPayload<Row>): void {
  const field = payload.column.name as keyof Row
  rows.value = rows.value.map(row => row.id === payload.row.id ? { ...row, [field]: payload.value } as Row : row)
}
</script>

<template>
  <NfzQGridStandalone
    :rows="rows"
    :columns="columns"
    row-key="id"
    editable
    @cell-edit-start="payload => console.info('start', payload)"
    @cell-edit-change="payload => console.info('change', payload)"
    @cell-edit-save="payload => console.info('save', payload)"
    @cell-edit-cancel="payload => console.info('cancel', payload)"
    @cell-edit="onCellEdit"
  />
</template>

Valeurs normalisées : YYYY-MM-DD pour les dates, HH:mm pour les heures, YYYY-MM-DDTHH:mm pour les dates/heures, et la valeur contrôlée de l'option pour QSelect.

Tous les events d'édition exposent le même payload typé NfzQGridCellEditPayload, y compris cell-edit-start. Le champ previousValue est donc toujours disponible pour l'audit, les confirmations ou un rollback applicatif.

Imports sécurisés

<NfzQGridStandalone
  :rows="rows"
  :columns="columns"
  :import-enabled="true"
  :max-import-file-bytes="2 * 1024 * 1024"
  :max-import-rows="5000"
  :max-cell-length="10000"
  @import-data="payload => openImportPreview(payload)"
/>

Bon pattern de production : importer → prévisualiser → valider côté backend → confirmer → écrire en base.

Structure du projet

app/components/nfz-qgrid/NfzQGridStandalone.vue
app/assets/css/nfz-qgrid.css
app/types/nfz-qgrid.ts
.playground/app/pages/index.vue
docs/
examples/basic.vue
tests/package.test.ts
PROMPT_CONTEXT.md
PATCHLOG.md
JOURNAL.md

Développement

bun install
bun run dev
bun run typecheck
bun run test
bun run docs:dev
bun run docs:build
bun run check

Publication

npm pack --dry-run
npm publish --access public --tag beta

Documentation

La documentation VitePress est bilingue :

  • / : guide français ;
  • /en/ : guide anglais.

À chaque patch, maintenir au minimum :

  • README.md ;
  • PROMPT_CONTEXT.md ;
  • PATCHLOG.md ;
  • JOURNAL.md ;
  • docs/ FR/EN.

Licence

MIT

Édition liste avec badge

Pour une colonne de statut, le grid utilise QSelect en édition et conserve un badge en lecture :

import type { NfzQGridColumn, NfzQGridSelectOption } from '@vevedh/nfz-qgrid/types'

type Row = {
  id: string
  status: 'draft' | 'pending' | 'done' | 'rejected'
}

const statusOptions: NfzQGridSelectOption[] = [
  { label: 'Brouillon', value: 'draft', color: 'grey', icon: 'edit_note' },
  { label: 'En attente', value: 'pending', color: 'warning', icon: 'hourglass_empty' },
  { label: 'Terminé', value: 'done', color: 'positive', icon: 'check_circle' },
  { label: 'Rejeté', value: 'rejected', color: 'negative', icon: 'cancel' }
]

const columns: NfzQGridColumn<Row>[] = [
  {
    name: 'status',
    label: 'Statut',
    field: 'status',
    type: 'list',
    editor: 'select',
    options: statusOptions
  }
]

Pour désactiver uniquement le badge de lecture :

{ name: 'status', field: 'status', type: 'list', editor: 'select', badge: false }

Éditeur JSON

Une colonne type: 'json' utilise maintenant NfzQGridJsonCellEditor : validation immédiate, compteur de taille, actions de formatage/compactage et reset.

const columns: NfzQGridColumn<Row>[] = [
  {
    name: 'metadata',
    label: 'JSON',
    field: 'metadata',
    type: 'json',
    editor: 'json',
    sortable: false
  }
]

Le composant est aussi réutilisable hors grid :

<script setup lang="ts">
import { ref } from 'vue'
import NfzQGridJsonCellEditor from '@vevedh/nfz-qgrid/components/NfzQGridJsonCellEditor.vue'

const jsonText = ref('{
  "source": "demo"
}')
</script>

<template>
  <NfzQGridJsonCellEditor v-model="jsonText" :max-length="10000" />
</template>