跳转到内容

侧边栏选项 ​

根据屏幕尺寸或用户偏好,侧边栏可以默认折叠。

确定侧边栏是否默认折叠。

  • 'auto'默认:保持侧边栏打开,除非屏幕空间不足,在这种情况下它会自动折叠。
  • true 侧边栏开始折叠。
  • false 侧边栏开始展开。

侧边栏显示图形元素的上下文信息。它具有三个可定制的组件:

UI.sidebar.enabled: false 放下整个列并将其宽度返回到画布。属性面板和相邻面板各有一个开关 - UI.propertiesPanel.enabled、UI.neighborsPanel.enabled - 可删除该面板及其分隔符,同时保留侧边栏的其余部分。请参阅关闭功能。

点击查看代码
js
UI: {
    mode: 'full',
    sidebar: {
        collapsed: false,
    },
    mainHeader: {
        render: (element) => {
            const div = document.createElement('div')
            div.textContent = 'Main Header'
            div.style.fontWeight = 'bold'
            div.style.color = 'darkred'
            div.style.background = 'var(--pvt-bg-color-8)'
            div.style.padding = '4px 8px'
            div.style.borderRadius = '4px'
            return div
        }
    },
    propertiesPanel: {
        render: (element) => {
            const div = document.createElement('div')
            div.textContent = 'Properties Panel'
            div.style.fontWeight = 'bold'
            div.style.color = 'darkred'
            div.style.background = 'var(--pvt-bg-color-8)'
            div.style.padding = '4px 8px'
            div.style.borderRadius = '4px'
            return div
        }
    },
    extraPanels: [
        {
            title: 'Extra panel #1',
            render: (node) => {
                const div = document.createElement('div')
                div.textContent = 'Extra Panel #1'
                div.style.fontWeight = 'bold'
                div.style.color = 'darkred'
                div.style.background = 'var(--pvt-bg-color-8)'
                div.style.padding = '4px 8px'
                div.style.borderRadius = '4px'
                return div
            },
        },
        {
            title: 'Extra panel #2',
            render: (node) => {
                const div = document.createElement('div')
                div.textContent = 'Extra Panel #2'
                div.style.fontWeight = 'bold'
                div.style.color = 'darkred'
                div.style.background = 'var(--pvt-bg-color-8)'
                div.style.padding = '4px 8px'
                div.style.borderRadius = '4px'
                return div
            },
        },
    ],
    tooltip: {
        enabled: false,
    },
    contextMenu: {
        enabled: false,
    },
},

主标题接口 ​

显示所选节点或边的简明摘要,例如标题和副标题。它可以帮助用户快速识别当前选定的元素。

主标题面板可以通过映射功能进行自定义。节点的默认映射是

ts
{
    title:    node => node.data.label ?? "Could not resolve title"
    subtitle: node => node.data.description ?? ""
}

您可以通过覆盖您需要的部分来更改此映射:

ts
const options = {
    UI: {
        mainHeader: {
            nodeHeaderMap: {
                title: node => `Node ${node.id}`,
                subtitle: node => node.data.type ?? "",
            }
        }
    }
}
ts
const options = {
    UI: {
        mainHeader: {
            edgeHeaderMap: {
                title: node => `Node ${node.id}`,
                subtitle: node => node.data.type ?? "",
            }
        }
    }
}
ts
const options = {
    UI: {
        mainHeader: {
            render: (element) => {
                const div = document.createElement('div')
                div.textContent = 'Main Header'
                return div 
            }
        }
    }
}

警告

当提供render()时,Pivotick会跳过所有默认映射逻辑,除非它返回undefined。 undefined 结果(或解析为 undefined 的承诺)绘制该选择的默认标头:一个元素的 nodeHeaderMap / edgeHeaderMap 标题、多个元素的选择计数以及未选择任何内容时的画布总数。返回 null 将标题留空。

ts
mainHeader: {
    // A custom header for a lone element, the default counts for everything else.
    render: (element) => (element && !Array.isArray(element)) ? myHeader(element) : undefined,
}

属性面板界面 ​

