Naar inhoud

Plug-ins ​

Een plugin is een op zichzelf staande bundel van UI-elementen, sneltoetsen en levenscyclushaken dat zichzelf in een grafiek installeert zonder dat de kern hoeft te weten dat het bestaat. Registreren één declaratief of imperatief:

ts
import { Pivotick, minimap } from 'pivotick'

// declaratively
const graph = new Pivotick(container, data, { plugins: [minimap()] })

// …or at any point later
graph.use(minimap({ position: 'bottom-left' }))

Een plug-in die wordt geïnstalleerd nadat de grafiek live is, wordt bijgehouden in de huidige levenscyclusfase, dus er is geen "te laat" - en alles wat het registreert wordt afgebroken met de gebruikersinterface.

De vorm van een plug-in ​

ts
import type { PivotickPlugin } from 'pivotick'

const myPlugin: PivotickPlugin = {
    name: 'my-plugin',          // used for de-duplication and logging
    install(ctx) { /* … */ },   // called once
}

Als u dezelfde name tweemaal installeert, wordt het duplicaat gewaarschuwd en overgeslagen, zodat er een plug-in kan zijn vermeld in plugins en opnieuw toegepast via graph.use zonder te verdubbelen.

Welke install wordt overhandigd ​

ctx is een {@link PluginContext}:

LidWaar het voor is
graphde live Graph — knooppunten, randen, de datagebeurtenisbus, queryEngine
uide UIManager — opties, meldingen, de modusopslag
layouthet DOM-scaffold, lees live (nooit een momentopname). layout.canvas is waar canvas-gedockt chroom naartoe gaat
addElement(element, slot?)plaats een UIComponent in de levenscyclus, gemonteerd in slot
addPanel(panel) / removePanel(id) / refreshPanel(id?)zijbalkpanelen — dezelfde deur als UI.extraPanels
addDockTab(tab) / removeDockTab(id)een ruit in het onderste dock - dezelfde deur waar de ingebouwde tafel doorheen komt
addRailMode(mode) / removeRailMode(id)een modus op de linkerrail, naast Selecteren / Maken / Bekijken / Fysica
addTopBarAction(action) / removeTopBarAction(id)een pil in de bovenste balk — dezelfde deur als UI.topBar.actions
addPivot(definition)een uitvoerbare verrijking — zie Pivots & verrijking
onPhase(phase, cb)haak afterMount / graphReady / destroy; retourneert een afmelding
addKeybinding(binding)een snelkoppeling die wordt verwijderd wanneer de gebruikersinterface wordt afgebroken
keyManagerhet sleutelbindingsregister, voor alles wat er meer mee te maken heeft

De key van een binding zijn modificaties en vervolgens de sleutel, vergezeld door +: 'Shift+K', 'Ctrl+F', 'ArrowUp'. Schrijf modifiers als Ctrl, Meta (Cmd op macOS), Shift, Alt, daarin bestel en gebruik Mod om Ctrl of Meta te matchen - wat een snelkoppeling betekende om op beide platforms te werken wil. De sleutel zelf is letterlijk KeyboardEvent.key, dus dat is zo hoofdlettergevoelig en shift verandert dit: de twee helften van een ongedaan maken-paar zijn 'Mod+z' en 'Mod+Shift+Z'. Bindingen worden nooit geactiveerd als een tekstveld focus heeft.

Welke layout slots er bestaan, hangt af van de modus: canvas en notification altijd, graphnavigation in elke modus behalve static, mainheader / modal / slidePanel / moderail / toolpanel / flyout / legend in full en light, en sidebar in Alleen full. Lees het slot op het moment dat u het nodig heeft, in plaats van het in de cache op te slaan.

Een UI-element bijdragen ​

Verleng UIComponent en de levenscyclus wordt voor u bepaald, inclusief demontage van alles geregistreerd via track / listen / trackInteraction:

ts
import { UIComponent } from 'pivotick'

class Watermark extends UIComponent {
    onMount(slot) {
        this.element = document.createElement('div')
        this.element.textContent = 'draft'
        slot?.appendChild(this.element)
    }

    onGraphReady() {
        // `listen` and `trackInteraction` unsubscribe themselves on destroy
        this.trackInteraction('canvasZoom', () => this.reposition())
    }

    onDestroy() {
        this.element?.remove()
    }
}

