Vue3 Component for resizable and draggable elements
—
Comprehensive configuration options for Vue Draggable Resizable component behavior, styling, positioning, sizing, and constraints.
Control the CSS classes applied to the component in different states.
/**
* Styling configuration props for customizing component appearance
*/
interface StylingProps {
/** Base CSS class for the component (default: 'vdr') */
className: string;
/** CSS class when draggable is enabled (default: 'draggable') */
classNameDraggable: string;
/** CSS class when resizable is enabled (default: 'resizable') */
classNameResizable: string;
/** CSS class applied during dragging (default: 'dragging') */
classNameDragging: string;
/** CSS class applied during resizing (default: 'resizing') */
classNameResizing: string;
/** CSS class when component is active (default: 'active') */
classNameActive: string;
/** Base CSS class for resize handles (default: 'handle') */
classNameHandle: string;
}Usage Examples:
<template>
<!-- Custom CSS classes -->
<vue-draggable-resizable
class-name="my-draggable"
class-name-active="my-active"
class-name-handle="my-handle"
>
Content here
</vue-draggable-resizable>
</template>
<style>
.my-draggable {
border: 2px solid blue;
}
.my-active {
border-color: red;
}
.my-handle {
background: green;
}
</style>Enable or disable core functionality and control component interaction behavior.
/**
* Behavior control props for component interaction
*/
interface BehaviorProps {
/** Whether component is initially active (default: false) */
active: boolean;
/** Prevent deactivation when clicking outside (default: false) */
preventDeactivation: boolean;
/** Enable dragging functionality (default: true) */
draggable: boolean;
/** Enable resizing functionality (default: true) */
resizable: boolean;
/** Disable text selection during drag (default: true) */
disableUserSelect: boolean;
/** Enable browser native drag functionality (default: false) */
enableNativeDrag: boolean;
}Usage Examples:
<template>
<!-- Resizable only, no dragging -->
<vue-draggable-resizable
:draggable="false"
:resizable="true"
:active="true"
:prevent-deactivation="true"
>
I can only be resized
</vue-draggable-resizable>
<!-- Draggable only, no resizing -->
<vue-draggable-resizable
:draggable="true"
:resizable="false"
>
I can only be dragged
</vue-draggable-resizable>
</template>Control initial position, size, and z-index of the component.
/**
* Positioning and sizing props for component dimensions and location
*/
interface PositionSizeProps {
/** Initial X position in pixels (default: 0) */
x: number;
/** Initial Y position in pixels (default: 0) */
y: number;
/** Initial width in pixels or 'auto' (default: 200) */
w: number | 'auto';
/** Initial height in pixels or 'auto' (default: 200) */
h: number | 'auto';
/** Z-index value or 'auto' (default: 'auto') */
z: number | 'auto';
}Usage Examples:
<template>
<!-- Positioned element with auto dimensions -->
<vue-draggable-resizable
:x="100"
:y="50"
w="auto"
h="auto"
:z="999"
>
<div style="padding: 20px;">Auto-sized content</div>
</vue-draggable-resizable>
<!-- Fixed size positioned element -->
<vue-draggable-resizable
:x="200"
:y="100"
:w="300"
:h="200"
>
300x200 element
</vue-draggable-resizable>
</template>Define size and position constraints to limit component behavior.
/**
* Constraint props for limiting component size and position
*/
interface ConstraintProps {
/** Minimum width in pixels (default: 0) */
minWidth: number;
/** Minimum height in pixels (default: 0) */
minHeight: number;
/** Maximum width in pixels (default: null - no limit) */
maxWidth: number | null;
/** Maximum height in pixels (default: null - no limit) */
maxHeight: number | null;
/** Constrain movement and size to parent element (default: false) */
parent: boolean;
/** Grid snapping intervals [x, y] (default: [1, 1]) */
grid: [number, number];
/** Restrict drag to axis: 'x', 'y', or 'both' (default: 'both') */
axis: 'x' | 'y' | 'both';
}Usage Examples:
<template>
<div style="width: 800px; height: 600px; position: relative; border: 1px solid #ccc;">
<!-- Constrained to parent with size limits -->
<vue-draggable-resizable
:parent="true"
:min-width="100"
:min-height="80"
:max-width="400"
:max-height="300"
:grid="[20, 20]"
>
Constrained element
</vue-draggable-resizable>
<!-- Horizontal drag only -->
<vue-draggable-resizable
axis="x"
:y="200"
:w="150"
:h="100"
>
Horizontal only
</vue-draggable-resizable>
</div>
</template>Advanced behavior modification including aspect ratio, scaling, and handle customization.
/**
* Advanced configuration props for specialized behavior
*/
interface AdvancedProps {
/** Maintain aspect ratio during resize (default: false) */
lockAspectRatio: boolean;
/** Scale factor for parent transforms - number or [x, y] (default: 1) */
scale: number | [number, number];
/** Array of enabled resize handles (default: all 8 handles) */
handles: HandleType[];
/** CSS selector for elements that act as drag handles */
dragHandle?: string;
/** CSS selector for elements that prevent dragging */
dragCancel?: string;
}
/** Available resize handle positions */
type HandleType = 'tl' | 'tm' | 'tr' | 'mr' | 'br' | 'bm' | 'bl' | 'ml';Usage Examples:
<template>
<!-- Aspect ratio locked with limited handles -->
<vue-draggable-resizable
:lock-aspect-ratio="true"
:handles="['br', 'tr', 'mr']"
:w="200"
:h="150"
>
Aspect ratio preserved
</vue-draggable-resizable>
<!-- Custom drag handle -->
<vue-draggable-resizable
drag-handle=".drag-me"
drag-cancel=".no-drag"
>
<div class="drag-me" style="cursor: move; background: #eee; padding: 10px;">
Drag here
</div>
<div class="no-drag">
Can't drag this part
</div>
</vue-draggable-resizable>
<!-- Scaled parent handling -->
<div style="transform: scale(0.8);">
<vue-draggable-resizable :scale="0.8">
Handles parent scaling
</vue-draggable-resizable>
</div>
</template>Function props that allow intercepting and controlling drag/resize operations. These callbacks are invoked during interaction and can prevent or modify behavior by returning false.
/**
* Callback props for controlling drag and resize behavior
*/
interface CallbackProps {
/**
* Called before drag starts - return false to cancel drag operation
* @param event - The originating mouse or touch event
* @returns boolean | void - Return false to prevent dragging
* @default () => true
*/
onDragStart?: (event: MouseEvent | TouchEvent) => boolean | void;
/**
* Called during drag with new position - return false to cancel current movement
* @param x - New X position in pixels
* @param y - New Y position in pixels
* @returns boolean | void - Return false to cancel this drag movement
* @default () => true
*/
onDrag?: (x: number, y: number) => boolean | void;
/**
* Called before resize starts - return false to cancel resize operation
* @param handle - The resize handle being used
* @param event - The originating mouse or touch event
* @returns boolean | void - Return false to prevent resizing
* @default () => true
*/
onResizeStart?: (handle: HandleType, event: MouseEvent | TouchEvent) => boolean | void;
/**
* Called during resize with new dimensions - return false to cancel current resize
* @param handle - The resize handle being used
* @param x - New X position in pixels
* @param y - New Y position in pixels
* @param width - New width in pixels
* @param height - New height in pixels
* @returns boolean | void - Return false to cancel this resize movement
* @default () => true
*/
onResize?: (handle: HandleType, x: number, y: number, width: number, height: number) => boolean | void;
}Usage Examples:
<template>
<vue-draggable-resizable
:on-drag-start="checkDragStart"
:on-drag="constrainDrag"
:on-resize-start="checkResizeStart"
:on-resize="validateResize"
>
Controlled element
</vue-draggable-resizable>
</template>
<script>
export default {
data() {
return {
isDragLocked: false,
minArea: 5000,
maxBounds: { x: 500, y: 400 }
};
},
methods: {
checkDragStart(event) {
console.log('Drag starting:', event.type);
// Return false to prevent dragging based on conditions
if (this.isDragLocked) {
console.log('Drag prevented - element is locked');
return false;
}
return true;
},
constrainDrag(x, y) {
// Only allow dragging within certain bounds
if (x < 0 || y < 0 || x > this.maxBounds.x || y > this.maxBounds.y) {
console.log('Drag constrained:', x, y);
return false; // Cancel this drag movement
}
},
checkResizeStart(handle, event) {
console.log('Resize starting with handle:', handle);
// Allow resize only on weekdays (example business logic)
const isWeekday = new Date().getDay() >= 1 && new Date().getDay() <= 5;
if (!isWeekday) {
console.log('Resize prevented - not a weekday');
return false;
}
},
validateResize(handle, x, y, width, height) {
// Prevent resize if area becomes too small
const area = width * height;
if (area < this.minArea) {
console.log('Resize prevented - area too small:', area);
return false;
}
// Log valid resize operations
console.log(`Resize via ${handle}:`, { x, y, width, height });
}
}
}
</script>Advanced Callback Patterns:
<template>
<vue-draggable-resizable
:on-drag="throttledDragCallback"
:on-resize="debouncedResizeCallback"
>
Performance-optimized callbacks
</vue-draggable-resizable>
</template>
<script>
import { throttle, debounce } from 'lodash';
export default {
methods: {
// Throttled drag callback for performance
throttledDragCallback: throttle(function(x, y) {
// Update external systems at most every 100ms
this.updateServerPosition(x, y);
}, 100),
// Debounced resize callback to batch updates
debouncedResizeCallback: debounce(function(handle, x, y, width, height) {
// Save to database only after resize stops for 300ms
this.saveElementDimensions({ x, y, width, height });
}, 300)
}
}
</script>Install with Tessl CLI
npx tessl i tessl/npm-vue-draggable-resizable