Pular para o conteúdo

Opções da interface ​

Pivotick fornece uma camada de UI flexível na parte superior do seu gráfico, permitindo controlar como os usuários interagem com nós, bordas e tela. Usando as opções UI, você pode:

  • Configure o mode geral da UI (completo, visualizador, estático, etc.).
  • Personalize barra lateral e painéis para mostrar propriedades ou informações extras.
  • Defina tooltips para nós e arestas, com renderizadores personalizados opcionais.
  • Configure menus de contexto para nós, arestas e tela.
  • Declare as facetas do painel filter ou deixe que elas sejam derivadas de seus dados.
  • Encaixe um legend na tela — uma chave para suas cores que funciona como um filtro e que aparece sozinha quando suas cores são explicadas por um tipo de nó declarado.
  • Preencha qualquer uma dessas superfícies de forma assíncrona, a partir dos dados que você busca sob demanda.
  • Desligue qualquer recurso que sua integração não tenha utilidade.
  • Coloque o texto da própria biblioteca no idioma dos seus usuários: consulte Traduzindo a UI.

Modo UI ​

A opção mode controla o comportamento geral e o nível de interação da IU do gráfico.

  • full padrão: UI completa com todos os painéis, menus e interações habilitados.
  • light: UI mínima com interações essenciais habilitadas.
  • viewer: Permite apenas navegar no gráfico (panorâmica, zoom, arrastar) sem exibir nenhum painel da UI.
  • static: Gráfico estático, sem painéis de UI ou interações; o gráfico é somente leitura.

Para uso de estilo de imagem de Pivotick, use o seguinte:

ts
const container = document.getElementById('graph-container')
const options = {
    UI: {  
        mode: 'viewer'
    },
}
const graph = Pivotick(container, options)

Outro componente de UI pode ser configurado através de seu respectivo namespace

ts
const options = {
    UI: { 
        sidebar: {},
        mainHeader: {},
        propertiesPanel: {},
        extraPanels: [],
        tooltip: {},
        contextMenu: {},
        filter: {},
    }
}

A superfície de controle do modo full ​

No modo full, o cromo é um layout orientado por modo: um mode rail esquerdo (Selecionar / Criar / Visualizar / Física), um painel de ferramentas contextuais para o modo de ponteiro ativo, dois submenus de configurações - View (fundo de tela e interruptores) e Física (layout + simulação) — a barra superior (pesquisa, filtro, notas, desfazer/refazer), a seleção barra lateral (propriedades, facetas, vizinhos e uma linha de ação em massa) e uma janela de visualização ** do lado direito rail** (ajuste e centralização, zoom, configurações, tela cheia) e um minimapa no canto inferior direito livre, que se dobra quando a tela é pequena demais para poupar espaço (UI.minimap: false para soltá-la). O modo light elimina a barra lateral e o minimapa; viewer mantém apenas o trilho da viewport; static é uma tela vazia.

Migrando do canto cromado

As sobreposições flutuantes GraphControls e GraphToolbar foram removidas. Layout e física foram movidos para o submenu Física (UIManager.physicsFlyout) — eles foram enviados para o submenu Visualizar (UIManager.viewFlyout) na versão 1.5.0, que agora contém o plano de fundo da tela e as opções de grade. Selecione/crie ferramentas ao vivo no painel de ferramentas (UIManager.toolPanel) e no modo ferroviário (UIManager.modeRail). A opção UI.selectionMenu desapareceu — use contextMenu para ações por nó e a linha de ação em massa da barra lateral para ações de seleção múltipla. UIManager.graphNaviation foi renomeado para graphNavigation. A opção UI.modeRail e seus slots Explore / Enrich desativados foram removidos — em vez disso, registre um modo real com addRailMode. Veja o CHANGELOG.

Os quatro modos do rail estão integrados, mas não os únicos que ele pode conter: um plugin adiciona o seu próprio com addRailMode, que é como um modo Explorar ou Enriquecer que sabe o que seus significa é construído.