const watermark: PivotickPlugin = {
    name: 'watermark',
    install: (ctx) => ctx.addElement(new Watermark(ctx.ui), ctx.layout?.canvas),
}

De vier fasen zijn mount(slot) → afterMount() → graphReady() → destroy(). graphReady wordt geactiveerd zodra de simulatie is voltooid, dus het kan seconden later zijn mount op een grote grafiek - alles wat op het scherm zou moeten staan, hoort er onmiddellijk in onMount / onAfterMount (een frame uitgesteld als graph.renderer nodig is, wat gebouwd na de gebruikersinterface).

Zie de galerij Uitbreiden met een plug-in kaart voor een live, compleet voorbeeld.

Een dokvenster bijdragen ​

ctx.addDockTab plaatst een venster in het onderste dock, naast de gegevens tafel. Het dok is eigenaar van de regio: de hoogte, de scheidslijn, de vouw en de strook die geeft de vensters een naam - en uw venster is eigenaar van wat erin zit:

ts
const auditLog: PivotickPlugin = {
    name: 'auditLog',
    install: (ctx) => ctx.addDockTab({
        label: 'Audit',
        render: () => buildPane(ctx.graph),   // once, on first activation
        toolbar: () => [clearButton],         // on every activation
        onActivate: () => resumePainting(),
        onDeactivate: () => stopPainting(),
    }),
}

Vier dingen zijn de moeite waard om te weten:

  • Een tabblad is een venster, niet een weergave ervan. Als uw venster meerdere eigen weergaven heeft, blijft een enkel docktabblad en tekent zijn eigen schakelaar in toolbar, bellend handle.refresh() om van lichaam te veranderen — wat render opnieuw aanroept. Dat is precies wat de gegevenstabel geldt voor Nodes / Edges, en waarom de strook van het dok er nooit één plat maakt het uitzicht van het venster naast een ander venster. Het schakelen van uw eigen DOM achter de rug van het dock doet dat wel werkt niet: het behoudt het element render dat het heeft gegeven, en zou een oud knooppunt opnieuw bevestigen op de volgende activering. Teken een binnenste schakelaar als een gesegmenteerd besturingselement, niet als tabbladen: de buitenste niveau ziet er al uit als tabbladen. pvt-dock-views op de strip en pvt-dock-view op elk knop (plus active) zijn openbaar, dus het lijkt op de schakelaar van de tafel en volgt de thema zonder dat jij het ook herhaalt.
  • Het eerste venster bouwt de regio. Plug-ins worden geïnstalleerd nadat de gebruikersinterface is gebouwd, dus een tabblad komt altijd te laat voordat de eigen moduspoort van het dok namens hem ja heeft gezegd. Door er een te registreren, ontstaat het dock, wat betekent dat uw plug-in ermee werkt UI.table: false en er hoeft niets anders te zijn ingeschakeld dan de full-modus.
  • render wordt één keer aangeroepen, lui, de eerste keer dat het venster wordt geopend; het element is bewaard en daarna opnieuw bevestigd, zodat het zijn eigen scrollpositie behoudt. toolbar is bij elke activering opnieuw opgebouwd, zodat de bedieningselementen de huidige status van uw paneel kunnen lezen.
  • onActivate / onDeactivate zijn het enige signaal dat u buiten beeld bent, en wat wat ermee te doen is, hangt af van uw paneel. Inhoud die een functie is van de stroom van de grafiek de staat kan stoppen met werken terwijl hij verborgen is en bij terugkeer opnieuw worden afgeleid – dat is wat de tabel zegt doet. Inhoud die iets miss zou missen (een ruit die naar een live bus kijkt - een evenement is verdwenen zodra het is afgevuurd) moet blijven werken en alleen maar stoppen met schilderen, waardoor de achterstand wordt weggewerkt als het terugkomt. Ook niets over de haken heeft de voorkeur.

Een tabblad is geen UIComponent, dus niets drijft levenscyclusfasen daarin aan. Wanneer uw ruit heeft graphReady nodig, houd een UIComponent vast, addElement deze en roep addDockTab vanuit zijn onMount — zie Een UI-element bijdragen hierboven.

Zie de galerijkaart Een dockvenster toevoegen voor een live, compleet voorbeeld: een venster met twee eigen weergaven, naast de gegevenstabel.

