Aller au contenu

Options de la barre latérale ​

La barre latérale peut être réduite par défaut en fonction de la taille de l'écran ou des préférences de l'utilisateur.

Détermine si la barre latérale est réduite par défaut.

  • 'auto' par défaut : maintient la barre latérale ouverte sauf s'il n'y a pas assez d'espace sur l'écran, auquel cas elle se réduit automatiquement.
  • true La barre latérale commence à être réduite.
  • false La barre latérale commence à être développée.

La barre latérale affiche des informations contextuelles sur les éléments du graphique. Il comporte trois composants personnalisables :

UI.sidebar.enabled: false supprime la colonne entière et restitue sa largeur au canevas. Les panneaux de propriétés et de voisins ont chacun un commutateur — UI.propertiesPanel.enabled, UI.neighborsPanel.enabled — qui supprime ce panneau et son séparateur tandis que le reste de la barre latérale reste. Voir Désactivation des fonctionnalités.

Cliquez pour voir le code
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,
    },
},

En-tête principal interface ​

Affiche un résumé concis du nœud ou du bord sélectionné, tel que le titre et le sous-titre. Il aide les utilisateurs à identifier rapidement l'élément actuellement sélectionné.

Le panneau d'en-tête principal peut être personnalisé via des fonctions de mappage. Le mappage par défaut pour un nœud est

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

Vous pouvez modifier ce mappage en remplaçant les éléments dont vous avez besoin :

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

avertissement

Lorsque render() est fourni, Pivotick ignore toute la logique de mappage par défaut, à moins qu'il ne renvoie undefined. Un résultat undefined (ou une promesse se résolvant à undefined) dessine l'en-tête par défaut pour cette sélection : le titre nodeHeaderMap / edgeHeaderMap pour un élément, le nombre de sélections pour plusieurs et le total du canevas lorsque rien n'est sélectionné. Renvoyez null pour laisser l'en-tête vide.

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

Panneau de propriétés interface ​

Affiche les propriétés détaillées du nœud ou du bord sélectionné dans la barre latérale. Chaque propriété a un nom et une valeur qui peuvent être statiques ou calculées dynamiquement.

Le comportement par défaut est d'afficher toutes les paires clé/valeur du getData() du nœud ou du bord.

Vous pouvez personnaliser les propriétés affichées et la manière dont elles sont rendues à l'aide des fonctions de mappage (nodePropertiesMap et edgePropertiesMap) ou d'un moteur de rendu entièrement personnalisé.

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

Une carte de propriétés peut renvoyer plusieurs entrées avec le même nom, par exemple une entrée Tag par balise. Dans une sélection multiple, chaque valeur compte ensuite les nœuds qui la portent, conserve/exclue les correspondances avec n'importe quelle entrée d'un nœud, et chaque valeur obtient sa propre barre, car les valeurs se chevauchent.

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

conseil

Lorsque render() est fourni, Pivotick ignore toute la logique de mappage par défaut, à moins qu'il ne renvoie undefined. Un résultat undefined (ou une promesse se résolvant à undefined) dessine le panneau par défaut pour cette sélection : les lignes nodePropertiesMap / edgePropertiesMap pour un élément, et la table agrégée, avec ses filtres conserver/exclure, pour plusieurs. Retournez null pour laisser le panneau vide.

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

Panneau des voisins interface ​

Affiche les connexions du nœud sélectionné : un petit graphique de ses voisins, les statistiques de connexion et une liste.

Le graphique voisin dessine par défaut avec les options de rendu du graphique principal. Les styles qui conviennent à un canevas complet, tels que les niveaux de détails de nœuds ou une carte de focus, peuvent encombrer un panneau de cette taille, donc graph prend ses propres remplacements :

  • graph.render est fusionné sur les options de rendu du graphique principal. Les propres paramètres du panneau (pas de déplacement, pas de zoom, pas d'expansion de nœud) s'appliquent toujours. Un maxZoom ici limite également le degré de zoom du graphique pour l'ajuster, de sorte qu'un nœud avec un voisin n'explose pas.
  • graph.layout est fusionné sur la disposition radiale du panneau. La racine est toujours le nœud sélectionné.
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 },
            },
        },
    },
}

