Skip to main content

Vue composables

Reactive composables for accessing board state inside Vue components.

All composables must be called inside a component that is a descendant of <BoardRoot>. They inject the board engine context via Vue's provide/inject system.

useBoardEngine

Returns the full board engine context. This is the primary composable — all other composables are thin wrappers over this.

ts
function useBoardEngine(): BoardEngineContext

Return type

PropertyTypeDescription
engineBoardEngineThe engine instance for calling commands.
rootElementRef<HTMLElement | null>The BoardRoot DOM element.
viewportSizeRef<Point>Current viewport dimensions in pixels.
renderersShallowRef<BoardRendererRegistry>Registered custom renderers.
resolvedGridComputedRef<ResolvedBoardGridOptions>Resolved grid options (engine defaults merged with BoardRoot props).
toLocalPoint(clientX, clientY) => PointConverts page coordinates to board-local coordinates.
$cameraShallowRef<Camera>Reactive camera state.
$gridShallowRef<GridSettings>Reactive grid state.
$nodesShallowRef<ReadonlyMap<NodeId, BoardNode>>Reactive node map.
$selectionShallowRef<ReadonlySet<NodeId>>Reactive selection set.
$interactionShallowRef<InteractionState>Reactive interaction state.
$snapGuidesShallowRef<readonly SnapGuide[]>Reactive snap guides.
vue
<script setup lang="ts">
import { useBoardEngine } from '@lupinum/vue-board'

const { engine, $camera, $nodes } = useBoardEngine()

// Read the current camera
console.log($camera.value.z) // zoom level

// Call engine commands
engine.selectAll()
</script>

useBoardCamera

Returns a computed ref of the current camera state.

ts
function useBoardCamera(): ComputedRef<Camera>
vue
<script setup lang="ts">
import { useBoardCamera } from '@lupinum/vue-board'

const camera = useBoardCamera()
</script>

<template>
  <div>Zoom: {{ Math.round(camera.z * 100) }}%</div>
</template>

useBoardNodes

Returns a computed ref of the node map.

ts
function useBoardNodes(): ComputedRef<ReadonlyMap<NodeId, BoardNode>>
vue
<script setup lang="ts">
import { useBoardNodes } from '@lupinum/vue-board'

const nodes = useBoardNodes()
const nodeCount = computed(() => nodes.value.size)
</script>

useBoardSelection

Returns a computed ref of the currently selected node IDs as an array.

ts
function useBoardSelection(): ComputedRef<readonly NodeId[]>
vue
<script setup lang="ts">
import { useBoardSelection } from '@lupinum/vue-board'

const selection = useBoardSelection()
</script>

<template>
  <div>{{ selection.length }} selected</div>
</template>

useBoardInteraction

Returns a computed ref of the current interaction state.

ts
function useBoardInteraction(): ComputedRef<InteractionState>
vue
<script setup lang="ts">
import { useBoardInteraction } from '@lupinum/vue-board'

const interaction = useBoardInteraction()
const isDragging = computed(() => interaction.value.mode === 'dragging-nodes')
</script>

useBoardVisibleBounds

Returns a computed ref of the current viewport bounds in world-space coordinates.

ts
function useBoardVisibleBounds(): ComputedRef<Bounds>
vue
<script setup lang="ts">
import { useBoardVisibleBounds } from '@lupinum/vue-board'

const bounds = useBoardVisibleBounds()
// bounds.value = { minX, minY, maxX, maxY }
</script>

useBoardVisibleNodes

Returns a computed ref of all visible nodes within the viewport, with an optional culling margin.

ts
function useBoardVisibleNodes(
  margin?: number,
): ComputedRef<readonly BoardNode[]>
ParameterTypeDefaultDescription
marginnumber200Extra world-space margin around the viewport for culling.
vue
<script setup lang="ts">
import { useBoardVisibleNodes } from '@lupinum/vue-board'

const visible = useBoardVisibleNodes(300) // 300 world-unit margin
</script>

<template>
  <div>{{ visible.length }} nodes visible</div>
</template>

useBoardGridStyle

Returns a computed CSS variable object for rendering the grid. Used internally by BoardGrid but available for custom grid implementations.

ts
function useBoardGridStyle(): ComputedRef<Record<string, string>>

Returned CSS variables

VariableDescription
--grid-minor-sizeScreen-space minor grid step.
--grid-major-sizeScreen-space major grid step.
--grid-minor-xMinor grid horizontal offset.
--grid-minor-yMinor grid vertical offset.
--grid-major-xMajor grid horizontal offset.
--grid-major-yMajor grid vertical offset.
--grid-minor-colorMinor grid line color (with computed opacity).
--grid-major-colorMajor grid line color (with computed opacity).
--grid-mask-imageEdge fade mask (radial gradient or none).

Grid opacity automatically reduces at low zoom levels (minor lines fade below 12px screen step, disappear below 6px).


useBoardNode

Returns reactive state and actions for a single node. Ideal for custom renderer components.

ts
function useBoardNode(id: MaybeRefOrGetter<NodeId>): {
  node: ComputedRef<BoardNode>
  selected: ComputedRef<boolean>
  editing: ComputedRef<boolean>
  locked: ComputedRef<boolean>
  style: ComputedRef<CSSProperties>
  beginEdit: () => void
  commitText: (text: string) => void
  startDrag: (event: PointerEvent) => void
  startResize: (handle: ResizeHandle, event: PointerEvent) => void
}

Return type

PropertyTypeDescription
nodeComputedRef<BoardNode>The reactive node data. Throws if the node is removed.
selectedComputedRef<boolean>Whether the node is selected.
editingComputedRef<boolean>Whether the node is being text-edited.
lockedComputedRef<boolean>Whether the node is locked.
styleComputedRef<CSSProperties>CSS position/size/zIndex for absolute positioning.
beginEdit() => voidStart text editing when this is a text node.
commitText(text: string) => voidCommit text when this is a text node.
startDrag(event: PointerEvent) => voidBegin dragging this node from a pointer event.
startResize(handle, event) => voidBegin resizing this node from a handle and pointer event.
vue
<script setup lang="ts">
import { useBoardNode } from '@lupinum/vue-board'
import type { NodeId } from '@lupinum/board-core'

const props = defineProps<{ nodeId: NodeId }>()
const { node, selected, style, beginEdit } = useBoardNode(() => props.nodeId)
</script>

<template>
  <div :style="style" :class="{ selected }" @dblclick="beginEdit">
    {{ node.text }}
  </div>
</template>

useBoardBoxSelectBounds

Returns a computed ref of the current box selection bounds in screen-space, or null if no box selection is active.

ts
function useBoardBoxSelectBounds(): ComputedRef<Bounds | null>
vue
<script setup lang="ts">
import { useBoardBoxSelectBounds } from '@lupinum/vue-board'

const bounds = useBoardBoxSelectBounds()
</script>

<template>
  <div
    v-if="bounds"
    class="custom-box-select"
    :style="{
      left: bounds.minX + 'px',
      top: bounds.minY + 'px',
      width: bounds.maxX - bounds.minX + 'px',
      height: bounds.maxY - bounds.minY + 'px',
    }"
  />
</template>