useOpenDraw() and custom UI

Read and drive the whiteboard from any Vue component with useOpenDraw(), or render only the canvas and build your own toolbar around it.

useOpenDraw()

Every component inside <OpenDraw> shares one reactive editor context. Call useOpenDraw() from any descendant to read state as computed refs and call commands. Every write goes through the document, so it is undoable and fires @change.

PaintButton.vue
// Inside <OpenDraw> (any descendant component):
const { selectedElements, changeStyle } = useOpenDraw()

function paintRed() {
  changeStyle({ strokeColor: '#e03131' })
}
GroupMembers
Stateelements, selectedElements, appState, activeTool, theme, viewport, gridSize, editingId, canUndo, canRedo
Tools & themesetActiveTool(tool), toggleToolLock(), setTheme(theme), toggleTheme(), setGridSize(size), setViewBackgroundColor(color)
Selectionselect(ids, mode?), selectGrouped(ids), selectAll(), isIdSelected(id)
ElementsupdateElement(id, patch), deleteSelection(), duplicateSelection(), copySelection(), cutSelection(), paste(at?)
StylechangeStyle(style), changeTextStyle(style), changeRoundness(r), changeArrowhead(end, head), changeArrowType(type), copyStyles(), pasteStyles()
ArrangegroupSelection(), ungroupSelection(), reorderSelection(action), alignSelection(a), distributeSelection(axis), flipSelection(axis)
Historyundo(), redo()
ViewportzoomIn(at), zoomOut(at), zoomAt(at, zoom), resetZoom(), zoomToFitAll(), zoomToFitSelection(), panBy(dx, dy)
Filesserialize(), toJSON(), loadFile(file), loadJSON(text), saveToFile(), openFile(), exportPNG(), exportSVG(), exportDataURL(), clearCanvas()
ImagesinsertImageFiles(files, at?), openImagePicker()

editor exposes the framework-free controller and doc the raw document, for anything the context does not cover.

Custom UI

Want your own chrome? Render <OpenDrawCanvas> under a context you provide with createOpenDrawContext() and provideOpenDraw(), then build any toolbar you like. The canvas keeps every gesture, shortcut and the text editor; you only draw the buttons. (Or keep <OpenDraw ui="none"> for a bare canvas with persistence.)

MyBoard.vue
<script setup lang="ts">
// Provide one context, then render the canvas and your own chrome.
const ctx = createOpenDrawContext({ appState: { theme: 'light' } })
provideOpenDraw(ctx)

const tools = ['selection', 'rectangle', 'ellipse', 'arrow', 'text'] as const
</script>

<template>
  <div class="board">
    <OpenDrawCanvas />
    <nav class="my-toolbar">
      <button
        v-for="tool in tools"
        :key="tool"
        :aria-pressed="ctx.activeTool.value === tool"
        @click="ctx.setActiveTool(tool)"
      >{{ tool }}</button>
      <button :disabled="!ctx.canUndo.value" @click="ctx.undo()">Undo</button>
      <button @click="ctx.exportPNG('board')">Export</button>
    </nav>
  </div>
</template>