Panneaux supplémentaires interface ​

Permet d'ajouter des panneaux entièrement personnalisés avec du contenu dynamique ou statique. Ces panneaux sont idéaux pour afficher des informations contextuelles supplémentaires, des contrôles personnalisés ou des widgets interactifs liés à l'élément sélectionné.

Chaque panneau supplémentaire a un title et un render() facultatifs, qui peuvent tous deux être statiques (string/HTMLElement) ou fonction de la sélection actuelle. Un string renvoyé s'affiche sous la forme text ; renvoyez un HTMLElement pour afficher votre propre balisage. Retournez null pour laisser le corps du panneau vide.

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 sélection avec laquelle un panneau est rendu ​

title et render sont réinvoqués à chaque changement de sélection, avec ce qui est actuellement sélectionné :

SélectionArgumentation
Un nœud / un bordle Node ou le Edge
Plusieurs nœuds/arêtesa Node[] / Edge[]
Rien de sélectionnénull

Un panneau n'est affiché que affiché lorsque quelque chose est sélectionné, à moins qu'il ne définisse alwaysVisible: true — mais un panneau réactif est restitué dans les deux cas, il ne contient donc jamais de contenu décrivant une sélection obsolète.

Définissez reactive: false pour un panneau qui ne décrit pas la sélection et qui est coûteux à construire : il est ensuite rendu une fois, et seul un refreshPanel() explicite le reconstruit.

Enregistrement des panneaux au moment de l'exécution ​

UI.extraPanels est la forme déclarative du même registre. Tout ce que vous pouvez y déclarer, vous pouvez également l'enregistrer - et le supprimer - à tout moment de la vie du graphique, via le 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éthode
addPanel(panel)Enregistrez un panel ; renvoie un broyeur. id est généré automatiquement lorsqu'il est omis. Fonctionne avant et après que le graphique soit prêt, et à partir du install d'un plugin (comme ctx.addPanel).
removePanel(id)Retirez le panneau et son DOM.
refreshPanel(id?)Rendre un panneau, ou tous. Actualise également les panneaux reactive: false.
getPanels()Les panels inscrits, par ordre d'affichage.

Les panneaux sont triés par order (croissant, par défaut 0) et conservent l'ordre d'enregistrement dans la même valeur, donc UI.extraPanels lit de haut en bas et les panneaux d'exécution s'ajoutent après eux.

Le title / render d'un panneau reçoit un deuxième argument - une poignée sur lui-même - afin qu'il puisse piloter ses propres mises à jour sans accéder au graphique :

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

Barre latérale uniquement

Les panneaux sont affichés par la barre latérale, qui existe en mode full. L'enregistrement réussit dans n'importe quel mode - le panneau n'a tout simplement nulle part où afficher jusqu'à ce qu'une barre latérale le fasse.

Remplir un panneau de manière asynchrone ​

title et render peuvent tous deux être async, tout comme le render / nodePropertiesMap / edgePropertiesMap du panneau de propriétés et le render de l'en-tête principal. Le panneau affiche un espace réservé pendant que la promesse est en attente :

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

Notez l'ordre des arguments : la poignée du panneau reste deuxième et le RenderContext est toujours dernier.

Étant donné qu'un panneau est restitué à chaque changement de sélection, une récupération est systématiquement remplacée avant d'être résolue. La bibliothèque gère cela pour vous : le signal de l'ancien rendu est abandonné et un résultat arrivant après le déplacement de la sélection est supprimé plutôt que peint dans un panneau décrivant un nœud différent. La même chose s'applique à refreshPanel() et au retrait du panneau. Voir contenu asynchrone pour le contrat complet.