Desativando recursos ​

O modo UI decide quanto cromo um gráfico recebe. Cada recurso dentro desse Chrome também pode ser desativado um de cada vez, e é assim que uma integração descarta aquilo que seu back-end ou seus usuários não têm utilidade.

Um recurso desativado leva tudo que chega até ele: o botão, o painel, a entrada do menu de contexto e o atalho do teclado, todos juntos, então nada é deixado para trás para clicar ou pressionar e recusar silenciosamente.

ts
const options = {
    UI: {
        mode: 'full',
        notes: { enabled: false },        
        history: { enabled: false },
        inspector: { enabled: false },
        editors: { deletion: { enabled: false } },
    },
}

Os interruptores ​

OpçãoO que vem com isso
UI.topBar.enabledA faixa superior, a altura reservada, os atalhos que seus controles possuem (Shift+J, Shift+K, Shift+N, Mod+Z) e quaisquer ações de host. Use os interruptores abaixo para largar um comprimido e guardar o resto.
UI.search.enabledA pílula Search, seu seletor de nós e Shift+J.
UI.filter.enabledA pílula Filter Graph, seu painel e Shift+K. graph.queryEngine ainda filtra o código.
UI.notes.enabledA pílula e painel Notas, Shift+N, a ferramenta Adicionar nota, Adicionar nota do menu de tela, N, o menu de contexto da nota e noteManager.addNote, que recusa.
UI.history.enabledOs botões desfazer/refazer, o menu suspenso do histórico e Mod+Z/Mod+Shift+Z. graph.history continua gravando.
UI.inspector.enabledA entrada Inspecionar Propriedades do menu do nó e I.
UI.notifications.enabledBrindes. graph.notifier.* torna-se um undefined de retorno autônomo.
UI.sidebar.enabledToda a coluna lateral e a largura que ela ocupava.
UI.propertiesPanel.enabledO painel de propriedades da barra lateral e seu separador.
UI.neighborsPanel.enabledO painel de vizinhos da barra lateral e seu separador.
UI.viewFlyout.enabledO botão Exibir trilho e seu painel.
UI.physicsFlyout.enabledO botão do trilho Física e seu painel.
UI.tooltip.enabledDicas de ferramentas suspensas, incluídas as fixadas. Também leva { nodes, edges, groups } ou (element, kind) => boolean, veja Tooltips.
UI.contextMenu.enabledCada menu de contexto.
UI.navigation.enabledO trilho da janela de visualização: ajuste, zoom e tela cheia.
UI.legend: falseA legenda da tela, incluindo aquela que ela derivaria por si só.
UI.minimap: falseO minimapa.
UI.emptyState: falseO cartão mostra uma tela vazia.
UI.table: falseO data dock e seu Shift+T.
UI.pivotMode: falseO modo trilho Pivot, qualquer que esteja registrado.

Os recursos de caminho de gravação têm a mesma opção em UI.editors, portanto, uma integração somente leitura remove a capacidade em vez de vetar cada clique: nodeCreator, edgeCreator, nodeEditor, edgeEditor e deletion.

O comportamento do renderizador é desativado em render: zoomEnabled (que também usa os botões de zoom do trilho, mantendo o ajuste e o centro), dragEnabled, selectionBox.enabled (a marca ** e ** a ferramenta Selecionar ▸ Laço), enableNodeExpansion (a divisa, seu Enter e o tracejado contorno recolhido), enableFocusMode e interactionEnabled para tudo de uma vez. simulation.enabled interrompe a execução do layout.

O modo Criar segue suas ferramentas

O modo Criar trilho contém exatamente quatro ferramentas: Adicionar nó, Adicionar aresta, Adicionar nota e Editar nó. Desligue todos os quatro e o modo sai do trilho, junto com seu atalho C, em vez de abrir em um painel vazio. Selecione sempre permanece.

Suas próprias ações na barra superior ​

