Options de l’interface
Pivotick fournit une couche d'interface utilisateur flexible au-dessus de votre graphique, vous permettant de contrôler la façon dont les utilisateurs interagissent avec les nœuds, les bords et le canevas. En utilisant les options UI, vous pouvez :
- Configurez le mode global de l'interface utilisateur (complète, visionneuse, statique, etc.).
- Personnalisez sidebar et les panneaux pour afficher les propriétés ou des informations supplémentaires.
- Définissez tooltips pour les nœuds et les bords, avec des moteurs de rendu personnalisés facultatifs.
- Configurez les menus contextuels **** pour les nœuds, les bords et le canevas.
- Déclarez les facettes du panneau filter, ou laissez-les être dérivées de vos données.
- Ancrez un legend sur le canevas - une clé pour vos couleurs qui sert également de filtre et qui apparaît d'elle-même lorsque vos couleurs sont expliquées par un type de nœud déclaré.
- Remplissez n'importe laquelle de ces surfaces de manière asynchrone, à partir des données que vous récupérez à la demande.
- Désactivez toute fonctionnalité pour laquelle votre intégration n'a aucune utilité.
- Mettez le texte de la bibliothèque dans la langue de vos utilisateurs : voir Traduire l'interface utilisateur.
Mode interface utilisateur
L'option mode contrôle le comportement global et le niveau d'interaction de l'interface utilisateur du graphique.
fullpar défaut : interface utilisateur complète avec tous les panneaux, menus et interactions activés.light: interface utilisateur minimale avec les interactions essentielles activées.viewer: permet uniquement de naviguer dans le graphique (panoramique, zoom, glisser) sans afficher aucun panneau d'interface utilisateur.static: graphique statique, pas de panneaux d'interface utilisateur ni d'interactions ; le graphique est en lecture seule.
Pour une utilisation de style image de Pivotick, utilisez ce qui suit :
const container = document.getElementById('graph-container')
const options = {
UI: {
mode: 'viewer'
},
}
const graph = Pivotick(container, options)D'autres composants de l'interface utilisateur peuvent être configurés via leur espace de noms respectif
const options = {
UI: {
sidebar: {},
mainHeader: {},
propertiesPanel: {},
extraPanels: [],
tooltip: {},
contextMenu: {},
filter: {},
}
}La surface de contrôle en mode full
En mode full, le chrome est une mise en page pilotée par le mode : un rail de mode (Sélectionner/Créer/Afficher/Physique), un panneau d'outils contextuel pour le mode pointeur actif, deux menus volants de paramètres — Affichage (arrière-plan du canevas et commutateurs) et Physique (mise en page + simulation) — la barre supérieure (recherche, filtre, notes, annuler/rétablir), la sélection barre latérale (propriétés, facettes, voisins et ligne d'action groupée) et une fenêtre de droite rail (ajustement et centrage, zoom, paramètres, plein écran) et une mini-carte dans le coin libre en bas à droite, qui se replie lorsque la toile est trop petite pour épargner la pièce (UI.minimap: false pour la laisser tomber). Le mode light supprime la barre latérale et la mini-carte ; viewer conserve uniquement le rail de la fenêtre ; static est une toile nue.
Migration depuis le coin Chrome
Les superpositions flottantes GraphControls et GraphToolbar ont été supprimées. La mise en page et la physique ont été déplacées dans le menu volant Physique (UIManager.physicsFlyout) — elles ont été livrées dans le menu déroulant Vue (UIManager.viewFlyout) dans la version 1.5.0, qui contient désormais l'arrière-plan du canevas et les commutateurs de grille. Sélectionnez/Créez des outils en direct dans le panneau d'outils (UIManager.toolPanel) et le rail de mode (UIManager.modeRail). L'option UI.selectionMenu a disparu : utilisez contextMenu pour les actions par nœud et la ligne d'action groupée de la barre latérale pour les actions à sélection multiple. UIManager.graphNaviation a été renommé graphNavigation. L'option UI.modeRail et ses emplacements Explore / Enrich désactivés ont été supprimés — enregistrez un mode réel avec addRailMode à la place. Voir le CHANGELOG.
Les quatre modes du rail sont intégrés, mais ce ne sont pas les seuls qu'il peut contenir : un plugin ajoute le sien avec addRailMode, c'est ainsi qu'un mode Explorer ou Enrich qui sait ce que signifient vos données est construit.
Désactiver les fonctionnalités
Le mode UI décide de la quantité de chrome qu'un graphique obtient. Chaque fonctionnalité de ce chrome peut également être désactivée une par une, ce qui permet à une intégration de supprimer ce dont son backend ou ses utilisateurs n'ont aucune utilité.
Une fonctionnalité désactivée prend tout qui l'atteint : le bouton, le panneau, l'entrée du menu contextuel et le raccourci clavier vont tous ensemble, donc rien n'est laissé derrière pour cliquer ou appuyer et refuser tranquillement.
const options = {
UI: {
mode: 'full',
notes: { enabled: false },
history: { enabled: false },
inspector: { enabled: false },
editors: { deletion: { enabled: false } },
},
}Les interrupteurs
| Options | Qu'est-ce qui va avec |
|---|---|
UI.topBar.enabled | La bande supérieure, la hauteur qu'elle réserve, les raccourcis propres à ses contrôles (Shift+J, Shift+K, Shift+N, Mod+Z) et les éventuelles actions hôtes. Utilisez les interrupteurs ci-dessous pour déposer une pilule et conserver le reste. |
UI.search.enabled | La pilule Search, son sélecteur de nœuds et Shift+J. |
UI.filter.enabled | La pilule Filter Graph, son panneau et Shift+K. graph.queryEngine filtre toujours à partir du code. |
UI.notes.enabled | La pilule et le panneau Notes, Shift+N, l'outil Ajouter une note, Ajouter une note du menu canevas, N, le menu contextuel de la note et noteManager.addNote, qui refuse. |
UI.history.enabled | Les boutons annuler/rétablir, leur liste déroulante historique et Mod+Z / Mod+Shift+Z. graph.history continue l'enregistrement. |
UI.inspector.enabled | L'entrée Inspecter les propriétés du menu du nœud et I. |
UI.notifications.enabled | Des toasts. graph.notifier.* devient un non-op renvoyant undefined. |
UI.sidebar.enabled | Toute la colonne latérale et la largeur qu'elle prenait. |
UI.propertiesPanel.enabled | Le panneau des propriétés de la barre latérale et son séparateur. |
UI.neighborsPanel.enabled | Le panneau des voisins de la barre latérale et son séparateur. |
UI.viewFlyout.enabled | Le bouton Afficher le rail et son panneau. |
UI.physicsFlyout.enabled | Le bouton du rail Physique et son panneau. |
UI.tooltip.enabled | Survolez les info-bulles, celles épinglées incluses. Prend également { nodes, edges, groups } ou (element, kind) => boolean, voir Tooltips. |
UI.contextMenu.enabled | Chaque menu contextuel. |
UI.navigation.enabled | Le rail de visualisation : ajustement, zoom et plein écran. |
UI.legend: false | La légende de la toile, y compris celle qu'elle dériverait d'elle-même. |
UI.minimap: false | La mini-carte. |
UI.emptyState: false | La carte qu'un toile vide montre. |
UI.table: false | Le dock de données et son Shift+T. |
UI.pivotMode: false | Le mode ferroviaire Pivot, quel que soit celui enregistré. |
Les fonctionnalités de chemin d'écriture ont le même commutateur sous UI.editors, donc une intégration en lecture seule supprime l'opportunité plutôt que d'opposer son veto à chaque clic : nodeCreator, edgeCreator, nodeEditor, edgeEditor et deletion.
Le comportement du rendu est désactivé sous render : zoomEnabled (qui prend également les boutons de zoom du rail, en gardant l'ajustement et le centre), dragEnabled, selectionBox.enabled (le rectangle de sélection ** et ** l'outil Sélectionner ▸ Lasso), enableNodeExpansion (le chevron, son Enter et le pointillé contour réduit), enableFocusMode et interactionEnabled pour tout cela à la fois. simulation.enabled arrête l'exécution de la mise en page.
Le mode Créer suit ses outils
Le mode Créer un rail contient exactement quatre outils : Ajouter un nœud, Ajouter un bord, Ajouter une note et Modifier un nœud. Éteignez les quatre et le mode quitte le rail, avec son raccourci C, plutôt que de s'ouvrir sur un panneau vide. Sélectionner reste toujours.
Vos propres actions dans la barre supérieure
Une action sur l'ensemble du canevas, telle que sa sauvegarde, appartient à la barre supérieure à côté du groupe annuler-rétablir. UI.topBar.actions déclare les pilules dessinées dans le même style que la recherche, le filtre et les notes, donc un thème qui les restyle les restyle également.
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 }]
: [],
}],
},
},
}- Où.
placement: 'end'(valeur par défaut) place la pilule juste avant le groupe annuler-rétablir ;'start'le place après les pilules intégrées à gauche. Les pilules conservent l’ordre dans lequel elles ont été déclarées. - Ce qu'il dit.
text,title,visibleetenabledprennent une valeur ou une fonction du graphique. Les fonctions sont relues après chaque changement de données, legraph.UIManager.refreshTopBar(), et une fois qu'une promesse renvoyée paronclickest réglée. Tant que cette promesse n'est pas respectée, la pilule est désactivée, donc une sauvegarde lente ne peut pas être lancée deux fois. - Ce qu'il obtient.
onclick(event, ctx)reçoit le graphique et deux assistants qui ouvrent le modal de la bibliothèque :ctx.promptData({ fields })résout les valeurs ounullen cas d'annulation, etctx.confirm({ body })résouttrueuniquement sur le bouton de confirmation.ctx.refresh()relit chaque action. - Un caret. Avec
menu, la pilule devient un bouton partagé : l'étiquette exécuteonclick, et un caret ouvre les lignes, dans le même format de ligne que les menus contextuels (sans sous-menus). Un formulaire de fonction reçoit le mêmectxet est appelé à chaque actualisation et à chaque ouverture ; bien qu'elle ne renvoie aucune ligne, la pilule n'a pas de curseur. Échapper ou un clic ailleurs ferme les lignes. - Un raccourci.
shortcutdessine un badge sur la pilule. Il s'agit uniquement d'une étiquette : enregistrez vous-même la reliure.
Un plugin ajoute une pilule avec ctx.addTopBarAction(action), qui renvoie un broyeur (voir Plugins). Les pilules suivent la plaquette : aucune en mode viewer ou static, et aucune en mode UI.topBar.enabled: false.
Une toile vide
Un graphique sans nœud ni note affiche une carte au milieu du canevas, donc une source sans rien à dessiner ne se lit pas comme un graphique dont le chargement a échoué. Il indique "Rien sur le canevas pour l'instant" à moins que vous ne lui donniez autre chose à dire :
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
},
},
},
}La carte suit les données. Ajouter un nœud ou une note l'enlève, et supprimer le dernier d'entre eux, à la main ou en annulant l'ingestion qui les a apportés, le ramène. Les nœuds cachés par les filtres comptent toujours : le graphique est rétréci, pas vide.
render s'exécute à chaque fois que la carte apparaît, pas à chaque changement pendant qu'elle est active. initial est true jusqu'à ce que le graphique ait contenu un nœud, donc un analyste qui a vidé un canevas complet peut savoir autre chose que ce que l'on dit à une source vide. Une chaîne renvoyée s'affiche sous forme de texte brut, et render peut être async comme les crochets ci-dessous.
La carte utilise la surface et la bordure du chrome, elle suit donc le thème. Il laisse passer les gestes du canevas, donc le panoramique, le zoom et le menu du canevas fonctionnent sur celui-ci, sauf sur le contenu interactif : un bouton, un lien, un contrôle de formulaire ou tout ce qui porte un tabindex obtient ses propres clics. UI.emptyState: false ne montre rien du tout.
Contenu asynchrone
Chaque hook de contenu fourni par le consommateur peut renvoyer un Promise au lieu du contenu lui-même. Cela couvre les mainHeader.render de la barre latérale, propertiesPanel.render / nodePropertiesMap / edgePropertiesMap, neighborsPanel.render, un un panneau supplémentaire title et render, les tooltip's render, renderNodeExtra, renderEdgeExtra et les cartes de propriétés, et les canevas vides render.
Cela existe dans le cas où les données d'un nœud sont une référence à un enregistrement plutôt que l'enregistrement lui-même, et le contenu intéressant se trouve derrière un appel HTTP :
const options = {
UI: {
tooltip: {
renderNodeExtra: async (node, { signal }) => {
const res = await fetch(`/enrich/${node.getData().uuid}`, { signal })
return renderChips(await res.json()) // an HTMLElement
},
},
},
}Ajoutez async, transférez le signal, et la bibliothèque s'occupe du reste :
| Espace réservé | Pendant que la promesse est en attente, un squelette thématique occupe l’emplacement. |
| Échange | Lors de la résolution, le contenu remplace l'espace réservé en place. |
| Obsolescence | Un résultat qui arrive après la disparition de son emplacement (vous avez survolé un autre nœud, modifié la sélection, actualisé le panneau) est dropped. Seul le rendu le plus récent d'une surface peut être validé, quel que soit l'ordre dans lequel il est résolu. |
| Annulation | ctx.signal est abandonné lorsqu'un rendu est remplacé ou que le graphique est détruit, donc un fetch transmis est annulé plutôt que divulgué. |
| Échec | Une promesse rejetée affiche une ligne d'erreur compacte au lieu de laisser une flèche indéfiniment. Un rendu interrompu n'est pas un échec et ne montre rien. |
Le contexte de rendu
Chaque hook de contenu reçoit un RenderContext comme argument last. Les hooks synchrones existants l’ignorent simplement.
render: async (element, { signal, isStale }) => {
const rows = await fetchRows(element, signal)
if (isStale()) return '' // cheap guard for non-abortable work
return renderRows(rows)
}Personnalisation de l'espace réservé
UI.asyncContent remplace ce qui est affiché en attente et en cas d'échec. Les deux acceptent une chaîne, un HTMLElement ou une fonction de la surface :
const options = {
UI: {
asyncContent: {
placeholder: (surface) => surface === 'tooltip' ? 'Loading…' : mySkeleton(),
error: 'Could not load',
},
},
}surface est l'un des 'tooltip', 'properties', 'neighbors', 'mainHeader', 'extraPanel' ou 'emptyState'.
Les crochets synchrones sont intacts
Le contenu renvoyé se comporte directement exactement comme il l’a toujours fait : même appel, même cadre, pas d’espace réservé et pas d’élément wrapper. Rien dans une intégration existante ne change.
Ce qui ne peut pas être asynchrone
Deux groupes restent volontairement synchrones :
HeaderMapEntry.title/subtitlealimentent l'emplacement de titre à ajustement automatique * et * la recherche de nœuds (resolveNodeByName), qui ont tous deux besoin du texte maintenant. Utilisez unmainHeader.renderasynchrone si vous avez besoin de récupérer des détails dans l'en-tête.PropertyEntry.name/value— récupère les lignes à la place : unnodePropertiesMapasynchrone se résout une fois, puis restitue les entrées simples, plutôt que de mettre une double flèche dans chaque cellule.
Les hooks de rendu par image (render.renderNode, renderLabel, renderCluster et NodeStyle.html) sont également synchronisés uniquement : ils s'exécutent à l'intérieur du chemin de dessin, par nœud, par tick. Si vous avez besoin d'un nœud asynchrone decoration, récupérez les données du nœud et laissez le moteur de rendu synchrone les lire.