Skip to main content

Custom element (no framework)

It runs anywhere plain DOM does β€” including live on this page:

Loading the element…

CDN one-pager​

A complete HTML file β€” no build step:

<!doctype html>
<html>
<head>
<script
type="module"
src="https://unpkg.com/@oh-just-another/diagram/dist/oja-diagram.global.js"
></script>
<style>
body {
margin: 0;
}
oja-diagram {
display: block;
height: 100vh;
}
</style>
</head>
<body>
<oja-diagram grid snap theme="dark"></oja-diagram>
<script type="module">
const el = document.querySelector("oja-diagram");
el.addEventListener("ready", () => el.zoomToFit());
el.addEventListener("scenechange", (e) => console.log("scene", e.detail));
el.addEventListener("selectionchange", (e) => console.log("selected", e.detail));
</script>
</body>
</html>

npm / ESM​

npm i @oh-just-another/diagram
import "@oh-just-another/diagram"; // side effect: registers <oja-diagram>
<oja-diagram grid theme="dark" style="height: 100vh"></oja-diagram>

Importing the package registers the element automatically. defineOjaDiagram(tag?) is exported for registering under a different tag name; calling it is otherwise unnecessary (it is idempotent).

API​

  • Attributes: theme (dark | light | system β€” themes the chrome; the canvas itself always stays light), renderer (canvas2d | webgl2 | offscreen, omit to auto-detect), grid / snap (boolean β€” attribute present = on).
  • Property: scene β€” a Scene object; assigning loads it (queued until the editor is ready), reading returns the current scene.
  • Events (CustomEvent, payload in event.detail): ready (detail.editor is the live engine), scenechange (the new Scene), selectionchange (array of selected element ids), themechange (the new theme).
  • Methods: getScene() / loadScene(scene), undo() / redo(), zoomToFit(), getActiveTool() / setActiveTool(tool), getSelection() / setSelection(ids).
  • Escape hatch: the editor property is the full live engine (null before ready) β€” same surface as in React: driving from code.
el.setActiveTool("draw-rect"); // "select" | "hand" | "draw-rect" | "draw-ellipse" | ...
el.loadScene(savedScene);
const editor = el.editor; // full engine after `ready`

The Vue / Svelte / Angular wrappers below are thin bindings over this exact element.