Een spoormodus bijdragen ​

De vier modi van de linkerrail – Selecteren, Maken, Bekijken, Natuurkunde – zijn ingebouwd. Al het andere is van jou: addRailMode zet een eigen modus onder een scheidingslijn, erna, geordend onder zijn collega's met order.

Een aanwijzermodus is eigenaar van het contextuele toolpaneel. Geef zijn tools aan en het paneel trekt ze op de manier waarop Select's en Create's worden getekend, met de inschakeling, de ingeschakelde/uitgeschakelde statussen en de ineenstorting voor u afgehandeld:

js
const explore = {
    name: 'explore-mode',
    install(ctx) {
        const selected = () => ctx.graph.renderer.getGraphInteraction().getSelectedNode()?.node

        ctx.addRailMode({
            id: 'explore',
            label: 'Explore',
            icon: compassSvg,
            shortcut: 'E',
            tools: [
                { id: 'expand', label: 'Expand neighbours', icon: plusSvg, kind: 'action',
                  enabled: () => !!selected(),
                  run: () => expandFrom(ctx.graph, selected()) },
                { id: 'walk', label: 'Path walk', icon: pathSvg, kind: 'toggle',
                  run: (armed) => armPathWalk(ctx.graph, armed) },
            ],
            onExit: () => armPathWalk(ctx.graph, false),
        })
    },
}

new Pivotick(el, data, { plugins: [explore] })

De kind van een gereedschap bepaalt hoe het zich gedraagt. Een 'action' wordt één keer uitgevoerd en verlaat de modus zoals deze was. A 'toggle' activeert een modaal hulpmiddel: het paneel klapt in en de railknop verandert in het eigen pictogram en label van dat gereedschap, precies zoals het slot van Select Lasso wordt. Een 'default' is het hulpmiddel waarop de modus rust; noem het in defaultTool en het is waar een schakelaar naar terugkeert.

tools kan ook een functie zijn, herlees elke keer dat het paneel wordt getekend — gebruik dat wanneer de rijen zijn afhankelijk van wat er is geselecteerd en worden niet alleen grijs weergegeven. Voor alles een rij kan niet worden uitgedrukt, retourneert render() een element dat onder de rijen is toegevoegd:

js
ctx.addRailMode({
    id: 'explore', label: 'Explore', icon: compassSvg,
    tools: () => toolsFor(ctx.graph.renderer.getGraphInteraction().getSelectedNode()),
    render: () => depthSlider(),
})

Een flyout-modus opent in plaats daarvan een instellingen-overlay, zoals Weergave en Natuurkunde. Subklasse Flyout en met de hand addRailMode een fabriek — kern monteert het paneel, opent het precies terwijl uw modus is actief en neemt deze weg met de modus:

js
import { Pivotick, Flyout } from 'pivotick'

class EnrichFlyout extends Flyout {
    mode = 'enrich'                     // must match the definition's id
    template() { return this.headerRow(sparklesSvg, 'Enrich') + this.toggleRow('auto', gearSvg, 'Auto-enrich', 'Enrich on load') }
    wire() { this.wireToggle('auto', () => toggleAuto(), () => isAuto()) }
}

ctx.addRailMode({
    id: 'enrich', label: 'Enrich', icon: sparklesSvg,
    kind: 'flyout',
    flyout: (ui) => new EnrichFlyout(ui),
})

Modi sluiten elkaar uit, dus de jouwe sluit View en Physics gratis uit: één winkel, één actieve modus.

Drie dingen die de moeite waard zijn om te weten:

  • icon is een ruwe SVG-snaar, geïnjecteerd als HTML en nooit opgeschoond. Het moet afkomstig zijn van een bron die u vertrouwt. CSS verkleint het naar 20px op de rail, 18px in het paneel.
  • shortcut volgt de normale regels voor sneltoetsen: een sleutel claimen is al iets anders owns schaduwt het, met een waarschuwing, totdat je modus wordt verwijderd.
  • onExit is waar je uitschakelt. Het wordt uitgevoerd wanneer de modus wordt verlaten en wanneer deze wordt verwijderd terwijl actief - in welk geval de rail terugvalt naar Select. Het werkt niet op de gebruikersinterface afbraak; de ontdoeners die uw plug-in al bijhoudt, dekken dat.

