跳转到内容

撤消和历史记录 ​

graph.history 记录了画布所容纳和显示的内容——带进来的东西、拿走的东西、隐藏的东西——以及将其取回的方法。

它并不是库可以执行的每个突变的记录。节点的数据属于您的后端,因此库不会提供恢复非其创作的字段。它所代表的就是组合,这也是分析师的推理:十二个节点到达,三个节点离开,五个节点隐藏。

词汇,下面一致使用

  • entry:发生的一件可逆的事情,作为菜单中的一行
  • kind:是六种条目中的哪一种
  • span:从最新条目到您单击的条目的连续块
  • persisted:消费者写入后端的条目
  • 密封:持久的删除 — 列出,永不逆转

六种 ​

一个闭集。图书馆记录的所有内容都是其中之一,没有记录其他任何内容。

种类它是什么是如何逆转的
pivot一次摄取 — 请参阅拓展和浓缩运行的担保被放弃,任何其他担保都消失
delete用户发起的节点或边删除元素回归,出处等等
visibility持久的隐藏或取消隐藏 (queryEngine.excludeNode / includeNode)另一个
create手工绘制的节点或边该元素会消失,除非有其他东西证明它
removalgraph.removeBySource(source) 通话剩下的会回来,留下的会得到来源的担保
group手动创建、重命名或删除的组(请参阅简化)手工制作的团体又回到了原来的样子;没有添加或删除节点

故意不记录:

  • 属性编辑。后端状态,如上所述。 onBeforeNodeEditCommit 是您实现自己的的地方。
  • 过滤器和图例状态。设置有自己的清除而不是行为。过滤器表单适用于每次击键,因此输入 domain 将推送六个条目。
  • 注释。 编写的文本,更接近于编辑而不是图形合成。
  • graph.hideNode(). 下次过滤器重新派生时它会被擦除,因此它的条目会反转已经恢复自身的内容。 excludeNode 是幸存的皮革,也是被记录的皮革。
  • 打开一个组,拉出一个节点,切换一个简化规则。一种查看方式,就像过滤器一样。

程序性突变——graph.addNode()、graph.removeNode()——也不是条目。只有手势,再加上 graph.removeBySource()(应用程序如何向分析师提供“删除此源带来的内容”操作)以及 graph.simplify.groupNodes()、renameGroup() 和 ungroup()(组手势背后的调用)。

撤消是连续的 ​

瞄准向下的第三行会撤消这三行,因为一个 dataBatchChanged 和一个重新渲染。没有办法单独反转一个旧条目:中间可以拉出的堆栈不再描述图是如何变成这样的。

对于“该提供商返回了垃圾,请将其丢弃并保留其余部分”,请使用 graph.removeBySource(source)。它会放弃一个源的担保,只删除其他没有担保的节点,因此第二次运行也发现的节点仍然存在,有人声称更轻。这是一个前进操作,记录为新条目而不是倒回。

每当来源担保任何内容时,即使每个元素都留下来,因为另一个来源也担保它,该条目就会被记录:无论哪种方式,声明都消失了,并且该行显示没有留下画布。撤消它会按原样恢复每个元素的担保,因此它下面的运行条目仍然完整,下一个撤消会恢复该运行。

重做是严格线性的,任何新操作都会清空它。一次被撤销并因后续工作而搁浅的运行已经消失了;恢复它意味着再次运行拓展。

从顶部栏 ​

标题中的两个按钮都是一个拆分按钮:图标步进一次,插入符号打开历史记录。 Ctrl+Z和Ctrl+Shift+Z(⌘Z和macOS上的⌘⇧Z)在没有标题的情况下达到相同的两个步骤,并在文本字段具有焦点时进入浏览器自己的文本撤消。这两个按钮都处于 full 和 light 模式,这是标题所在的位置,UI.history: { enabled: false } 将它们、下拉菜单和两个快捷键移开,以便消费者从自己的 chrome 驱动撤消操作 - graph.history 继续以任何一种方式进行记录。

下拉菜单是任一按钮的一个列表,最新的位于顶部,其中有 now 线:上面的行已被撤消,下面的行仍然可以撤消。单击一行,线条就会经过它,这在两个方向上都是相同的手势。

每行都带有其种类的图标、标签和点击它会移动多少步。该数字是页脚所显示的数字,因此列表和页脚不能不一致。页脚说明了提交任何内容之前的确切净效果。方向键瞄准,Enter 移动,Escape 关闭。

画布也给出了答案 ​

悬停一行会预测画布上的变化,并且不会改变任何其他内容:单击会取出它们所在位置的元素,它会隐藏它们的节点,而它会带回来的东西是概述它将落在 - 图形尚未包含的节点,在它离开的位置绘制为圆环。最后一部分是重做必须说的大部分内容,也是预测不是亮点的原因:没有任何后退可以使标记的元素脱颖而出,因为问题是会发生什么变化,而不是行命名哪些元素。

