Deshacer e historial
graph.history es un registro de lo que contiene y muestra el lienzo: lo que se trajo, lo que se sacó, lo que está oculto y una forma de recuperarlo.
No es un registro de todas las mutaciones que la biblioteca puede realizar. Los datos de un nodo pertenecen a su backend, por lo que la biblioteca no ofrecerá revertir un campo que no haya creado. que puede hablar para su composición, que es también sobre lo que razona un analista: llegaron doce nodos, tres Fue, cinco están escondidos.
Vocabulario, usado consistentemente a continuación.
- entrada: algo reversible que sucedió, como una fila en el menú
- kind: cuál de los seis tipos de entrada es
- span: el bloque contiguo desde la entrada más nueva hasta aquella en la que hizo clic
- persisted: una entrada que el consumidor escribió en un backend
- sellado: una eliminación persistente — listada, nunca revertida
los seis tipos
Un conjunto cerrado. Todo lo que registra la biblioteca es uno de estos y no se registra nada más.
| amable | que es | como se invierte |
|---|---|---|
pivot | Una ingesta: consulte Pivotes y enriquecimiento | La garantía de la carrera se descarta y todo lo que no avala se pierde. |
delete | Una eliminación de nodos o bordes iniciada por el usuario | Los elementos regresan, procedencia y todo. |
visibility | Un ocultar o mostrar duradero (queryEngine.excludeNode / includeNode) | el otro |
create | Un nodo o borde dibujado a mano. | El elemento desaparece, a menos que algo más lo avale. |
removal | Una llamada graph.removeBySource(source) | Lo que queda vuelve y lo que queda recupera el aval de la fuente. |
group | Un grupo creado, renombrado o eliminado a mano (ver Simplificar) | Los grupos hechos a mano vuelven a ser como eran; no se agrega ni elimina ningún nodo |
No registrado, a propósito:
- Ediciones de propiedad. Estado del backend, como arriba.
onBeforeNodeEditCommites donde tú implementar el tuyo propio. - Estado de filtro y leyenda. Configuraciones con su propio Borrar en lugar de actos. el filtro El formulario se aplica en vivo con cada pulsación de tecla, por lo que escribir
domaingeneraría seis entradas. - Notas. Texto de autor, más cercano a una edición que a una composición gráfica.
graph.hideNode(). Se borra la próxima vez que los filtros se vuelvan a derivar, por lo que se debe crear una entrada para ello. revertiría algo que ya se había revertido.excludeNodees la piel que sobrevive, y el que está registrado.- Abrir un grupo, sacar un nodo, cambiar una regla de Simplificación. Una forma de mirar, como los filtros.
Las mutaciones programáticas (graph.addNode(), graph.removeNode()) tampoco son entradas. Sólo un gesto es, más graph.removeBySource(), que es como una aplicación ofrece la analista una acción de "eliminar lo que trajo esta fuente", y graph.simplify.groupNodes(), renameGroup() y ungroup(), las llamadas detrás de los gestos de grupo.
Deshacer es contiguo
Apuntar a la fila tres hacia abajo deshace esos tres, como uno dataBatchChanged y uno volver a renderizar. No hay manera de revertir una entrada antigua por sí sola: una pila cuyo medio puede ser extraído ya no describe cómo el gráfico llegó a esta posición.
Para "ese proveedor devolvió basura, déjela y quédese con el resto", use graph.removeBySource(source). Elimina la garantía de una fuente y elimina sólo lo que nada más lo avala, por lo que un nodo que también se encontró en una segunda ejecución sobrevive, un reclamo más ligero. es un operación de avance, registrada como una nueva entrada en lugar de un rebobinado.
Esa entrada se registra cada vez que la fuente atestigua algo, incluso cuando todos los elementos permanecen porque otra fuente también lo avala: el reclamo se fue de cualquier manera, y la fila dice No salió nada del lienzo. Al deshacerlo se restaura la garantía de cada elemento exactamente como estaba, por lo que la entrada de ejecución debajo todavía está completa y el siguiente Deshacer retoma esa ejecución.
Rehacer es estrictamente lineal y cualquier nueva acción lo vacía. Una carrera que se deshizo y luego abandonado por trabajos posteriores se ha ido; recuperarlo significa ejecutar el pivote nuevamente.
Desde la barra superior
Cada uno de los dos botones en el encabezado es un botón dividido: el ícono avanza una vez, el cursor abre la historia. Ctrl+Z y Ctrl+Shift+Z (⌘Z y ⌘⇧Z en macOS) llegan a los mismos dos pasos sin el encabezado y pasar al propio texto del navegador para deshacer mientras un campo de texto tiene foco. Ambos botones viven en modo full y light, que es donde está el encabezado, y UI.history: { enabled: false } los elimina, el menú desplegable y los dos atajos por un el consumidor deshacer desde su propio cromo: graph.history continúa grabando de cualquier manera.
El menú desplegable es una lista de cualquiera de los botones, el más nuevo en la parte superior, con una línea now it: las filas de arriba se han deshecho, las filas de abajo son las que aún se pueden deshacer. Haga clic en fila y la línea pasa por ella, que es el mismo gesto en ambas direcciones.
Cada fila lleva el ícono de su tipo, su etiqueta y cuántos pasos recorre un clic. Ese número es el que indica el pie de página, por lo que la lista y el pie de página no pueden estar en desacuerdo. el El pie de página indica el efecto neto exacto antes de que se cometa algo. Las teclas de flecha apuntan, Enter viaja, Escape se cierra.
El lienzo también responde.
Al pasar el cursor sobre una fila pronostica el cambio en el lienzo y no cambia nada más: los elementos el clic sacaría el drenaje donde están, los nodos los escondería con ellos, y lo que sea que traería de regreso está delineado dónde aterrizaría: un nodo que el gráfico no muestra espera todavía, dibujado como un anillo en la posición que dejó. Esa última parte es la mayor parte de lo que tiene un rehacer. decir, y la razón por la que un pronóstico no es un punto culminante: nada retrocede para hacer lo marcado Los elementos se destacan, porque la pregunta es qué cambiaría, no qué elementos nombra una fila.
Los esquemas no son conjeturas. Un deshacer observa dónde se encontraba cada elemento cuando lo quitó del lienzo, y rehacer lo devuelve allí, por lo que lo que dibuja el pronóstico es donde aterriza el clic cosas. Un pivote reproduce los datos sin procesar de su proveedor, que no contienen coordenadas, por lo que sin eso, una ingesta volvería esparcida en algún lugar nuevo.
preview() devuelve lo mismo que los datos, en forecast y graph.showForecast(…) / graph.clearForecast() pintarlo, para que la interfaz de usuario del historial del consumidor o cualquier otra cosa que quiere preguntar "¿y si?", obtiene el lenguaje del lienzo de forma gratuita.
Operaciones escritas en un backend
Sólo el consumidor puede saber que una operación se escribió en un backend, por lo que sólo el el consumidor puede decirlo. onBeforeNodeCreate, onBeforeEdgeCreate y onBeforeDelete cada uno aceptar persisted en su decisión:
callbacks: {
onBeforeNodeCreate: async (ctx) => {
const values = await ctx.promptData({ fields: [{ key: 'value', label: 'Value', type: 'text' }] })
if (!values) return false
const saved = await myBackend.create(values)
return { accept: true, id: saved.uuid, data: values, persisted: true }
},
}Deshacer no emite ninguna escritura de compensación, por lo que lo que hace con dicha entrada depende de en qué dirección la escritura fue.
Una creación persistente todavía se revierte. El lienzo pierde algo que el backend conserva, lo cual una nueva recuperación se deshace, por lo que la entrada se marca guardada en lugar de retenerse y el pie de página cuenta antes del clic: Undoes 2 steps · 1 item saved upstream.
Una eliminación persistente está sellada: aparece en la lista, porque es parte de cómo se obtuvo el lienzo de esta manera, pero nunca al revés. Restaurarlo devolvería un nodo que el backend ya no tiene, y nada en el lienzo posterior lo diría. Un tramo que lo contiene pasa sobre él en lugar de que detenerse allí, y lo dice primero: Undoes 2 of 3 · 1 saved item kept.
const [newest] = graph.history.entries()
newest.persisted // the consumer wrote it through
newest.sealed // …and it was a deletion, so undo will not take it backSaltar funciona porque cada entrada nombra los elementos específicos que tocó, por lo que no hay diferencias estatales para salirse del orden. Por eso también los seis tipos son cerrados y un consumidor no puede registrar una entrada propia: una devolución de llamada arbitraria en la pila podría llegar a la mitad a través de un lapso, y la historia no tendría manera de saberlo.
Recuperar una ingesta
Al deshacer una ingesta se vuelven a preparar sus candidatos, de modo que las filas regresen al panel Revisar donde provienen, el panel se vuelve a abrir si la ingesta lo había cerrado, los rechazos anteriores aún se mantienen, y no se llama a ningún proveedor. Esa última parte es el punto: la alternativa es recuperar dos mil correlaciones a través de una API de velocidad limitada para corregir un error cometido hace dos segundos.
Regresan sin clasificar en lugar de seguir marcados. Recuperar la carrera es para pasar correctamente, no para volver a aterrizar los mismos doce con un solo clic.
Una ejecución que aterrizó sin revisión, como un pivote con un clic desde el menú contextual o un El proveedor que establece autoIngest no proviene de ningún panel, por lo que deshacerlo solo elimina sus nodos.
Esto sucede solo cuando la ingesta sigue siendo la entrada más nueva. Si algo tiene sucedió desde entonces, deshacer elimina los nodos y no aparece ningún panel: un panel que resucita a sí mismo sobre El trabajo posterior sería peor que volver a buscarlo.
lo que contiene
Treinta entradas, el mayor desalojado. Vale la pena entender el desalojo, porque no es simétrico: pierde la capacidad de restaurar una ejecución anterior, no de eliminar una. La procedencia vive de los propios elementos, por lo que graph.removeBySource(pivotId) aún alcanza Una carrera que la historia ha olvidado.
El historial es solo sesión. No sobrevive a una recarga. Restaurar un estado anterior de un gráfico completo es una característica diferente, y no ésta.
Conduciéndolo
graph.history.entries() // what can be undone, newest first — the order the menu renders
graph.history.redoable() // what has been undone and can be redone, newest first
graph.history.canUndo()
graph.history.canRedo()
graph.history.undo() // the newest entry
graph.history.undo(entryId) // the span from the newest through that entry
graph.history.redo(entryId) // and back again
graph.history.preview(entryId) // what that span would do, before doing it
graph.history.on(history => …) // every change to either direction; returns a disposer
graph.history.clear() // forget everything; the canvas is left as it isEl id ** de una entrada de pivote es** es runId, por lo que un PivotRunOutcome ya lleva el identificador a su propia fila:
const outcome = await graph.pivots.run('correlations', graph.getSelectedNodes())
// …later
graph.history.undo(outcome.runId)preview(entryId, direction?) merece una nota. No resume las entradas; reproduce el lapso contra una copia del estado del gráfico, a través del mismo código al hacer clic confirma y diferencia el resultado:
const { entries, skipped, canvasOnly, nodes, edges, effect, forecast } = graph.history.preview(entryId)
// entries the span, newest first
// skipped the sealed ones inside it, which will be left alone
// canvasOnly the ones it reverses that the backend keeps a copy of
// nodes the elements it would touch that are on the canvas now
// effect { nodesRemoved, nodesRestored, edgesRemoved, edgesRestored, nodesHidden, nodesShown }
// forecast the same change as something to paint: { removing, hiding, touching, arriving }
graph.showForecast(forecast) // …and take it back down with graph.clearForecast()Como se juega más que se describe, los casos difíciles resultan inútiles: un ocultar cancelado por un mostrar redes posteriores a cero y lo dice, y un nodo un segundo pivote también avala no se cuenta como salida.
Procedencia y por qué la eliminación la recuerda
Cada nodo y borde lleva el conjunto de fuentes que lo avalan: 'seed' para los datos que fueron cargado, la identificación de un pivote para cualquier cosa que trajera y 'manual' para cualquier cosa dibujada a mano. un conjunto en lugar de un escalar, porque el caso normal es que dos pivotes se superpongan en un nodo.
Por lo tanto, una eliminación debe recordar la declaración de cada elemento, no sólo sus datos. sin eso: una corrida suma doce, el analista elimina tres, deshace la eliminación y los tres vienen de regreso como huérfanos desaparecidos; deshacer la ejecución después y luego dejarlos en el lienzo sin nada que los explique. Con él, se deshace la eliminación y luego la ejecución sale exactamente lo que había antes.
Una consecuencia que vale la pena conocer al actualizar: un nodo creado a mano informa ['manual'] de getSources(), no de ['seed']. Eso es lo que hace graph.removeBySource('manual') llegar al trabajo hecho a mano, y lo que permite deshacer una creación eliminar el elemento sólo cuando nada más todavía lo avala.