Uma ação em toda a tela, como salvá-la, pertence à barra superior ao lado do grupo desfazer-refazer. UI.topBar.actions declara pílulas desenhadas no mesmo estilo de Pesquisa, Filtro e Notas, portanto, um tema que reestiliza essas reestilizações também.

ts
const options = {
    UI: {
        topBar: {
            actions: [{ 
                id: 'save-graph',
                text: () => storedId ? 'Update graph' : 'Save as graph',
                async onclick(_event, ctx) {
                    if (storedId) return updateGraph(storedId, ctx.graph)
                    const values = await ctx.promptData({
                        fields: [{ key: 'name', label: 'Name', type: 'text' }],
                    })
                    if (values) storedId = await createGraph(values.name, ctx.graph)
                },
                menu: () => storedId
                    ? [{ text: 'Save as new graph…', onclick: saveAsNew }]
                    : [],
            }],
        },
    },
}
  • Onde. placement: 'end' (o padrão) coloca a pílula logo antes do grupo desfazer-refazer; 'start' coloca depois dos comprimidos embutidos à esquerda. Os comprimidos mantêm a ordem em que foram declarados.
  • O que diz. text, title, visible e enabled assumem um valor ou função do gráfico. As funções são lidas novamente após cada alteração de dados, em graph.UIManager.refreshTopBar(), e assim que uma promessa retornada por onclick for concluída. Enquanto essa promessa estiver fora, a pílula estará desativada, portanto, um salvamento lento não pode ser iniciado duas vezes.
  • O que obtém. onclick(event, ctx) recebe o gráfico e dois auxiliares que abrem o modal da biblioteca: ctx.promptData({ fields }) resolve para os valores ou null no cancelamento, e ctx.confirm({ body }) resolve true apenas no botão confirmar. ctx.refresh() relê cada ação.
  • Um sinal de intercalação. Com menu, o comprimido se torna um botão de divisão: o rótulo executa onclick e um sinal de intercalação abre as linhas, no mesmo formato de linha dos menus de contexto (sem submenus). Um formulário de função é entregue da mesma forma ctx e é chamado a cada atualização e a cada abertura; embora não retorne nenhuma linha, a pílula não tem sinal de intercalação. Escape ou clique em outro lugar fecha as linhas.
  • Um atalho. shortcut desenha um distintivo na pílula. É apenas uma etiqueta: registe você mesmo a encadernação.

Um plugin adiciona um comprimido com ctx.addTopBarAction(action), que retorna um triturador (veja Plugins). As pílulas seguem a faixa: nenhuma no modo viewer ou static e nenhuma no modo UI.topBar.enabled: false.

Uma tela vazia ​

Um gráfico sem nó e sem nota mostra um cartão no meio da tela, portanto, uma fonte sem nada para desenhar não é lida como um gráfico que falhou ao carregar. Diz "Nada na tela ainda", a menos que você dê algo mais a dizer:

ts
const options = {
    UI: {
        emptyState: { 
            render: ({ initial, graph }) => {
                const box = document.createElement('div')
                box.textContent = initial ? 'Nothing is related to this record.' : 'The canvas is empty.'
                const button = document.createElement('button')
                button.textContent = 'Find related'
                button.onclick = () => graph.UIManager.openPivotMode()
                box.append(button)
                return box
            },
        },
    },
}

O cartão segue os dados. Adicionar um nó ou uma nota tira-o, e remover o último deles, manualmente ou desfazendo o ingest que os trouxe, traz-o de volta. Os nós que os filtros ocultam ainda contam: o gráfico é reduzido, não vazio.

render é executado cada vez que o cartão aparece, não em todas as alterações enquanto ele está ativo. initial é true até que o gráfico tenha mantido um nó, então um analista que esvaziou uma tela completa pode receber algo diferente do que uma fonte vazia é informada. Uma string retornada é renderizada como texto simples e render pode ser async como os ganchos abaixo.