轮廓不是猜测。撤消会记录每个元素从画布上取下时所处的位置,而重做则将其放回原处 - 因此预测绘制的就是单击所落在的位置。拓展表从其提供者的原始数据中重放,该数据不携带坐标,因此如果没有坐标,摄取将分散在新的地方。

preview() 返回与 forecast 下的数据相同的内容,并且 graph.showForecast(…) / graph.clearForecast() 绘制它 - 因此消费者自己的历史 UI,或任何其他想要询问“假设”的内容,都可以免费获得画布语言。

写入后端的操作 ​

只有消费者才能知道操作已写入后端,因此只有消费者可以这么说。 onBeforeNodeCreate、onBeforeEdgeCreate和onBeforeDelete各自接受persisted的决定:

js
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 }
    },
}

撤消不会发出补偿写入,因此它对此类条目执行的操作取决于写入的方式。

持续的创造仍然会逆转。画布丢失了后端保留的内容,重新获取会撤消该内容,因此该条目被标记为 saved 而不是被保留,并且页脚在单击之前对其进行计数:Undoes 2 steps · 1 item saved upstream。

持久的删除是密封:它被列出,因为它是画布如何获得这种方式的一部分,但从未逆转。恢复它会放回后端不再有的节点,并且之后画布上不会有这样的信息。包含一个的跨度会越过它而不是停在它处,并首先这样说:Undoes 2 of 3 · 1 saved item kept。

js
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 back

跳过之所以有效,是因为每个条目都会对其所触及的特定元素进行命名,因此不会出现混乱的状态差异。这也是为什么这六种类型是封闭的,并且消费者无法记录自己的条目:堆栈中的任意回调可能会在跨度中途抛出,并且历史记录无法知道。

收回摄取 ​

撤消摄取会重新暂存其候选项,因此行会返回到它们来自的“审阅”窗格中,如果摄取已将其关闭,则窗格会重新打开,之前的拒绝仍然有效,并且不会调用任何提供者。最后一部分是重点:另一种方法是通过速率受限的 API 重新获取 2000 个相关性,以修复两秒前发生的错误。

他们回来时没有进行分类,而不是仍然被标记。回来是为了正确地完成它,而不是为了一键重新着陆相同的十二个。

未经审查而运行的运行(例如从上下文菜单中的一键式拓展或设置 autoIngest 的提供程序)来自任何窗格,因此撤消它只会删除其节点。

仅当摄取仍然是最新条目时,才会发生。如果此后发生了任何事情,撤消会删除节点并且不会出现任何窗格:在以后的工作中自行复活的窗格会比重新获取更糟糕。

它包含什么 ​

三十名参赛者,最年长的被驱逐者。驱逐值得理解,因为它不是对称的:它失去了 恢复旧运行的能力,而不是删除一个。起源依赖于元素本身,因此 graph.removeBySource(pivotId) 仍然达到了被历史遗忘的程度。

历史记录是仅会话。它无法在重新加载后幸存。恢复整个图的早期状态是一个不同的功能,而不是这个。

驾驶它 ​

js
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 is

拓展条目的 id ** 是 ** 它的 runId,因此 PivotRunOutcome 已经将句柄带到了自己的行:

js
const outcome = await graph.pivots.run('correlations', graph.getSelectedNodes())
// …later
graph.history.undo(outcome.runId)

preview(entryId, direction?) 值得注意。它不会总结条目 - 它通过单击提交的相同代码根据图形状态的副本重播跨度,并比较结果:

js
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()

因为它是玩的而不是描述的,所以尴尬的情况毫无意义:隐藏被后来的取消隐藏网络取消为零并这样说,并且第二个拓展也担保的节点不被算作离开。

出处,以及为什么删除会记住它 ​

每个节点和边都带有一组为其提供担保的源:'seed'对于已加载的数据,它带来的任何内容的拓展表 ID,以及'manual'对于任何手绘的东西。一个集合而不是一个标量,因为两个拓展在一个节点上重叠是正常情况。

因此,删除必须记住每个元素的担保,而不仅仅是其数据。如果没有这个:一次运行会增加十二个,分析师删除三个,撤消删除,然后三个作为下落不明的孤儿返回;之后撤消运行,然后将它们留在画布上,没有任何解释它们的东西。有了它,撤消删除,然后运行就留下之前的内容。

升级时值得了解的一个结果是:手动创建的节点从 getSources() 报告['manual'],而不是['seed']。这就是使 graph.removeBySource('manual') 达到手绘效果的原因,也是让撤消创作仅在没有其他东西可以保证时才删除该元素的原因。