controls
Define which areas can initiate dragging
The controls
plugin defines which parts of an element can be used to start dragging. Think modal headers, card handles, or toolbar grips - only specific areas should be draggable while the rest remains interactive.
controls({ allow: ControlFrom.selector('.handle') }); // Only handle can drag
controls({ block: ControlFrom.selector('.button') }); // Buttons can't drag
controls({
allow: ControlFrom.selector('.header'),
block: ControlFrom.selector('.close'),
}); // Header drags, close button doesn't
Basic Usage
<script setup>
import { controls, ControlFrom, vDraggable } from '@neodrag/vue';
const allowControls = [
controls({ allow: ControlFrom.selector('.header') }),
];
const blockControls = [
controls({ block: ControlFrom.selector('button') }),
];
</script>
<template>
<div>
<div v-draggable="allowControls">
<div class="header">Drag handle</div>
<div class="content">Content (not draggable)</div>
</div>
<div v-draggable="blockControls">
<p>Drag anywhere in this area</p>
<button>Can't drag from here</button>
</div>
</div>
</template>
ControlFrom Utilities
Selector-Based Controls
<script setup>
import { controls, ControlFrom, vDraggable } from '@neodrag/vue';
const singleAllow = [
controls({ allow: ControlFrom.selector('.handle') }),
];
const multiAllow = [
controls({ allow: ControlFrom.selector('.handle, .title, .grip') }),
];
const blockSome = [
controls({ block: ControlFrom.selector('button, input, select') }),
];
</script>
<template>
<div>
<div v-draggable="singleAllow">
<div class="handle">Drag me</div>
<div>Not draggable</div>
</div>
<div v-draggable="multiAllow">
<div class="title">Title bar</div>
<div class="handle">Handle</div>
<div class="grip">⋮⋮</div>
<div class="content">Content</div>
</div>
<div v-draggable="blockSome">
<p>Draggable area</p>
<button>Button (blocked)</button>
<input placeholder="Input (blocked)" />
</div>
</div>
</template>
Element-Based Controls
<script setup>
import { ref } from 'vue';
import { controls, ControlFrom, vDraggable } from '@neodrag/vue';
const handleElement = ref();
const blockElement = ref();
const allowControls = () => [
controls({
allow: () => ControlFrom.elements([handleElement.value]),
}),
];
const blockControls = () => [
controls({
block: () => ControlFrom.elements([blockElement.value]),
}),
];
</script>
<template>
<div>
<div v-draggable="allowControls">
<div ref="handleElement">Specific handle element</div>
<div>Not draggable</div>
</div>
<div v-draggable="blockControls">
<p>Draggable area</p>
<div ref="blockElement">Blocked element</div>
</div>
</div>
</template>
Advanced Control Logic
Priority Handling
<script setup>
import { controls, ControlFrom, vDraggable } from '@neodrag/vue';
const allowWins = [
controls({
allow: ControlFrom.selector('.drag-zone'),
block: ControlFrom.selector('.no-drag'),
priority: 'allow',
}),
];
const blockWins = [
controls({
allow: ControlFrom.selector('.drag-zone'),
block: ControlFrom.selector('.no-drag'),
priority: 'block',
}),
];
</script>
<template>
<div>
<div v-draggable="allowWins">
<div class="drag-zone">
Drag zone
<div class="no-drag">Allow wins here</div>
</div>
</div>
<div v-draggable="blockWins">
<div class="drag-zone">
Drag zone
<div class="no-drag">Block wins here</div>
</div>
</div>
</div>
</template>
Real-World Examples
Modal with Header Handle
<script setup>
import {
vDraggable,
controls,
ControlFrom,
bounds,
BoundsFrom,
} from '@neodrag/vue';
const modalPlugins = [
controls({
allow: ControlFrom.selector('.modal-header'),
block: ControlFrom.selector('.modal-close, .modal-body button'),
}),
bounds(BoundsFrom.viewport()),
];
</script>
<template>
<div v-draggable="modalPlugins">
<div class="modal-header">
<h3>Modal Title</h3>
<button class="modal-close">×</button>
</div>
<div class="modal-body">
<p>Modal content</p>
<button>Action Button</button>
</div>
</div>
</template>
How It Works
The controls plugin manages drag initiation permissions:
- Finds control zones on setup by querying the DOM
- Checks click position in
shouldStart
hook - Determines permission based on which zones contain the click point
- Returns boolean to allow/prevent drag start
Zone priority logic:
- If
allow
zones exist and click is outside all of them → block - If click is in both allow and block zones → use
priority
option - Smaller (nested) zones take precedence over larger ones
API Reference
function controls(options?: {
allow?: ReturnType<
typeof ControlFrom.selector | typeof ControlFrom.elements
>;
block?: ReturnType<
typeof ControlFrom.selector | typeof ControlFrom.elements
>;
priority?: 'allow' | 'block';
}): Plugin;
Options:
allow
- Control zones that can initiate draggingblock
- Control zones that cannot initiate draggingpriority
- Which wins when zones overlap (‘allow’ | ‘block’)
ControlFrom utilities:
ControlFrom.selector(cssSelector)
- Elements matching CSS selectorControlFrom.elements(nodeList)
- Specific DOM elements
Returns: A plugin object for use with draggable.