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

简体中文

English
Français
Deutsch
Português

简体中文

English
Français
Deutsch
Português

外观

Sidebar Navigation

快速入门

使用 Pivotick 的项目

配置

回调

边图层

简化

布局

渲染

模拟

自定义界面

侧栏

提示框

上下文菜单

筛选

图例

数据表格

界面样式

翻译界面

插件

Pivotick API

拓展与数据增强

保存拓展结果

撤销与历史记录

安全

API 文档

示例库

基础

节点样式

边样式

布局

事件与回调

界面自定义

编辑与创建

备注与注释

筛选、搜索与层级

编程控制

主题与性能

拓展与数据增强

综合示例

可过滤的图例 ​

颜色编码的图表需要一个键 - 一旦有了键,该键就是 UI 中最快的过滤器。 UI.legend 将一个停靠在画布角落:每个类别有一个样本、一个标签和一个节点计数,单击一行会隐藏该类别。指向一行首先会点亮画布上的节点,并使其余节点变暗,因此可以在关闭之前找到类别。

一行配置它:legend: { key: 'type' }。该库收集 data.type 的不同值,从 渲染器已解析的的颜色中读取每个样本,并计算其后面的节点。通常,您根本不需要线条 - 声明了 render.nodeTypeAccessor 后,一旦显示该尺寸,就会出现一个图例来解释颜色(请参阅Legend);该卡声明了显示旋钮的块。图例纯粹是描述性的 - 这里的颜色来自选项中的ColorPaletteMapper,与按类别的颜色完全相同,并且图例从未分配颜色。更改调色板,图例如下。

单击行会通过 graph.queryEngine 进行过滤,因此与 same 过滤标题的 图形过滤器 面板一样。该卡在图例自己的键下声明了一个type面,这使得二合一控制:关闭图例中的api,打开面板(漏斗,或Shift+K)并且其服务类型多选已将其丢弃。 resetFilters() — 或面板的 重置 — 重新点亮每一行。

根本没有 type 的节点不会获得任何行,并且图例永远不会隐藏它们:它只能作用于它列出的类别。

一把钥匙很少能说明整个故事。 同时使用两个键在同一个停靠卡中按尺寸堆叠一个标题部分 - 按下面的type +区域:type继续驱动面板的小面,zone获得自己的一个部分,并且两个过滤器和在一起,因此隐藏api 和 dmz 留下了两者都不是的东西。 zone 声明其自己的样本颜色,因为画布在其颜色中编码 type,并且 zone 的采样样本只是巧合。

部分也可以对 edges 而不是节点进行关键帧 — scope: 'edge' 使用线样本列出图形的关系类型,并在单击一个样本时隐藏整个层。这需要不同类型的边缘,因此它位于 Edge 层 卡上。

在节标题中: 显示全部、反转,以及将该节折叠到其标题的 V 形图标 - alt 键单击 V 形图标以折叠每个节。 悬停一行以在画布上点亮其类别; 按住 alt 键单击 即可仅显示该类别。切换通过 legendToggle 记录到控制台,这为其部分命名。

js
// import { ColorPaletteMapper } from 'pivotick'

// The consumer owns the colours — here a palette mapper over `type`, exactly as in
// the "Color by category" card. The legend never assigns a colour.
const palette = new ColorPaletteMapper('okabe-ito')

// `key` is all a legend needs: it collects the distinct values of `data.type`,
// reads each swatch out of the colour the renderer resolved, counts the nodes
// behind it, and makes every row a filter toggle.
//
// You can also leave `UI.legend` out entirely: with a `render.nodeTypeAccessor`
// declared, a legend appears by itself as soon as that dimension is shown to
// explain the colours. `UI.legend: false` opts out.
const legend = {
    key: 'type',
    title: 'Service type',
    // Every one of these is a default — spelled out here to show the knobs.
    position: 'bottom-left',
    showCounts: true,
    collapsible: true,
    filterable: true,
    maxVisibleEntries: 12
}
js
const options = {
    UI: {
        // The legend is chrome: `full` and `light` modes have it, `viewer` and
        // `static` don't. Full mode also ships the Graph Filters panel, which the
        // legend below shares a filter with.
        mode: 'full',
        // Off: `full` mode brings a minimap and the data dock, and this card is about
        // neither (see the Minimap and Data table cards for those).
        minimap: false,
        table: false,
        legend,
        // Declaring a facet under the *same key* the legend uses makes the two one
        // control: toggle a swatch and the panel's multiselect follows, and the
        // other way round. (The trade-off: an empty multiselect means "no
        // constraint" to the panel, so the legend won't let you switch off the last
        // remaining category.)
        filter: {
            facets: [
                {
                    key: 'type', label: 'Service type', type: 'multiselect',
                    options: (graph) => [...new Set(graph.getNodes().map((node) => node.getData().type))]
                        .sort().map((value) => ({ label: value, value }))
                }
            ]
        }
    },
    render: {
        defaultNodeStyle: {
            size: 15,
            strokeColor: '#ffffff',
            textColor: '#334155',
            text: (node) => node.getData()?.name,
            textVerticalShift: -1.8,
            color: (node) => palette.getColor(node.getData()?.type)
        }
    }
}
js
// The legend announces every toggle on the data bus, so the choice can be
// persisted, mirrored elsewhere, or logged.
function watchLegend(graph) {
    graph.on('legendToggle', ({ section, hidden, visible }) => {
        console.log(`legend [${section}]: showing ${visible.join(', ') || '(none)'} — hiding ${hidden.join(', ') || '(none)'}`)
    })
}

