import { createGraphEngine, installGraphExtension } from '@graphora/engine' import { createCircularLayout } from '@graphora/layouts' import { CanvasGraphRenderer, GRAPHORA_STYLE_ATTRIBUTE, createViewport } from '@graphora/renderer' import './style.css' const container = document.querySelector('#graph') const lifecycleOutput = document.querySelector('#lifecycle') if (container === null || lifecycleOutput === null) { throw new Error('Missing graph container or lifecycle output.') } void main() async function main(): Promise { const renderer = new CanvasGraphRenderer() const engine = createGraphEngine({ graph: { nodes: [ { id: 'api', label: 'API', attributes: { [GRAPHORA_STYLE_ATTRIBUTE]: { fill: '#0ea5e9' } } }, { id: 'worker', label: 'Worker', attributes: { [GRAPHORA_STYLE_ATTRIBUTE]: { fill: '#22c55e' } } }, { id: 'database', label: 'Database', attributes: { [GRAPHORA_STYLE_ATTRIBUTE]: { fill: '#f97316' } } }, { id: 'queue', label: 'Queue' } ], edges: [ { id: 'api-worker', source: 'api', target: 'worker', label: 'dispatches' }, { id: 'worker-db', source: 'worker', target: 'database', label: 'writes' }, { id: 'api-queue', source: 'api', target: 'queue', label: 'enqueues' }, { id: 'queue-worker', source: 'queue', target: 'worker', label: 'feeds' } ] }, renderer, layout: createCircularLayout({ radius: 120 }), viewport: createViewport({ width: container.clientWidth, height: container.clientHeight }), theme: { backgroundColor: '#f8fafc', node: { radius: 22 }, edge: { stroke: '#64748b', strokeWidth: 2 }, label: { fontSize: 13 } } }) const lifecycleEvents: string[] = [] engine.onAnyLifecycle((event) => { lifecycleEvents.push(event.type) lifecycleOutput.value = lifecycleEvents.join(' → ') }) const counter = document.querySelector('#extension-count')! const toggle = document.querySelector('#toggle-extension')! const redraw = document.querySelector('#render-graph')! let count = 0 const extension = installGraphExtension(engine, { id: 'render-counter', category: 'plugin', activate(context) { context.onCleanup( context.engine.onLifecycle('render:after', () => { counter.value = String(++count) }) ) }, dispose() { counter.dataset.disposed = 'true' toggle.disabled = true redraw.disabled = true } }) toggle.addEventListener('click', () => { if (extension.enabled) extension.disable() else extension.enable() toggle.textContent = extension.enabled ? 'Disable counter' : 'Enable counter' }) redraw.addEventListener('click', () => engine.render()) document .querySelector('#destroy-engine')! .addEventListener('click', () => engine.destroy()) window.addEventListener('pagehide', () => engine.destroy(), { once: true }) engine.mount(container) await engine.runLayout({ fitToView: { padding: 56 } }) }