addRailMode retourneert een ontdoener en werkt in elke modus: de knop wordt alleen daar getekend er is een rail, wat full en light betekent.

Het aansturen van de kijkpoort ​

Alles wat door de grafiek navigeert – een minimap, een overzicht, een ‘jump to’-besturingselement – maakt gebruik van twee renderermethoden:

ts
// The extent of everything drawn, in graph coordinates (null when there's nothing).
const bounds = graph.renderer.getContentBounds()

// Put a graph-space point in the middle of the canvas.
graph.renderer.setViewport({ x: 120, y: -40 })
graph.renderer.setViewport({ x: 0, y: 0, scale: 1.5, animate: true })

setViewport laat de schaal met rust tenzij je er een passeert, dus dit is de primitieve voor pannen. Om te lezen waar de weergave momenteel is is, keert u de canvashoeken om in plaats van reiken naar een zoomtransformatie - dit is renderer-agnostisch:

ts
const rect = graph.UIManager.layout.canvas.getBoundingClientRect()
const topLeft = graph.renderer.screenToGraphCoordinates(rect.left, rect.top)
const bottomRight = graph.renderer.screenToGraphCoordinates(rect.right, rect.bottom)

fitAndCenter() (past op alles), zoomIn() / zoomOut() en focusElement(nodeOrEdge) blijven de snelkoppelingen voor de veel voorkomende gevallen.

De minikaart ​

Een first-party plug-in: een in de cache opgeslagen overzicht van de hele grafiek, vastgezet in een canvashoek, met een rechthoek die laat zien wat er op het scherm staat. Klik om de weergave opnieuw te bekijken; sleep de rechthoek om te pannen. Een heel kleine schakelaar in de hoek waar hij tegenover staat, vouwt hem weg naar alleen die knop en brengt hem terug.

In de modus full wordt er een voor je geactiveerd — het maakt deel uit van het chroom van die modus, zoals de header, de zijbalk en de moderail. Elke andere modus laat het aan jou over:

ts
// full mode: already there, nothing to install
new Pivotick(container, data, { UI: { mode: 'full' } })

// any other mode: ask for it, with `UI.minimap` …
new Pivotick(container, data, { UI: { mode: 'light', minimap: true } })

// … or as the plugin it is
import { Pivotick, minimap } from 'pivotick'
new Pivotick(container, data, { UI: { mode: 'light' }, plugins: [minimap()] })

UI.minimap heeft dezelfde {@link MinimapOptions} nodig als de plug-in, dus { minimap: { position: 'top-left' } } configureert de enige volledige modus die met zich meebrengt. UI.minimap: false onderdrukt het. Het doorgeven van je eigen minimap() in plugins wint ook — de volledige modus staat opzij in plaats van een tweede te monteren - dus een bestaande plugins: [minimap({ … })] blijft precies zo werken als voorheen.

html
<!-- browser build: it hangs off the global, like Node and Edge -->
<script>new Pivotick(el, data, { plugins: [Pivotick.minimap()] })</script>

Opties ​

OptieTypStandaardWat het doet
position'bottom-right' | 'bottom-left' | 'top-right' | 'top-left''bottom-right'In welke hoek hij vastklikt. 'bottom-right' is de enige hoek die het ingebouwde chroom vrijlaat in de full-modus.
widthnumber200Breedte in CSS pixels, inclusief rand.
heightnumberafgeleidHoogte in CSS pixels. Als dit wordt weggelaten, volgt het de beeldverhouding van het canvas (vastgeklemd op 70–400px), zodat de rechthoek de vorm van het echte venster behoudt.
collapsedboolean | 'auto'false, of 'auto' voor de ene full-modus mountsIn welke staat het wordt geopend. De schakelaar is er altijd; dit is pas waar het begint. Zie Uit de weg gaan voor 'auto'.

Niets anders is configureerbaar, want niets anders hoeft dat: het detailniveau en de hertekencadans past zich aan de grafiek aan.

Wat het trekt ​