O cartão usa superfície e borda cromadas, portanto segue o tema. Ele permite a passagem dos gestos da tela, de modo que o movimento panorâmico, o zoom e o menu da tela funcionam nela, exceto no conteúdo interativo: um botão, um link, um controle de formulário ou qualquer coisa com tabindex recebe seus próprios cliques. UI.emptyState: false não mostra absolutamente nada.

Conteúdo assíncrono ​

Cada gancho de conteúdo fornecido pelo consumidor pode retornar um Promise em vez do conteúdo em si. Isso cobre mainHeader.render, propertiesPanel.render / nodePropertiesMap / edgePropertiesMap, neighborsPanel.render, um painel extra title e render, a dica de ferramenta render, renderNodeExtra, renderEdgeExtra e mapas de propriedades, e a tela vazia render.

Isso existe para o caso em que os dados de um nó são uma referência para um registro em vez do registro em si, e o conteúdo interessante está por trás de uma chamada HTTP:

ts
const options = {
    UI: {
        tooltip: {
            renderNodeExtra: async (node, { signal }) => { 
                const res = await fetch(`/enrich/${node.getData().uuid}`, { signal })
                return renderChips(await res.json())       // an HTMLElement
            },
        },
    },
}

Adicione async, encaminhe signal e a biblioteca cuida do resto:

Espaço reservadoEnquanto a promessa está pendente, um esqueleto temático ocupa o espaço.
TrocarNa resolução, o conteúdo substitui o espaço reservado no lugar.
EstabilidadeUm resultado que chega depois que seu slot acabou - você passou o mouse sobre outro nó, alterou a seleção, atualizou o painel - é dropado. Somente a renderização mais recente de uma superfície pode ser confirmada, independentemente da ordem em que for resolvida.
Cancelamentoctx.signal é abortado quando uma renderização é substituída ou o gráfico é destruído, então um fetch encaminhado é cancelado em vez de vazar.
FalhaUma promessa rejeitada renderiza uma linha de erro compacta em vez de deixar um botão giratório para sempre. Uma renderização abortada não é uma falha e não mostra nada.

O contexto de renderização ​

Cada gancho de conteúdo recebe um RenderContext como seu argumento last. Os ganchos síncronos existentes simplesmente o ignoram.

ts
render: async (element, { signal, isStale }) => {
    const rows = await fetchRows(element, signal)
    if (isStale()) return ''            // cheap guard for non-abortable work
    return renderRows(rows)
}

Personalizando o espaço reservado ​

UI.asyncContent substitui o que é mostrado enquanto pendente e em caso de falha. Ambos aceitam uma string, um HTMLElement ou uma função da superfície:

ts
const options = {
    UI: {
        asyncContent: { 
            placeholder: (surface) => surface === 'tooltip' ? 'Loading…' : mySkeleton(),
            error: 'Could not load',
        },
    },
}

surface é um de 'tooltip', 'properties', 'neighbors', 'mainHeader', 'extraPanel' ou 'emptyState'.

Ganchos síncronos permanecem intactos

O retorno direto do conteúdo se comporta exatamente como sempre: mesma chamada, mesmo quadro, sem espaço reservado e sem elemento wrapper. Nada muda em uma integração existente.

O que não pode ser assíncrono

Dois grupos permanecem sincronizados propositalmente:

  • HeaderMapEntry.title / subtitle alimenta o slot de título de ajuste automático * e* pesquisa de nó (resolveNodeByName), ambos os quais precisam do texto agora. Use um mainHeader.render assíncrono se precisar de detalhes buscados no cabeçalho.
  • PropertyEntry.name / value — em vez disso, busca as linhas: um nodePropertiesMap assíncrono resolve uma vez e depois devolve entradas simples, em vez de colocar um controle giratório em cada célula.

Os ganchos do renderizador por quadro (render.renderNode, renderLabel, renderCluster e NodeStyle.html) também são somente de sincronização: eles são executados dentro do caminho de desenho, por nó, por tick. Se você precisar do nó assíncrono decoration, busque os dados do nó e deixe o renderizador síncrono lê-los.