Aller au contenu

Édition

Deux fonctions : edit() de @dzgrid/dom/edit tient les modifications (enregistrement, annulation, refus ; textes -edit, CSS edit.css), editors() dessine les champs (textes -editors). Prenez editors dans @dzgrid/dom/editors (un calendrier de dates venu de @tzslot/*, chargé à la première utilisation) ou dans @dzgrid/dom/editors-base (sans calendrier : un jour se tape).

F2, Entrée, un double-clic ou la frappe d’un caractère ouvre le champ ; Entrée garde la valeur (et descend avec enterMoves: 'down'), Tab va à la cellule modifiable suivante, Échap rend l’ancienne valeur.

Tapez par-dessus un nom, ou essayez une taille de 300 (refusée : 120 au plus). Annuler et Rétablir au-dessus, ou Ctrl+Z et Ctrl+Y.

import { createGrid } from '@dzgrid/dom';
import type { ColumnOrGroup } from '@dzgrid/core';
import type { SaveRequest } from '@dzgrid/core/edit';
import { edit } from '@dzgrid/dom/edit';
import { editors } from '@dzgrid/dom/editors-base';
import { listEditor } from '@dzgrid/dom/editors-list';
import { FR } from '@dzgrid/dom/locales/fr';
import { FR_EDIT } from '@dzgrid/dom/locales/fr-edit';
import { FR_EDITORS } from '@dzgrid/dom/locales/fr-editors';
import '@dzgrid/theme/dzgrid.css';
import '@dzgrid/theme/edit.css';
type Gnome = { id: string; name: string; hat: string; height: number; painted: string; locked: boolean };
const columns: ColumnOrGroup<Gnome>[] = [
{ id: 'name', header: 'Nom', editable: (g) => !g.locked, edit: { required: true, maxLength: 40 } },
{ id: 'hat', header: 'Bonnet', type: 'enum', editable: true,
edit: { options: [{ value: 'red', label: 'Rouge' }, { value: 'green', label: 'Vert' }] } },
{ id: 'height', header: 'Taille (cm)', type: 'number', editable: true, edit: { min: 5, max: 120 },
validate: (v) => (typeof v === 'number' && v % 1 !== 0 ? 'whole' : null) },
{ id: 'painted', header: 'Peint le', type: 'day', editable: true },
];
async function save(req: SaveRequest<Gnome>, signal: AbortSignal): Promise<void> {
const body = req.changes.map((c) => ({ id: c.row.id, column: c.column, value: c.value }));
const answer = await fetch('/api/gnomes', { method: 'PATCH', body: JSON.stringify(body), signal });
if (!answer.ok) throw Object.assign(new Error('refusé'), { reason: await answer.text() });
}
// Pas typé `GridMessages` : les clés `validation.<code>` propres à une application sont des clés en plus.
const messages = {
...FR,
text: { ...FR.text, ...FR_EDIT.text, ...FR_EDITORS.text, 'validation.whole': 'un nombre entier' },
};
const grid = createGrid<Gnome>(document.getElementById('gnomes')!, {
rows: [{ id: 'g1', name: 'Barnabé', hat: 'red', height: 31, painted: '2025-04-02', locked: false }],
columns,
getRowId: (g) => g.id,
messages,
features: [edit<Gnome>({ save: (req, signal) => save(req, signal as AbortSignal) }), editors({ enterMoves: 'down', list: listEditor })],
onEvent: (e) => {
if (e.type === 'cellEdited') console.log(e.column, e.previous, '→', e.value, e.origin);
if (e.type === 'saveFailed') console.warn('non enregistré', e.column, e.reason);
},
});
  • editable : true, ou une fonction (row, context, info) ; une cellule qui ne peut pas être modifiée répond « lecture seule ».
  • edit : required, min, max, pattern, maxLength, options (une liste, ou une fonction de la ligne), et editor (le nom de votre propre partie d’éditeur).
  • Une liste (une colonne enum, ou edit.options) se choisit dans l’éditeur de liste : donnez-le aux éditeurs avec editors({ list: listEditor }), listEditor venant de @dzgrid/dom/editors-list (son entrée à lui, pour qu’une grille sans listes ne le charge pas). Sans lui, une colonne de liste ouvre un champ texte lu par son codec, avec un avertissement missing-feature par colonne.
  • validate(value, row, context) rend un code, ou null quand la valeur convient. Donnez la phrase du code dans messages sous validation.<code> ; sans elle, un « invalide » générique est dit.
  • Une valeur refusée reste dans le champ ouvert, clignote, et est dite aux lecteurs d’écran avec le nom de la colonne et la raison.

Chaque clé a sa page sous Options de colonne : editable, edit, validate.

save(request, signal) reçoit une demande par geste (request.changes : key, column, value, previous, row ; request.origin dit s’il s’agissait d’une modification, d’un collage, d’une recopie, d’une annulation…). Résoudre : enregistré. Rejeter ou lever une erreur : refusé — les cellules reviennent en arrière, et une erreur avec une reason en chaîne affiche cette raison. Sans save, les modifications ne sont gardées que dans la grille (vous entendez cellEdited et pouvez l’appliquer à vos lignes). Pendant un enregistrement, la cellule montre une bordure animée et dit « enregistrement » aux lecteurs d’écran. Les enregistrements d’une même cellule passent dans l’ordre : un geste plus récent attend le plus ancien. La grille n’écrit jamais dans vos lignes : une valeur gardée est affichée par-dessus la ligne jusqu’à ce que vos lignes (un nouveau tableau, une transaction, une réponse du serveur) la portent. Transmettez signal à fetch.

Ctrl+Z annule, Ctrl+Y ou Ctrl+Maj+Z rétablit (Cmd sur Mac), sur les 100 derniers gestes, chacun repassant par save. Depuis le code :

import { createGrid } from '@dzgrid/dom';
import { edit } from '@dzgrid/dom/edit';
import { editors } from '@dzgrid/dom/editors-base';
import { FR } from '@dzgrid/dom/locales/fr';
import { FR_EDIT } from '@dzgrid/dom/locales/fr-edit';
import { FR_EDITORS } from '@dzgrid/dom/locales/fr-editors';
type Note = { id: string; text: string };
const editing = edit<Note>();
const grid = createGrid<Note>(document.getElementById('notes')!, {
rows: [{ id: 'n1', text: 'Nourrir le dragon' }],
columns: [{ id: 'text', header: 'Note', editable: true }],
getRowId: (n) => n.id,
messages: { ...FR, text: { ...FR.text, ...FR_EDIT.text, ...FR_EDITORS.text } },
features: [editing, editors()],
onEvent: (e) => {
if (e.type === 'undoChanged') console.log('peut annuler :', e.canUndo, 'peut rétablir :', e.canRedo);
},
});
document.getElementById('undo')!.addEventListener('click', () => editing.api(grid)?.undo());

En Angular : [save]="save" sur <dz-grid>, (cellEdited), (saveFailed), (undoChanged), et ctrl.edit()?.undo().

Une partie editor dessine le champ ; le keep(input) de son contexte garde une valeur (par les mêmes contrôles et le même enregistrement), cancel() ferme sans valeur, refuse(code) refuse avec une raison. Enregistrez-la sous un nom et faites pointer l’edit.editor de la colonne vers elle (ou nommez-la editor.<type> pour remplacer l’éditeur de tout un type).