Aller au contenu
Pivotick
Main Navigation Accueil
Bien démarrer
Configuration
Référence de l’API
GalerieProjetsEssayer !

Français

English
简体中文
Deutsch
Português

Français

English
简体中文
Deutsch
Português

Apparence

Sidebar Navigation

Bien démarrer

Projets utilisant Pivotick

Configuration

Fonctions de rappel

Couches d’arêtes

Simplifier

Disposition

Rendu

Simulation

Personnaliser l’interface

Barre latérale

Infobulle

Menu contextuel

Filtres

Légende

Tableau de données

Style de l’interface

Traduire l’interface

Extensions

API Pivotick

Pivots et enrichissement

Enregistrer les résultats des pivots

Annulation et historique

Sécurité

Documentation de l’API

Galerie

Bases

Style des nœuds

Style des arêtes

Dispositions

Événements et rappels

Personnalisation de l’interface

Édition et création

Notes et annotations

Filtres, recherche et hiérarchie

Contrôle par programmation

Thèmes et performances

Pivots et enrichissement

Démonstrations complètes

Légende filtrable ​

Un graphique à code couleur a besoin d’une clé – et une fois qu’il en a une, la clé constitue le filtre le plus rapide de l’interface utilisateur. UI.legend en ancre un dans un coin du canevas : un échantillon, une étiquette et un nombre de nœuds par catégorie, et cliquer sur une ligne masque cette catégorie. Pointer sur une ligne éclaire d'abord ses nœuds sur le canevas et assombrit le reste, afin qu'une catégorie puisse être trouvée avant qu'elle ne soit désactivée.

Une ligne le configure : legend: { key: 'type' }. La bibliothèque collecte les valeurs distinctes de data.type, lit chaque échantillon à partir de la couleur que le moteur de rendu ** a déjà résolu ** et compte les nœuds derrière lui. Souvent, vous n'avez besoin d'aucune ligne — avec un render.nodeTypeAccessor déclaré, une légende apparaît d'elle-même une fois que cette dimension est affichée pour expliquer les couleurs (voir Légende) ; cette carte déclare le bloc pour afficher les boutons. La légende est purement descriptive — les couleurs ici proviennent d'un ColorPaletteMapper dans les options, exactement comme dans Couleur par catégorie, et la légende n'en attribue jamais. Changez la palette et la légende suit.

Cliquer sur une ligne filtre à travers graph.queryEngine, c'est donc le même qui filtre le panneau Filtres graphiques de l'en-tête. Cette carte déclare une facette type sous la propre clé de la légende, ce qui fait que les deux ne font qu'un seul contrôle : désactivez api dans la légende, ouvrez le panneau (entonnoir, ou Shift+K) et sa multisélection Type de service l'a déjà supprimé. resetFilters() — ou le Reset du panneau — rallume chaque ligne.

Les nœuds sans type n'obtiennent aucune ligne et la légende ne les cache jamais : elle ne peut agir que sur les catégories qu'elle répertorie.

Une clé représente rarement toute l’histoire. Deux touches à la fois empile une section intitulée par dimension dans la même carte ancrée — appuyez sur type + zone ci-dessous : type continue de piloter la facette du panneau, zone obtient sa propre section et les deux filtres et ensemble, se cachant ainsi api et dmz laissent ce qui n'est ni l'un ni l'autre. zone déclare ses propres couleurs d'échantillon, car le canevas code type dans ses couleurs et un échantillon échantillonné pour zone ne serait qu'une coïncidence.

Une section peut également saisir edges au lieu de nœuds — scope: 'edge' répertorie les types de relations du graphique avec un échantillon de ligne et masque un calque entier lorsque vous cliquez sur un. Cela nécessite des bords de différentes sortes, donc il vit sur la carte Couches de bord.

Dans un en-tête de section : afficher tout, inverser et un chevron qui plie cette section jusqu'à son titre — alt-clic le chevron pour plier chaque section. Survolez une ligne pour éclairer sa catégorie sur la toile ; alt-clic un pour afficher uniquement cette catégorie. Les bascules sont enregistrées sur la console via legendToggle, qui nomme sa section.

js
// import { ColorPaletteMapper } from 'pivotick'

// The consumer owns the colours — here a palette mapper over `type`, exactly as in
// the "Color by category" card. The legend never assigns a colour.
const palette = new ColorPaletteMapper('okabe-ito')

