Ir al contenido

Opciones de la barra lateral ​

La barra lateral se puede contraer de forma predeterminada según el tamaño de la pantalla o las preferencias del usuario.

Determina si la barra lateral está contraída de forma predeterminada.

  • 'auto' predeterminado: Mantiene la barra lateral abierta a menos que no haya suficiente espacio en la pantalla, en cuyo caso se contrae automáticamente.
  • true La barra lateral comienza a colapsarse.
  • false La barra lateral comienza a expandirse.

La barra lateral muestra información contextual de los elementos del gráfico. Tiene tres componentes personalizables:

UI.sidebar.enabled: false deja caer toda la columna y devuelve su ancho al lienzo. Los paneles de propiedades y vecinos tienen un interruptor cada uno: UI.propertiesPanel.enabled, UI.neighborsPanel.enabled - que elimina ese panel y su separador mientras que el resto de la barra lateral permanece. Consulte Desactivación de funciones.

Haga clic para ver el 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,
    },
},

Encabezado principal interfaz ​

Muestra un resumen conciso del nodo o borde seleccionado, como título y subtítulo. Ayuda a los usuarios a identificar rápidamente el elemento seleccionado actualmente.

El panel de encabezado principal se puede personalizar mediante funciones de mapeo. La asignación predeterminada para un nodo es

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

Puede cambiar este mapeo anulando las partes que necesita:

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

advertencia

Cuando se proporciona render(), Pivotick omite toda la lógica de mapeo predeterminada, a menos que devuelva undefined. Un resultado undefined (o una promesa que se resuelve en undefined) dibuja el encabezado predeterminado para esa selección: el título nodeHeaderMap / edgeHeaderMap para un elemento, el recuento de selección para varios y el total del lienzo cuando no se selecciona nada. Regrese null para dejar el encabezado vacío.

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

Panel de propiedades interfaz ​

Muestra propiedades detalladas del nodo o borde seleccionado en la barra lateral. Cada propiedad tiene un nombre y un valor que pueden ser estáticos o calcularse dinámicamente.

El comportamiento predeterminado es mostrar todos los pares clave/valor del nodo o borde getData().

Puede personalizar qué propiedades se muestran y cómo se representan utilizando funciones de mapeo (nodePropertiesMap y edgePropertiesMap) o un renderizador personalizado completo.

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

Un mapa de propiedades puede devolver varias entradas con el mismo nombre, como una entrada Tag por etiqueta. En una selección múltiple, cada valor cuenta los nodos que lo transportan, mantener/excluir coincide con cualquiera de las entradas de un nodo y cada valor obtiene su propia barra, ya que los valores se superponen.

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

Consejo

Cuando se proporciona render(), Pivotick omite toda la lógica de mapeo predeterminada, a menos que devuelva undefined. Un resultado undefined (o una promesa que se resuelve en undefined) dibuja el panel predeterminado para esa selección: las filas nodePropertiesMap / edgePropertiesMap para un elemento y la tabla agregada, con sus filtros de mantener/excluir, para varios. Regrese null para dejar el panel vacío.

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

Panel de Vecinos interfaz ​

Muestra las conexiones del nodo seleccionado: un pequeño gráfico de sus vecinos, estadísticas de conexión y una lista.

El gráfico vecino se dibuja con las opciones de representación del gráfico principal de forma predeterminada. Los estilos que se adaptan a un lienzo completo, como niveles de detalles de nodos o una tarjeta de enfoque, pueden llenar un panel de este tamaño, por lo que graph realiza sus propias anulaciones:

  • graph.render se fusiona con las opciones de renderizado del gráfico principal. Se siguen aplicando las configuraciones propias del panel (sin arrastre, sin zoom, sin expansión de nodos). Un maxZoom aquí también limita hasta qué punto el gráfico se acerca para ajustarse, de modo que un nodo con un vecino no explote.
  • graph.layout se fusiona sobre el diseño radial del panel. La raíz es siempre el nodo seleccionado.
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 },
            },
        },
    },
}

Paneles adicionales interfaz ​

Permite agregar paneles totalmente personalizados con contenido dinámico o estático. Estos paneles son ideales para mostrar información contextual adicional, controles personalizados o widgets interactivos relacionados con el elemento seleccionado.

Cada panel adicional tiene un title y un render() opcionales, los cuales pueden ser estáticos (cadena/HTMLElement) o una función de la selección actual. Un string devuelto se representa como text; devuelve un HTMLElement para representar tu propio marcado. Regrese null para dejar el cuerpo del panel vacío.

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

La selección con la que se representa un panel. ​

title y render se vuelven a invocar en cada cambio de selección, con lo que está actualmente seleccionado:

SelecciónArgumento
Un nodo/un bordeel Node o Edge
Varios nodos/aristasa Node[] / Edge[]
Nada seleccionadonull

Un panel solo se se muestra mientras algo está seleccionado, a menos que establezca alwaysVisible: true, pero un panel reactivo se vuelve a representar de cualquier manera, por lo que nunca contiene contenido que describa una selección obsoleta.

Configure reactive: false para un panel que no describe la selección y es costoso de construir: luego se representa una vez y solo un refreshPanel() explícito lo reconstruye.

Registro de paneles en tiempo de ejecución ​

UI.extraPanels es la forma declarativa del mismo registro. Todo lo que puedas declarar allí también podrás registrarlo (y eliminarlo) en cualquier momento de la vida del gráfico, a través del 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)Registrar un panel; devuelve un triturador. id se genera automáticamente cuando se omite. Funciona antes y después de que el gráfico esté listo y desde el install de un complemento (como ctx.addPanel).
removePanel(id)Retire el panel y su DOM.
refreshPanel(id?)Vuelva a renderizar un panel o todos. También actualiza los paneles reactive: false.
getPanels()Los paneles registrados, en orden de visualización.

Los paneles se ordenan por order (ascendente, predeterminado 0) y mantienen el orden de registro dentro del mismo valor, por lo que UI.extraPanels lee de arriba a abajo y los paneles de ejecución se agregan después de ellos.

El title / render de un panel recibe un segundo argumento (un identificador sobre sí mismo) para que pueda generar sus propias actualizaciones sin tener que recurrir al gráfico:

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

Solo barra lateral

Los paneles se muestran en la barra lateral, que existe en el modo full. El registro se realiza correctamente en cualquier modo: el panel simplemente no tiene dónde renderizar hasta que lo tiene una barra lateral.

Llenar un panel de forma asincrónica ​

title y render pueden ser ambos async, al igual que el panel de propiedades render / nodePropertiesMap / edgePropertiesMap y el encabezado principal render. El panel muestra un marcador de posición mientras la promesa está pendiente:

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

Tenga en cuenta el orden de los argumentos: el identificador del panel permanece en segundo lugar y el RenderContext es siempre último.

Debido a que un panel se vuelve a representar con cada cambio de selección, rutinariamente se realiza una búsqueda. reemplazado antes de que se resuelva. La biblioteca se encarga de eso por usted: el viejo El renderizado signal se cancela y llega un resultado después de que la selección continúa se deja caer en lugar de pintarse en un panel que describe un nodo diferente. el Lo mismo se aplica a refreshPanel() y a quitar el panel. Ver contenido asincrónico para el contrato completo.