Tableau de données
Une disposition des forces est mauvaise pour trois choses que les gens font constamment : lire des valeurs exactes, sélectionner à grande échelle et déterminer par où commencer. UI.table divise un data dock du bas du canevas pour répondre aux trois : les lignes du graphique, triables et sélectionnables.
La boucle est table à trouver, canevas à comprendre, barre latérale à lire :
- Le tableau s'ouvre trié par demandes, le service le plus occupé est donc la première ligne.
- Cliquez sur n'importe quel titre pour trier à nouveau ; cliquez à nouveau pour inverser.
- Cliquez sur une ligne pour la sélectionner,
Ctrl+Clickpour en ajouter une, ouShift+Clickpour prendre une plage dans l'ordre indiqué — puis agissez sur le lot avec les actions groupées de la barre latérale. - Élastiquez plutôt un groupe sur la toile et les lignes correspondantes s’illuminent.
- Affinez les lignes des en-têtes — le contrôle suit la colonne : tapez dans Service, choisissez parmi Kind, définissez des limites sur Requests / day. Cela ne touche jamais le canevas : le filtre du graphique reste avec le panneau de filtres, donc les deux ne peuvent pas se battre.
- Nodes / Edges changez ce qui est répertorié. CSV et JSON écrivent exactement ce qui est à l'écran – cet onglet, ces colonnes, ce tri, ce filtre.
Le dock répertorie également les nœuds que le graphique est actuellement masqué, avec une colonne Visibility indiquant si un filtre ou un masquage manuel est responsable – donc « 23 nœuds cachés » est quelque chose que vous pouvez inspecter plutôt que de faire confiance.
Faites glisser le séparateur pour le redimensionner ou pliez-le sur sa barre d'en-tête avec le chevron. Ni l'un ni l'autre ne déplace le graphique : la simulation s'adapte au conteneur, pas au canevas, de sorte que l'ouverture et la fermeture de Chrome ne peuvent jamais modifier une mise en page.
Voir Tableau de données pour l'ensemble complet des options.
// import { ColorPaletteMapper, tableColumns } from 'pivotick'
//
// The data dock comes with `mode: 'full'`, folded to its header bar — click the chevron
// or press Shift+T. `open: true` below starts it expanded instead. Everything here is
// optional; `UI: { mode: 'full' }` alone gets you a dock.
//
// Columns are derived when you don't declare any: the graph-aware ones lead (Label,
// Degree, Visibility) and the data keys follow, ordered by how many nodes carry them.
// They are declared here instead, to put `requests` on the right and give three columns
// a row filter — one of each kind, since the control follows the column's `type`.
// `tableColumns` holds the graph-aware ones — clone one to relabel it.
const options = {
UI: {
mode: 'full',
// Full mode's minimap would sit in the corner this card wants for the dock,
// and the dock is the subject — see the Minimap card for that one.
minimap: false,
// Top-left, because this embed is 560px tall and the dock takes 42% of it: the
// canvas that's left is shorter than the mode rail, so the bottom-left corner has
// no column to give. A bottom-left legend would shrink to its header here.
legend: { position: 'top-left' },
table: {
open: true,
height: 0.42,
// Busiest first — the question you actually arrive with.
sort: { key: 'requests', direction: 'desc' },
columns: [
{ ...tableColumns.label, label: 'Service', filterable: true },
{ key: 'kind', label: 'Kind', type: 'select', filterable: true },
{ key: 'owner', label: 'Owner', type: 'select' },
{ key: 'tier', label: 'Tier', type: 'select' },
{ key: 'requests', label: 'Requests / day', type: 'numberRange', align: 'right', filterable: true },
{ ...tableColumns.degree, label: 'Links' },
],
},
},
// Colour by the same `kind` the table shows, so a row and a dot can be matched by
// eye. Declaring `nodeTypeAccessor` is also what earns the canvas its legend.
render: {
nodeTypeAccessor: (node) => node.getData().kind,
defaultNodeStyle: {
color: (node) => palette.getColor(node.getData().kind),
},
},
}const data = {
nodes: [
{ id: 'api', data: { label: 'api-gateway', kind: 'service', owner: 'Platform', requests: 48200, tier: 'edge' } },
{ id: 'auth', data: { label: 'auth-service', kind: 'service', owner: 'Identity', requests: 31400, tier: 'core' } },
{ id: 'billing', data: { label: 'billing-service', kind: 'service', owner: 'Payments', requests: 8600, tier: 'core' } },
{ id: 'search', data: { label: 'search-service', kind: 'service', owner: 'Discovery', requests: 22750, tier: 'core' } },
{ id: 'users-db', data: { label: 'users-db', kind: 'datastore', owner: 'Identity', requests: 39900, tier: 'data' } },
{ id: 'orders-db', data: { label: 'orders-db', kind: 'datastore', owner: 'Payments', requests: 12300, tier: 'data' } },
{ id: 'index', data: { label: 'search-index', kind: 'datastore', owner: 'Discovery', requests: 20100, tier: 'data' } },
{ id: 'cache', data: { label: 'edge-cache', kind: 'datastore', owner: 'Platform', requests: 51000, tier: 'edge' } },
{ id: 'mailer', data: { label: 'mailer', kind: 'worker', owner: 'Payments', requests: 1450, tier: 'async' } },
{ id: 'reindexer', data: { label: 'reindexer', kind: 'worker', owner: 'Discovery', requests: 980, tier: 'async' } },
],
edges: [
{ from: 'api', to: 'auth', data: { label: 'authenticates' } },
{ from: 'api', to: 'search', data: { label: 'queries' } },
{ from: 'api', to: 'billing', data: { label: 'charges' } },
{ from: 'api', to: 'cache', data: { label: 'reads' } },
{ from: 'auth', to: 'users-db', data: { label: 'reads' } },
{ from: 'billing', to: 'orders-db', data: { label: 'writes' } },
{ from: 'billing', to: 'mailer', data: { label: 'enqueues' } },
{ from: 'search', to: 'index', data: { label: 'reads' } },
{ from: 'reindexer', to: 'index', data: { label: 'writes' } },
{ from: 'reindexer', to: 'orders-db', data: { label: 'reads' } },
],
}