// Swap the legend at runtime — a graph that started without one gets it built on
// the spot, and `undefined` removes it (clearing its filter with it).
function legendByType(graph) {
    graph.setLegend({ key: 'type', title: 'Service type' })
}

function removeLegend(graph) {
    graph.setLegend(false)
}

// Declared entries, for when the categories aren't a plain data key: supply the
// label, the colour and the predicate yourself. An array works too; a function is
// re-resolved whenever the data changes.
function legendByTier(graph) {
    graph.setLegend({
        title: 'Tier',
        entries: [
            {
                id: 'edge', label: 'Edge', color: '#0072B2',
                predicate: (node) => node.getData().type === 'web'
            },
            {
                id: 'service', label: 'Services', color: '#E69F00',
                predicate: (node) => node.getData().type === 'api'
            },
            {
                id: 'storage', label: 'Storage', color: '#009E73',
                predicate: (node) => ['database', 'cache'].includes(node.getData().type)
            }
        ]
    })
}

// Two keys at once: `sections` stacks one titled block per dimension in a single
// docked card. Each section filters on its own, and the filters *and* together —
// hide `api` above and `dmz` below and what is left is neither.
function legendByTypeAndZone(graph) {
    graph.setLegend({
        // `position` belongs to the card, not to a section. Top-left, because a
        // two-section card is tall enough to reach the mode rail in the corner the
        // single-key legend uses.
        position: 'top-left',
        sections: [
            // Still adopts the declared `type` facet, so this section and the filter
            // panel remain one control.
            { key: 'type', title: 'Service type' },
            {
                key: 'zone',
                title: 'Network zone',
                // The colours encode `type`, not `zone`, and the legend can only
                // sample colours — so this section declares swatches of its own
                // rather than showing one that would be a coincidence. `key` still
                // supplies the predicate.
                entries: [
                    { id: 'dmz', label: 'DMZ', color: '#CC79A7' },
                    { id: 'internal', label: 'Internal', color: '#56B4E9' }
                ]
            }
        ]
    })
}
js
// A service graph whose `type` drives both the colours and the legend. The counts
// differ per type on purpose — the legend reports them. `zone` is a second
// dimension the colours say nothing about — see the stacked legend below.
const data = {
    nodes: [
        { id: 'web-1', data: { name: 'Web app', type: 'web', zone: 'dmz' } },
        { id: 'web-2', data: { name: 'Mobile web', type: 'web', zone: 'dmz' } },
        { id: 'api-1', data: { name: 'Auth API', type: 'api', zone: 'internal' } },
        { id: 'api-2', data: { name: 'Orders API', type: 'api', zone: 'internal' } },
        { id: 'api-3', data: { name: 'Billing API', type: 'api', zone: 'internal' } },
        { id: 'db-1', data: { name: 'Postgres', type: 'database', zone: 'internal' } },
        { id: 'db-2', data: { name: 'Replica', type: 'database', zone: 'internal' } },
        { id: 'cache-1', data: { name: 'Redis', type: 'cache', zone: 'internal' } }
    ],
    edges: [
        { from: 'web-1', to: 'api-1' },
        { from: 'web-1', to: 'api-2' },
        { from: 'web-2', to: 'api-1' },
        { from: 'api-2', to: 'api-3' },
        { from: 'api-1', to: 'cache-1' },
        { from: 'api-2', to: 'db-1' },
        { from: 'api-3', to: 'db-1' },
        { from: 'db-1', to: 'db-2' }
    ]
}
Pager
下一页快速入门