Pular para o conteúdo

Opções da barra lateral ​

A barra lateral pode ser recolhida por padrão, dependendo do tamanho da tela ou da preferência do usuário.

Determina se a barra lateral está recolhida por padrão.

  • 'auto' padrão: Mantém a barra lateral aberta, a menos que não haja espaço suficiente na tela, caso em que ela é recolhida automaticamente.
  • true A barra lateral começa recolhida.
  • false A barra lateral começa expandida.

A barra lateral exibe informações contextuais para elementos gráficos. Possui três componentes personalizáveis:

UI.sidebar.enabled: false descarta a coluna inteira e devolve sua largura para a tela. Os painéis de propriedades e vizinhos têm uma opção cada - UI.propertiesPanel.enabled, UI.neighborsPanel.enabled - que remove esse painel e seu separador enquanto o restante da barra lateral permanece. Consulte Desativando recursos.

Clique para ver o código
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,
    },
},

Cabeçalho Principal interface ​

Mostra um resumo conciso do nó ou aresta selecionado, como título e subtítulo. Ajuda os usuários a identificar rapidamente o elemento atualmente selecionado.

O painel principal do cabeçalho pode ser personalizado por meio de funções de mapeamento. O mapeamento padrão para um nó é

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

Você pode alterar esse mapeamento substituindo as partes necessárias:

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

aviso

Quando render() é fornecido, Pivotick ignora toda a lógica de mapeamento padrão, a menos que retorne undefined. Um resultado undefined (ou uma promessa resolvida para undefined) desenha o cabeçalho padrão para essa seleção: o título nodeHeaderMap / edgeHeaderMap para um elemento, a contagem de seleção para vários e o total da tela quando nada é selecionado. Retorne null para deixar o cabeçalho vazio.

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

Painel de Propriedades interface ​

Exibe propriedades detalhadas do nó ou aresta selecionado na barra lateral. Cada propriedade possui um nome e um valor que podem ser estáticos ou calculados dinamicamente.

O comportamento padrão é mostrar todos os pares chave/valor do nó ou borda getData().

Você pode personalizar quais propriedades são exibidas e como elas são renderizadas usando funções de mapeamento (nodePropertiesMap e edgePropertiesMap) ou um renderizador totalmente personalizado.

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 }))
            }
        }
    }
}

Um mapa de propriedades pode retornar diversas entradas com o mesmo nome, como uma entrada Tag por tag. Em uma seleção múltipla, cada valor conta os nós que o transportam, manter/excluir corresponde a qualquer uma das entradas de um nó e cada valor obtém sua própria barra, uma vez que os valores se sobrepõem.

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

dica

Quando render() é fornecido, Pivotick ignora toda a lógica de mapeamento padrão, a menos que retorne undefined. Um resultado undefined (ou uma promessa resolvendo para undefined) desenha o painel padrão para essa seleção: as linhas nodePropertiesMap / edgePropertiesMap para um elemento, e a tabela agregada, com seus filtros manter/excluir, para vários. Retorne null para deixar o painel vazio.

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

Painel de Vizinhos interface ​

Mostra as conexões do nó selecionado: um pequeno gráfico de seus vizinhos, estatísticas de conexão e uma lista.

O gráfico vizinho desenha com as opções de renderização do gráfico principal por padrão. Estilos adequados para uma tela completa, como camadas de detalhes de nós ou um cartão de foco, podem ocupar um painel desse tamanho, então graph assume suas próprias substituições:

  • graph.render é mesclado nas opções de renderização do gráfico principal. As próprias configurações do painel (sem arrastar, sem zoom, sem expansão de nós) ainda se aplicam. Um maxZoom aqui também limita o quanto o gráfico aumenta para caber, para que um nó com um vizinho não seja ampliado.
  • graph.layout é mesclado no layout radial do painel. A raiz é sempre o nó selecionado.
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 },
            },
        },
    },
}

Painéis Extras interface ​

Permite adicionar painéis totalmente customizados com conteúdo dinâmico ou estático. Esses painéis são ideais para mostrar informações contextuais adicionais, controles personalizados ou widgets interativos relacionados ao elemento selecionado.

Cada painel extra possui um title e um render() opcionais, ambos podendo ser estáticos (string/HTMLElement) ou uma função da seleção atual. Um string retornado é renderizado como text; retorne um HTMLElement para renderizar sua própria marcação. Retorne null para deixar o corpo do painel vazio.

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
                }
            }
        ]
    }
}

A seleção com que um painel é renderizado ​

title e render são invocados novamente em cada mudança de seleção, com o que está atualmente selecionado:

SeleçãoArgumento
Um nó/uma arestao Node ou Edge
Vários nós/arestasa Node[] / Edge[]
Nada selecionadonull

Um painel é apenas mostrado enquanto algo está selecionado, a menos que defina alwaysVisible: true — mas um painel reativo é renderizado novamente de qualquer maneira, portanto nunca contém conteúdo que descreve uma seleção obsoleta.

Defina reactive: false para um painel que não descreve a seleção e é caro para construir: ele então é renderizado uma vez e apenas um refreshPanel() explícito o reconstrói.

Registrando painéis em tempo de execução ​

UI.extraPanels é a forma declarativa do mesmo registro. Qualquer coisa que você declarar lá também poderá registrar — e remover — a qualquer momento da vida do gráfico, através do 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')
Método
addPanel(panel)Cadastrar um painel; retorna um triturador. id é gerado automaticamente quando omitido. Funciona antes e depois do gráfico estar pronto e a partir de um plugin install (como ctx.addPanel).
removePanel(id)Remova o painel e seu DOM.
refreshPanel(id?)Rerenderize um painel ou todos eles. Atualiza os painéis reactive: false também.
getPanels()Os painéis cadastrados, na ordem de exibição.

Os painéis são classificados por order (crescente, padrão 0) e mantêm a ordem de registro dentro do mesmo valor, então UI.extraPanels lê de cima para baixo e os painéis de tempo de execução são anexados após eles.

O próprio title / render de um painel recebe um segundo argumento - um identificador para si mesmo - para que possa conduzir suas próprias atualizações sem alcançar o gráfico:

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

Somente barra lateral

Os painéis são mostrados pela barra lateral, que existe no modo full. O registro é bem-sucedido em qualquer modo – o painel simplesmente não tem onde renderizar até que uma barra lateral o faça.

Preenchendo um painel de forma assíncrona ​

title e render podem ser async, assim como render / nodePropertiesMap / edgePropertiesMap do painel de propriedades e render do cabeçalho principal. O painel mostra um espaço reservado enquanto a promessa está pendente:

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())
    },
})

Observe a ordem dos argumentos: o identificador do painel permanece em segundo lugar e RenderContext é sempre last.

Como um painel é renderizado novamente a cada alteração na seleção, uma busca é rotineiramente substituída antes de ser resolvida. A biblioteca cuida disso para você: o signal da renderização antiga é abortado e um resultado que chega após a seleção ser movida é descartado em vez de pintado em um painel que descreve um nó diferente. O mesmo se aplica a refreshPanel() e à remoção do painel. Consulte conteúdo assíncrono para o contrato completo.