Annuler et historique
Les deux boutons de la barre supérieure sont des boutons partagés : l'icône avance une fois, le curseur ouvre tout l'historique. Il s'agit d'une chronologie traversée par une ligne now - les lignes du dessus ont été annulées, les lignes du dessous sont ce qui peut encore être annulé - et cliquer sur une ligne pour y accéder.
Créez un historique, puis ouvrez-le :
- Masquer le nœud ou Supprimer le nœud dans le menu contextuel d'un nœud
- Créer ▸ Ajouter un nœud sur le rail de gauche, puis cliquer sur le canevas
- Exécutez un pivot, si vous en avez un – une ingest est une entrée comme une autre
Survolez une ligne et elle marque la durée qu'un clic inverserait, indique l'effet net dans le pied de page et le prévoit sur la toile : ce qui irait s'écoule là où il se trouve, et ce qui reviendrait est indiqué là où il atterrirait. Ctrl + Z recule sans le menu.
L'annulation est contiguë : viser trois lignes vers le bas inverse ces trois, comme un seul lot.
// Nothing here switches the history on: `graph.history` records the four kinds of
// composition change on its own, and the two split buttons in the header are wired to
// it. `mode: 'full'` is what brings the header the buttons live in, and the left rail's
// Create tool.
const options = {
UI: {
mode: 'full',
// Off: this card is about the top bar, and `full` mode's minimap and data dock
// are neither (see the Minimap and Data table cards for those).
minimap: false,
table: false
},
render: { nodeTypeAccessor: (node) => node.getData().kind }
}// A consumer that writes a change through to its own backend says so. Undo makes no
// compensating write, so it follows the direction of yours: a creation still reverses
// and the row is chipped `saved`, while a deletion is sealed — undoing it would put
// back a node your backend no longer has.
const writeThrough = {
callbacks: {
onBeforeNodeCreate: async (ctx) => {
const values = await ctx.promptData({
fields: [{ key: 'label', label: 'Name', type: 'text' }]
})
if (!values) return false
const saved = await myBackend.create(values)
return { accept: true, id: saved.uuid, data: values, persisted: true }
},
onBeforeDelete: async (ctx) => {
await myBackend.remove(ctx.nodes.map((node) => node.id))
return { accept: true, persisted: true }
}
}
}const data = {
nodes: [
{ id: 'gw', data: { label: 'gateway-01', kind: 'Host' } },
{ id: 'db', data: { label: 'orders-db', kind: 'Database' } },
{ id: 'api', data: { label: 'orders-api', kind: 'Service' } },
{ id: 'cache', data: { label: 'session-cache', kind: 'Service' } },
{ id: 'job', data: { label: 'nightly-export', kind: 'Job' } },
{ id: 'sftp', data: { label: 'partner-sftp', kind: 'Host' } }
],
edges: [
{ from: 'gw', to: 'api', data: { label: 'routes' } },
{ from: 'api', to: 'db', data: { label: 'reads' } },
{ from: 'api', to: 'cache', data: { label: 'reads' } },
{ from: 'job', to: 'db', data: { label: 'reads' } },
{ from: 'job', to: 'sftp', data: { label: 'writes' } }
]
}L'annulation n'écrit jamais sur votre backend, donc une modification que vous avez écrite suit la direction dans laquelle elle est allée. Un creation s'inverse toujours — le canevas perd ce que le backend conserve et le pied de page le compte (Undoes 2 steps · 1 item saved upstream). Une deletion est scellée : répertoriée, jamais inversée, et un span en contenant une passe dessus (Undoes 2 of 3 · 1 saved item kept).
Voir Annuler et historique pour les quatre types d'entrée, ce qui n'est délibérément pas enregistré et comment l'annulation d'une acquisition remet ses candidats dans le volet de triage.