// `key` is all a legend needs: it collects the distinct values of `data.type`,
// reads each swatch out of the colour the renderer resolved, counts the nodes
// behind it, and makes every row a filter toggle.
//
// You can also leave `UI.legend` out entirely: with a `render.nodeTypeAccessor`
// declared, a legend appears by itself as soon as that dimension is shown to
// explain the colours. `UI.legend: false` opts out.
const legend = {
    key: 'type',
    title: 'Service type',
    // Every one of these is a default — spelled out here to show the knobs.
    position: 'bottom-left',
    showCounts: true,
    collapsible: true,
    filterable: true,
    maxVisibleEntries: 12
}
js
const options = {
    UI: {
        // The legend is chrome: `full` and `light` modes have it, `viewer` and
        // `static` don't. Full mode also ships the Graph Filters panel, which the
        // legend below shares a filter with.
        mode: 'full',
        // Off: `full` mode brings a minimap and the data dock, and this card is about
        // neither (see the Minimap and Data table cards for those).
        minimap: false,
        table: false,
        legend,
        // Declaring a facet under the *same key* the legend uses makes the two one
        // control: toggle a swatch and the panel's multiselect follows, and the
        // other way round. (The trade-off: an empty multiselect means "no
        // constraint" to the panel, so the legend won't let you switch off the last
        // remaining category.)
        filter: {
            facets: [
                {
                    key: 'type', label: 'Service type', type: 'multiselect',
                    options: (graph) => [...new Set(graph.getNodes().map((node) => node.getData().type))]
                        .sort().map((value) => ({ label: value, value }))
                }
            ]
        }
    },
    render: {
        defaultNodeStyle: {
            size: 15,
            strokeColor: '#ffffff',
            textColor: '#334155',
            text: (node) => node.getData()?.name,
            textVerticalShift: -1.8,
            color: (node) => palette.getColor(node.getData()?.type)
        }
    }
}
js
// The legend announces every toggle on the data bus, so the choice can be
// persisted, mirrored elsewhere, or logged.
function watchLegend(graph) {
    graph.on('legendToggle', ({ section, hidden, visible }) => {
        console.log(`legend [${section}]: showing ${visible.join(', ') || '(none)'} — hiding ${hidden.join(', ') || '(none)'}`)
    })
}

// Swap the legend at runtime — a graph that started without one gets it built on
// the spot, and `undefined` removes it (clearing its filter with it).
function legendByType(graph) {
    graph.setLegend({ key: 'type', title: 'Service type' })
}

function removeLegend(graph) {
    graph.setLegend(false)
}

// Declared entries, for when the categories aren't a plain data key: supply the
// label, the colour and the predicate yourself. An array works too; a function is
// re-resolved whenever the data changes.
function legendByTier(graph) {
    graph.setLegend({
        title: 'Tier',
        entries: [
            {
                id: 'edge', label: 'Edge', color: '#0072B2',
                predicate: (node) => node.getData().type === 'web'
            },
            {
                id: 'service', label: 'Services', color: '#E69F00',
                predicate: (node) => node.getData().type === 'api'
            },
            {
                id: 'storage', label: 'Storage', color: '#009E73',
                predicate: (node) => ['database', 'cache'].includes(node.getData().type)
            }
        ]
    })
}

// Two keys at once: `sections` stacks one titled block per dimension in a single
// docked card. Each section filters on its own, and the filters *and* together —
// hide `api` above and `dmz` below and what is left is neither.
function legendByTypeAndZone(graph) {
    graph.setLegend({
        // `position` belongs to the card, not to a section. Top-left, because a
        // two-section card is tall enough to reach the mode rail in the corner the
        // single-key legend uses.
        position: 'top-left',
        sections: [
            // Still adopts the declared `type` facet, so this section and the filter
            // panel remain one control.
            { key: 'type', title: 'Service type' },
            {
                key: 'zone',
                title: 'Network zone',
                // The colours encode `type`, not `zone`, and the legend can only
                // sample colours — so this section declares swatches of its own
                // rather than showing one that would be a coincidence. `key` still
                // supplies the predicate.
                entries: [
                    { id: 'dmz', label: 'DMZ', color: '#CC79A7' },
                    { id: 'internal', label: 'Internal', color: '#56B4E9' }
                ]
            }
        ]
    })
}
js
// A service graph whose `type` drives both the colours and the legend. The counts
// differ per type on purpose — the legend reports them. `zone` is a second
// dimension the colours say nothing about — see the stacked legend below.
const data = {
    nodes: [
        { id: 'web-1', data: { name: 'Web app', type: 'web', zone: 'dmz' } },
        { id: 'web-2', data: { name: 'Mobile web', type: 'web', zone: 'dmz' } },
        { id: 'api-1', data: { name: 'Auth API', type: 'api', zone: 'internal' } },
        { id: 'api-2', data: { name: 'Orders API', type: 'api', zone: 'internal' } },
        { id: 'api-3', data: { name: 'Billing API', type: 'api', zone: 'internal' } },
        { id: 'db-1', data: { name: 'Postgres', type: 'database', zone: 'internal' } },
        { id: 'db-2', data: { name: 'Replica', type: 'database', zone: 'internal' } },
        { id: 'cache-1', data: { name: 'Redis', type: 'cache', zone: 'internal' } }
    ],
    edges: [
        { from: 'web-1', to: 'api-1' },
        { from: 'web-1', to: 'api-2' },
        { from: 'web-2', to: 'api-1' },
        { from: 'api-2', to: 'api-3' },
        { from: 'api-1', to: 'cache-1' },
        { from: 'api-2', to: 'db-1' },
        { from: 'api-3', to: 'db-1' },
        { from: 'db-1', to: 'db-2' }
    ]
}
Pager
Page suivanteBien démarrer