OG
JS
Docs
Demos
Examples
Node paint order
Higher priorities paint and pick on top. Equal priorities keep stable insertion order.
Promote blue
Promote amber
Promote rose
Equal order
Top: blue · hover: none · click: none
// `paintOrder` expresses which overlapping node should appear and pick on top. // OGJS keeps equal-priority insertion order stable and applies style rules without changing // topology, so applications never reorder render buffers manually. import { Ogjs } from '../../src/index'; const og = new Ogjs({ container: '#viz', theme: 'light' }); og.setGraph({ nodes: [ { id: 'blue', x: -12, y: 0, size: 38, color: '#4f7fbf', mark: 'square', paintOrder: 7, borderWidth: 2, borderColor: '#ffffff' }, { id: 'amber', x: 0, y: 0, size: 38, color: '#d39a3c', mark: 'circle', paintOrder: 2, borderWidth: 2, borderColor: '#ffffff' }, { id: 'rose', x: 12, y: 0, size: 38, color: '#c96b78', mark: 'diamond', paintOrder: 0, borderWidth: 2, borderColor: '#ffffff' }, ], edges: [], }); og.camera.fit({ target: 'all', paddingPx: 32, maxOccupancy: .18, retain: true }); const status = document.querySelector('#order-status'); const state = { top: 'blue', hover: null, click: null, mode: 'initial' }; og.styles.addNodeRule(({ id }) => { // A larger `paintOrder` moves a node above overlapping peers for rendering and picking without // reordering or replacing the underlying graph records. if (state.mode === 'initial') return undefined; return { paintOrder: state.mode === 'tie' ? 0 : id === state.mode ? 10 : 0 }; }); const update = () => { status.textContent = `Top: ${state.top} · hover: ${state.hover ?? 'none'} · click: ${state.click ?? 'none'}`; }; for (const button of document.querySelectorAll('[data-promote]')) { button.addEventListener('click', () => { state.mode = button.dataset.promote; state.top = state.mode === 'tie' ? 'rose' : state.mode; state.hover = null; state.click = null; og.styles.apply(); for (const control of document.querySelectorAll('[data-promote]')) { control.setAttribute('aria-pressed', String(control === button)); } update(); }); } og.on('nodehover', ({ nodeIndex }) => { state.hover = String(og.graph.getNodeId(nodeIndex)); update(); }); og.on('nodeout', () => { state.hover = null; update(); }); og.on('nodeclick', ({ nodeIndex }) => { // Click evidence confirms the visually top mark is also the picked mark; OGJS keeps draw and // hit-test order under the same declarative priority. state.click = String(og.graph.getNodeId(nodeIndex)); update(); });
<!doctype html> <html lang="en"><head><meta charset="utf-8"/> <meta name="viewport" content="width=device-width, initial-scale=1"/> <title>Ogjs example — Node paint order</title> <link rel="stylesheet" href="../site.css"/><link rel="stylesheet" href="./data-lifecycle.css"/> <style> #viz { left: 452px; } .order-controls { display:flex; flex-wrap:wrap; gap:8px; margin-top:14px; } .order-controls button { border:1px solid #cbd5e1; border-radius:8px; background:#fff; color:#0f172a; padding:9px 12px; font:700 12px Archivo,system-ui; cursor:pointer; } .order-controls button:hover { border-color:#7b8da4; } .order-controls button[aria-pressed="true"] { border-color:#4f7fbf; background:#e8eff8; color:#274c78; } .order-controls button:focus-visible { outline:3px solid #d39a3c; outline-offset:2px; } @media (max-width:760px) { #viz { top:310px; left:0; } } </style> </head><body> <header class="rail"><a class="wordmark" href="../index.html">OG<b>JS</b></a><nav> <a href="../docs.html">Docs</a><a href="../demos.html">Demos</a> <a href="./index.html" aria-current="page">Examples</a></nav></header> <div id="viz"></div> <section class="workflow-panel"><h1>Node paint order</h1> <p>Higher priorities paint and pick on top. Equal priorities keep stable insertion order.</p> <div class="order-controls"> <button data-promote="blue" aria-pressed="true">Promote blue</button> <button data-promote="amber" aria-pressed="false">Promote amber</button> <button data-promote="rose" aria-pressed="false">Promote rose</button> <button data-promote="tie" aria-pressed="false">Equal order</button> </div> <div class="status" id="order-status" aria-live="polite">Top: blue · hover: none · click: none</div> </section> <script type="module"> // `paintOrder` expresses which overlapping node should appear and pick on top. // OGJS keeps equal-priority insertion order stable and applies style rules without changing // topology, so applications never reorder render buffers manually. import { Ogjs } from '../../src/index'; const og = new Ogjs({ container: '#viz', theme: 'light' }); og.setGraph({ nodes: [ { id: 'blue', x: -12, y: 0, size: 38, color: '#4f7fbf', mark: 'square', paintOrder: 7, borderWidth: 2, borderColor: '#ffffff' }, { id: 'amber', x: 0, y: 0, size: 38, color: '#d39a3c', mark: 'circle', paintOrder: 2, borderWidth: 2, borderColor: '#ffffff' }, { id: 'rose', x: 12, y: 0, size: 38, color: '#c96b78', mark: 'diamond', paintOrder: 0, borderWidth: 2, borderColor: '#ffffff' }, ], edges: [], }); og.camera.fit({ target: 'all', paddingPx: 32, maxOccupancy: .18, retain: true }); const status = document.querySelector('#order-status'); const state = { top: 'blue', hover: null, click: null, mode: 'initial' }; og.styles.addNodeRule(({ id }) => { // A larger `paintOrder` moves a node above overlapping peers for rendering and picking without // reordering or replacing the underlying graph records. if (state.mode === 'initial') return undefined; return { paintOrder: state.mode === 'tie' ? 0 : id === state.mode ? 10 : 0 }; }); const update = () => { status.textContent = `Top: ${state.top} · hover: ${state.hover ?? 'none'} · click: ${state.click ?? 'none'}`; }; for (const button of document.querySelectorAll('[data-promote]')) { button.addEventListener('click', () => { state.mode = button.dataset.promote; state.top = state.mode === 'tie' ? 'rose' : state.mode; state.hover = null; state.click = null; og.styles.apply(); for (const control of document.querySelectorAll('[data-promote]')) { control.setAttribute('aria-pressed', String(control === button)); } update(); }); } og.on('nodehover', ({ nodeIndex }) => { state.hover = String(og.graph.getNodeId(nodeIndex)); update(); }); og.on('nodeout', () => { state.hover = null; update(); }); og.on('nodeclick', ({ nodeIndex }) => { // Click evidence confirms the visually top mark is also the picked mark; OGJS keeps draw and // hit-test order under the same declarative priority. state.click = String(og.graph.getNodeId(nodeIndex)); update(); }); </script></body></html>