VSTDOCSv0.0.17

DOCS / UI REFERENCE

Events & gestures

The RootView hit-tests every mouse event into the shadow tree with DOM-style bubbling, maintains hover chains, and applies hover:/active: style layers natively — no JS round-trip for a hover repaint.

Pointer events

PROPPAYLOADNOTES
onClickFires on release over the pressed node.
onDoubleClickThe DAW reset gesture — built-in controls use it for reset-to-default.
onWheel{dy}Controls get first refusal on the wheel; scroll containers keep it otherwise. dy is the notch fraction (~0.1/notch).
onMouseEnter / onMouseLeaveHover chain, parent-to-child, like the DOM.
onMouseDown / onMouseUpDrive active: styles.
onDragStart / onDrag / onDragEnd{dx, dy, x, y}Deltas from the drag origin plus the live pointer position, in root coordinates — everything a knob or an XY pad needs.

Drag gestures

The built-in knob maps vertical drag to value with dragToValue(startValue, dy). Rolling your own control is a few lines:

a custom vertical fader
function Fader({ value, onChange }) {
  const start = useRef(0);

  return (
    <View
      className="w-10 h-40 bg-zinc-900 rounded-lg cursor-pointer"
      onDragStart={() => { start.current = value; }}
      onDrag={(e) => onChange(clamp01(start.current - e.dy / 160))}
    >
      <View
        className="absolute left-0 right-0 bottom-0 bg-lime-400 rounded-lg"
        style={{ height: `${value * 100}%` }}
      />
    </View>
  );
}

Scroll containers

Give a View overflow-y-scroll and a bounded height: children lay out at full size, the mouse wheel scrolls, the painter clips and draws a thumb. Set the scrollTop prop to reset the offset programmatically (say, when switching tabs).

TSX
<View className="flex-1 overflow-y-scroll gap-2 p-3" scrollTop={0}>
  {items.map((item) => <Row key={item.id} item={item} />)}
</View>

Layout feedback — onLayout

Layout happens in C++ (Yoga), so JS normally never knows where anything landed. onLayout closes the loop: it fires with the node’s root-space rect (scroll-adjusted) whenever layout moves or resizes it — and only when the rect actually changes. This is the foundation for menus, tooltips, and popovers.

TSX
import { useLayoutRect, useOverlay } from "@vsreact/core";

function InfoTip({ children }) {
  const [rect, onLayout] = useLayoutRect();
  const overlay = useOverlay();

  return (
    <View
      onLayout={onLayout}
      onMouseEnter={() => rect && overlay.show(
        <View className="absolute rounded-lg bg-zinc-800 px-3 py-2"
              style={{ left: rect.x, top: rect.y + rect.height + 4 }}>
          <Text className="text-[11]">{children}</Text>
        </View>
      )}
      onMouseLeave={() => overlay.hide()}
    >
      <Text className="text-faint">?</Text>
    </View>
  );
}
  • useLayoutRect()[rect, onLayout] sugar for capturing the rect in state.
  • useOverlay() — a slot in the overlay layer, which render() mounts after your app automatically so overlay content paints on top and receives input first. show(node) / hide(); unmount cleans up.
  • The built-in <Select> is built entirely from these parts.

Cursors

Cursors come from classes — cursor-pointer, cursor-text, cursor-default — applied per-node by the hit-tester as the mouse moves.