跳转到内容
Pivotick
Main Navigation 首页
快速入门
配置
API 参考
示例库项目在线体验

简体中文

English
Français
Deutsch
Português

简体中文

English
Français
Deutsch
Português

外观

Sidebar Navigation

快速入门

使用 Pivotick 的项目

配置

回调

边图层

简化

布局

渲染

模拟

自定义界面

侧栏

提示框

上下文菜单

筛选

图例

数据表格

界面样式

翻译界面

插件

Pivotick API

拓展与数据增强

保存拓展结果

撤销与历史记录

安全

API 文档

示例库

基础

节点样式

边样式

布局

事件与回调

界面自定义

编辑与创建

备注与注释

筛选、搜索与层级

编程控制

主题与性能

拓展与数据增强

综合示例

添加轨道模式 ​

左栏容纳模式,而不是按钮。选择、创建、查看和物理都是内置的,每个工具都只显示自己的工具——这就是为什么无论图表能做什么,轨道仍保持四个槽高的原因。 addRailMode() 添加了第五个。

该卡添加了用于遍历服务图的 Explore 模式:选择一个节点,选择其邻居,将画布缩小到它们,然后退出。按 E,或单击分隔线下方的指南针。

您的模式落地的地方 ​

注册模式位于分隔符下方的,在内置模式之后,按order排序。插件无法重新排序或删除内置的四个,因此每个Pivotick图中线上方的所有内容都是相同的,而线下方的所有内容都是您的。

声明工具,而不是标记 ​

模式的 tools 成为上下文面板中的行,就像选择和创建一样绘制。你提供动词;每行的 kind 表示它的行为方式:

kind行为
'action'运行一次并保留该模式 - 所有三个工具都在这里
'toggle'装备一个工具:面板折叠,导轨按钮变成该工具,就像Select的插槽变成Lasso一样
'default'模式所依赖的工具,以及切换返回的内容

每当选择发生变化时,都会重新检查enabled() - 这就是为什么选择邻居和隔离保持灰色,直到您选择一个节点。

这里没有什么特权 ​

上面的每个工具都调用公共 API,您可以调用自己:getEdges() 遍历关系,selectNodes() 选择它们,setVisibleNodes() 缩小画布。这种模式只是给了他们一个居住的地方——一个铁路槽口、一条捷径、一个武装状态和一个面板。

这就是要点:您可以构建一个了解“邻居”对您的数据意味着什么的探索模式,而我们却不能。

模式相互排斥

打开“探索”会关闭“视图”和“物理”,反之亦然 - 一个商店,一种活动模式。撤消您在 onExit 中设置的任何模式;如果在模式处于活动状态时删除该模式,它也会运行,并且导轨会回退到“选择”。

当浮出控件更适合时 ​

配置而不是操作的模式需要宽覆盖,而不是窄工具条。设置 kind: 'flyout',对导出的 Flyout 进行子类化,并传递 addRailMode 工厂 — 请参阅贡献铁路模式。

js
// The rail's four modes — Select, Create, View, Physics — are built in. `addRailMode`
// is how you add a fifth. It renders below a divider, after them, and drives the same
// contextual tool panel the built-ins do: declare `tools`, and the arming, the
// enabled/disabled states and the collapse are handled for you.
//
// This card adds an **Explore** mode for walking the graph: reach the neighbours of what
// is selected, isolate a subgraph, and step back out. Nothing here is privileged — every
// tool is public graph API a consumer could call themselves.

const compass = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20"><path fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round" d="M12 21a9 9 0 1 0 0-18a9 9 0 0 0 0 18Z"/><path fill="currentColor" d="M15.5 8.5l-2 5l-5 2l2-5z"/></svg>'
const expandIcon = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M11 13v3a1 1 0 0 0 2 0v-3h3a1 1 0 0 0 0-2h-3V8a1 1 0 0 0-2 0v3H8a1 1 0 0 0 0 2zm1 9a10 10 0 1 1 0-20a10 10 0 0 1 0 20m0-2a8 8 0 1 0 0-16a8 8 0 0 0 0 16"/></svg>'
const isolateIcon = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="18" height="18" fill="none" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"><path d="M16 6H8a2 2 0 0 0-2 2v8m10 26H8a2 2 0 0 1-2-2v-8m26 10h8a2 2 0 0 0 2-2v-8M32 6h8a2 2 0 0 1 2 2v8"/><circle cx="24" cy="24" r="6"/></svg>'
const resetIcon = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18"><path fill="currentColor" d="M12 5V2L8 6l4 4V7a5 5 0 1 1-5 5H5a7 7 0 1 0 7-7"/></svg>'