在侧栏中显示所选节点或边的详细属性。每个属性都有一个名称和值,可以是静态的,也可以是动态计算的。

默认行为是显示节点或边的 getData() 中的所有键/值对。

您可以使用映射函数(nodePropertiesMap和edgePropertiesMap)或完整的自定义渲染器自定义显示哪些属性以及如何渲染它们。

ts
const options = {
    UI: {
        propertiesPanel: {
            nodePropertiesMap: (node: Node) => {
                return [
                    {
                        name: 'Node ID',
                        value: node.id,
                    },
                    {
                        name: (node) => `Type of node`,
                        value: (node) => el ? el.type : 'Unknown'
                    },
                    {
                        name: 'Custom HTML',
                        value: document.createElement('div')
                    }
                ]
            }
        }
    }
}
ts
const options = {
    UI: {
        propertiesPanel: {
            edgePropertiesMap: (node: Node) => {
                return [
                    {
                        name: 'Edge ID',
                        value: node.id,
                    },
                    {
                        name: (node) => `Type of edge`,
                        value: (node) => el ? el.type : 'Unknown'
                    },
                    {
                        name: 'Custom HTML',
                        value: document.createElement('div')
                    }
                ]
            }
        }
    }
}
ts
const options = {
    UI: {
        propertiesPanel: {
            render: (element: Node | Edge | Node[] | Edge[] | null) => {
                const div = document.createElement('div')
                div.textContent = `Element ID: ${element?.id}`
                div.style.fontWeight = 'bold'
                return div
            }
        }
    }
}
ts
const options = {
    UI: {
        propertiesPanel: {
            nodePropertiesMap: (node: Node) => {
                return Object.entries(node.getData())
                    .filter(([key, value]) => key && value)
                    .map(([key, value]) => ({ name: key, value }))
            }
        }
    }
}

属性映射可能会返回多个同名条目,例如每个标签一个 Tag 条目。在多重选择中,每个值都会对携带它的节点进行计数,保留/排除与节点的任何条目匹配,并且每个值都有自己的条形,因为值重叠。

ts
nodePropertiesMap: (node) => node.getData().tags.map((tag) => ({ name: 'Tag', value: tag }))

小费

当提供render()时,Pivotick会跳过所有默认映射逻辑,除非它返回undefined。 undefined 结果(或解析为 undefined 的承诺)会绘制该选择的默认面板:一个元素的 nodePropertiesMap / edgePropertiesMap 行,以及多个元素的聚合表及其保留/排除过滤器。返回 null 使面板留空。

ts
const options = {
    UI: {
        propertiesPanel: {
            // A custom view for a lone node, the default aggregated table for everything else.
            render: (element) => (element instanceof Node) ? myNodeView(element) : undefined,
        }
    }
}

邻居面板界面 ​

显示所选节点的连接:其邻居的小图、连接统计信息和列表。

默认情况下,邻居图使用主图的渲染选项进行绘制。适合整个画布的样式(例如节点细节层或焦点卡)可能会挤满这种大小的面板,因此 graph 会覆盖它自己的:

  • graph.render 合并到主图的渲染选项上。面板自身的设置(无拖动、无缩放、无节点扩展)仍然适用。这里的 maxZoom 还限制了图形放大到适合的距离,因此具有一个邻居的节点不会被放大。
  • graph.layout 合并到面板的径向布局上。根始终是选定的节点。
ts
const options = {
    UI: {
        neighborsPanel: {
            graph: {
                render: {
                    // Plain small nodes: no tiers, no focus card.
                    nodeStyleMap: { event: { shape: 'circle', size: 10 } },
                    maxZoom: 1,
                },
                layout: { radialGap: 80 },
            },
        },
    },
}

额外面板接口 ​

允许添加具有动态或静态内容的完全自定义面板。这些面板非常适合显示与所选元素相关的附加上下文信息、自定义控件或交互式小部件。

每个额外面板都有一个可选的 title 和 render(),两者都可以是静态的(字符串/HTMLElement)或当前选择的函数。返回的 string 渲染为 text;返回 HTMLElement 来呈现您自己的标记。返回 null 使面板主体留空。