Knooppunten zijn punten in de kleur die de renderer ze heeft gegeven, randen zijn haarlijnen eronder, en notities – de enige inhoud met een eigen formaat – zijn op schaal getekend als doorschijnend blokken in hun eigen kleur, over de knooppunten zoals ze zich op het canvas bevinden. Verborgen en uitgefilterde inhoud wordt weggelaten. Voorbij een paar duizend knooppunten wordt het beeld een dichtheid kaart in plaats daarvan (één stempel per knooppunt, geen randen), die beter leest dan tienduizenden overlappende punten; aantekeningen worden hoe dan ook getekend. Het wordt allemaal in de cache opgeslagen en alleen opnieuw gerasterd wanneer het beeld veranderde echt: een knooppunt viel weg, een notitie werd verplaatst, de lay-out werd aangepast, een filter toegepast — zodat pannen en zoomen vrij blijven, hoe groot de grafiek ook is.

Een HTML-kaart (html op shape: 'none') heeft geen kleur die de minimap kan lezen, dus de stip neemt de kleur aan van de kleinste tekening die een vorm is: de basisstijl, of anders de eerste dergelijke niveau in tiers. Bij geen, of bij een kleur transparent wordt de stip ingetekend de eigen inkt van de minimap (--pvt-minimap-ink). De grootte van de punten komt overeen met wat het knooppunt bedekt, dus a kaart of een renderNode knooppunt krijgt een punt ter grootte van een kaart in plaats van een punt ter grootte van size.

Uit de weg gaan ​

Een minimap waar je om hebt gevraagd, blijft waar je hem neerzet. De ene full-modus wordt namens u geactiveerd Er werd niet om gevraagd, dus het opent collapsed: 'auto' en houdt rekening met het canvas: it blijft open zolang het canvas minimaal vier minimaps breed en hoog is, en vouwt zichzelf op weg naar de schakelaar daaronder. Vanaf dat moment blijft het het canvas volgen – wegklappend wanneer de zijbalk eroverheen gaat of het raam smaller wordt, en terugkomt wanneer de kamer dat doet.

Op het moment dat iemand hem opvouwt of terugbrengt – via de schakelaar of er doorheen setCollapsed() — dat stopt: een expliciete keuze blijft hangen en het formaat wordt niet later overschreven het. Geef een expliciete collapsed: true / false door om u vanaf het begin af te melden voor 'auto'.

De pijl van de schakelaar wijst naar de hoek waarin de minimap zich bevindt: de richting waarin deze wordt opgevouwen weg – en draait om zodra hij is ingeklapt. Opgevouwen trekt hij helemaal niets, zelfs niet de rechthoekig, zodat het niets kost als het niet in de weg staat; openen waarvoor het opnieuw wordt gerasterd hoe het canvas er tegen die tijd ook uitziet. setCollapsed(boolean) en isCollapsed() op de Minimap instance stuurt hetzelfde vanuit code.

Wat het kost en wat het kost ​

De minimap brengt de inhoudsgrenzen ** samen met de huidige viewport** in kaart, zodat de de rechthoek is altijd zichtbaar en heeft een eerlijke grootte; zoom ver uit en de grafiek wordt kleiner binnen het frame in plaats van dat de rechthoek eraf glijdt.

Het houdt de grafiek in een bitmap buiten het scherm en herrastert deze alleen wanneer de afbeelding wordt weergegeven daadwerkelijk veranderd: bij een gegevenswijziging, wanneer een filter knooppunten verbergt of herstelt, wanneer een knooppunt dat is valt weg na slepen, bij elke 10e simulatietik terwijl een lay-out vastloopt, en bij het wijzigen van het formaat. Door te pannen en te zoomen wordt één afbeelding en één rechthoek opnieuw getekend, zodat navigeren hetzelfde kost of de grafiek 20 knooppunten of 50.000 heeft.

Detail verslechtert met opzet met de grootte:

GrafiekGetekend als
≤ 1500 knooppunteneen punt per knooppunt in de kleur die de renderer heeft opgelost, plus haarlijnranden (gezakt voorbij 4000 randen)
Meer dan 1500 knopenéén stempel per knooppunt in één enkele inkt, waarbij alfa zich ophoopt: dichte gebieden worden gelezen als een dichtheidskaart en er wordt helemaal geen stijl per knooppunt opgelost

Het werkt in de modi full, light en viewer, en full is degene die het koppelt zonder dat erom gevraagd wordt. In static — dat geen interacties belooft — is het niet aangekoppeld, en als u het daar installeert, wordt gewaarschuwd.

Zie de galerijkaart Minimap voor een live-kaart.