function exploreMode() {
    return {
        name: 'explore-mode',
        install(ctx) {
            const interaction = () => ctx.graph.renderer.getGraphInteraction()
            const selected = () => interaction().getSelectedNode()?.node

            /** Every node one hop from `id`, itself included. */
            const withNeighbours = (id) => {
                const keep = new Set([id])
                for (const edge of ctx.graph.getEdges()) {
                    if (edge.from.id === id) keep.add(edge.to.id)
                    if (edge.to.id === id) keep.add(edge.from.id)
                }
                return keep
            }

            ctx.addRailMode({
                id: 'explore',
                label: 'Explore',
                icon: compass,
                // Pressing E switches to the mode, or toggles its panel when it is
                // already active — exactly what V and C do for Select and Create.
                shortcut: 'E',
                tools: [
                    {
                        // A one-shot action: it runs and leaves the mode as it was.
                        id: 'expand',
                        label: 'Select neighbours',
                        icon: expandIcon,
                        kind: 'action',
                        // Re-checked on every selection change, so the row greys out
                        // when it would do nothing.
                        enabled: () => !!selected(),
                        run: () => {
                            const node = selected()
                            if (!node) return
                            const keep = withNeighbours(node.id)
                            interaction().selectNodes(
                                ctx.graph.getMutableNodes()
                                    .filter(n => keep.has(n.id))
                                    .map(n => ({ node: n, element: n.getGraphElement() }))
                            )
                        },
                    },
                    {
                        id: 'isolate',
                        label: 'Isolate neighbourhood',
                        icon: isolateIcon,
                        kind: 'action',
                        enabled: () => !!selected(),
                        run: () => {
                            const node = selected()
                            if (!node) return
                            const keep = withNeighbours(node.id)
                            ctx.graph.setVisibleNodes(
                                ctx.graph.getMutableNodes().filter(n => keep.has(n.id))
                            )
                        },
                    },
                    {
                        id: 'reset',
                        label: 'Show everything',
                        icon: resetIcon,
                        kind: 'action',
                        run: () => ctx.graph.setVisibleNodes(ctx.graph.getMutableNodes()),
                    },
                ],
            })
        },
    }
}

const options = {
    UI: {
        mode: 'full',
        // Neither is what this card is about, and both crowd a small canvas.
        minimap: false,
        table: false,
    },
    plugins: [exploreMode()],
}
js
const data = {
    nodes: [
        { id: 'api', data: { label: 'api-gateway', kind: 'service' } },
        { id: 'auth', data: { label: 'auth-service', kind: 'service' } },
        { id: 'billing', data: { label: 'billing-service', kind: 'service' } },
        { id: 'search', data: { label: 'search-service', kind: 'service' } },
        { id: 'users-db', data: { label: 'users-db', kind: 'datastore' } },
        { id: 'orders-db', data: { label: 'orders-db', kind: 'datastore' } },
        { id: 'index', data: { label: 'search-index', kind: 'datastore' } },
        { id: 'cache', data: { label: 'edge-cache', kind: 'datastore' } },
        { id: 'mailer', data: { label: 'mailer', kind: 'worker' } },
        { id: 'reindexer', data: { label: 'reindexer', kind: 'worker' } },
    ],
    edges: [
        { from: 'api', to: 'auth' },
        { from: 'api', to: 'search' },
        { from: 'api', to: 'billing' },
        { from: 'api', to: 'cache' },
        { from: 'auth', to: 'users-db' },
        { from: 'billing', to: 'orders-db' },
        { from: 'billing', to: 'mailer' },
        { from: 'search', to: 'index' },
        { from: 'reindexer', to: 'index' },
        { from: 'reindexer', to: 'orders-db' },
    ],
}
Pager
下一页快速入门