ts
const options = {
    UI: {
        extraPanels: [
            {
                title: "Info",
                render: "This is a static extra panel content"
            }
        ]
    }
}
ts
// The selection is a Node, an Edge, an array of either, or null — this example
// only cares about a single element:
const single = (element) => (element && !Array.isArray(element)) ? element : null

const options = {
    UI: {
        extraPanels: [
            {
                // Both are called with the live selection, on every selection change.
                title: (element) => single(element) ? `Node #${single(element).id}` : 'Nothing selected',
                render: (element) => {
                    const div = document.createElement('div')
                    div.textContent = single(element)?.getData().description ?? 'No description'
                    return div
                }
            }
        ]
    }
}

渲染面板的选择 ​

每次选择更改都会在上重新调用title和render,并使用当前选择的内容:

选择论证
一个节点/一条边Node 或 Edge
多个节点/边a Node[] / Edge[]
未选择任何内容null

当选择某些内容时,面板仅显示显示,除非它设置alwaysVisible: true - 但反应式面板会以任何方式重新渲染,因此它永远不会保存描述过时选择的内容。

为不描述选择且构建成本昂贵的面板设置 reactive: false:然后它渲染一次,并且只有显式 refreshPanel() 才能重建它。

在运行时注册面板 ​

UI.extraPanels 是同一注册表的声明形式。您可以在那里声明的任何内容都可以在图表生命周期中的任何时刻通过 UIManager 进行注册和删除:

ts
const graph = new Pivotick(container, data, options)

// Register a panel that depends on the graph itself. Returns a disposer.
const dispose = graph.UIManager.addPanel({
    id: 'unlinked-items',
    title: 'Unlinked items',
    alwaysVisible: true,
    order: -1,                              // sorts above the option-declared panels
    render: (selection) => tray.render(selection),
})

// Your own data changed (a save landed) rather than the selection —
// ask for a re-render instead of hand-patching the panel's DOM.
graph.UIManager.refreshPanel('unlinked-items')

// Gone for good (equivalent to calling `dispose()`):
graph.UIManager.removePanel('unlinked-items')
方法
addPanel(panel)注册面板;返回一个处理器。省略时自动生成id。在图表准备好之前和之后工作,并且来自插件的install(如ctx.addPanel)。
removePanel(id)删除面板及其 DOM。
refreshPanel(id?)重新渲染一个面板或全部面板。也刷新 reactive: false 面板。
getPanels()已注册的面板,按显示顺序排列。

面板按 order(升序,默认 0)排序,并将注册顺序保持在相同值内,因此 UI.extraPanels 从上到下读取,运行时面板附加在它们后面。

面板自己的 title / render 接收第二个参数 - 本身的句柄 - 因此它可以驱动自己的更新而无需到达图表:

ts
render: (selection, panel) => {
    const button = document.createElement('button')
    button.textContent = 'Reload'
    button.onclick = () => panel.refresh()   // …or panel.remove()
    return button
}

仅侧边栏

面板由侧边栏显示,侧边栏存在于 full 模式下。在任何模式下注册都会成功——面板根本没有地方可以渲染,直到侧边栏渲染为止。

异步填充面板 ​

title 和 render 都可以是 async,属性面板的 render / nodePropertiesMap / edgePropertiesMap 和主标题的 render 也可以。当承诺悬而未决时,面板会显示一个占位符:

ts
graph.UIManager.addPanel({
    id: 'sightings',
    title: 'Sightings',
    render: async (selection, panel, { signal }) => {
        if (!(selection instanceof Node)) return 'Select a node'
        const res = await fetch(`/sightings/${selection.id}`, { signal })
        return renderSightings(await res.json())
    },
})

请注意参数顺序:面板句柄保持第二位,并且 RenderContext 始终为 last。

由于面板会在每次选择更改时重新呈现,因此在解析之前通常会取代获取。该库会为您处理这个问题:旧渲染的 signal 被中止,并且选择移动后到达的结果将被丢弃,而不是绘制到描述不同节点的面板中。这同样适用于refreshPanel()以及移除面板。完整合约请参见异步内容。