Skip to content

Nesting

vdr supports two nesting styles: recursive data nesting through childrens, and template nesting through the default slot. Use childrens when your editor is driven by a JSON-like schema; use slots when you compose each level by hand.

Recursive Nesting with childrens

vue
<template>
  <vdr v-bind="root" />
</template>

<script setup lang="ts">
const root = {
  w: 400, h: 300, x: 40, y: 40, r: 0,
  uuid: 'root',
  childrens: [
    {
      w: 160, h: 100, x: 40, y: 40, r: 15,
      uuid: 'child-1',
    },
    {
      w: 100, h: 100, x: 240, y: 140, r: -10, lock: true,
      uuid: 'child-2',
      // Children can also define their own childrens.
      childrens: [
        { w: 60, h: 60, x: 20, y: 20, uuid: 'grand-1' },
      ],
    },
  ],
}
</script>

Child operations follow the parent's rotated coordinate system automatically, so you do not need to convert angles manually.

overflow for Clipping Children

By default, children are not clipped when they exceed the parent. Pass overflow to create a .childWrap with the requested overflow behavior:

vue
<vdr
  :w="300" :h="200" :x="40" :y="40"
  overflow="hidden"
  :childrens="[{ w: 200, h: 160, x: 60, y: 30, r: 20 }]"
/>

Common values are 'hidden', 'auto', and 'scroll'.

childWrapAttr

Use childWrapAttr when you need more control over the wrapper around child nodes, such as class, inline style, or other attributes:

vue
<vdr
  :w="300" :h="200" :x="40" :y="40"
  :child-wrap-attr="{
    class: 'my-wrap',
    style: { borderRadius: '8px', overflow: 'hidden' },
  }"
  :childrens="[{ w: 200, h: 160, x: 60, y: 30 }]"
/>

When both overflow and childWrapAttr are provided, overflow is merged into childWrapAttr.style.

Slot Nesting

vue
<vdr :w="320" :h="220" :x="30" :y="30">
  <vdr :w="140" :h="100" :x="40" :y="40">
    <div>child via slot</div>
  </vdr>
</vdr>

You can mix both styles: render part of the tree from childrens, then place hand-written vdr nodes in the slot.

Handling Child Events

Vue component events emitted by recursive childrens do not automatically bubble to the root vdr. To handle events from data-driven children, pass listeners in the child config with Vue's onEventName object syntax:

vue
<template>
  <vdr v-bind="root" @dragging="onRootDragging" />
</template>

<script setup lang="ts">
function onAnyChildChange(pos) {
  console.log(pos.uuid, pos.x, pos.y)
}

function onRootDragging(pos) {
  console.log('root', pos)
}

const root = {
  uuid: 'root',
  w: 320, h: 220, x: 30, y: 30,
  childrens: [
    {
      uuid: 'child-1',
      w: 140, h: 100, x: 30, y: 30,
      onDragging: onAnyChildChange,
      onResizing: onAnyChildChange,
      onRotating: onAnyChildChange,
    },
  ],
}
</script>

For slot nesting, bind listeners directly on each nested <vdr>.