Vue3 Component for resizable and draggable elements
—
Event system providing real-time feedback for drag and resize operations, plus lifecycle events for component activation state.
Events related to component activation state and user interaction.
/**
* Component lifecycle events
*/
interface LifecycleEvents {
/** Emitted when component becomes active (gains focus/selection) */
activated(): void;
/** Emitted when component becomes inactive (loses focus/selection) */
deactivated(): void;
/** Emitted for v-model support when active state changes */
'update:active'(active: boolean): void;
}Usage Examples:
<template>
<vue-draggable-resizable
@activated="onActivated"
@deactivated="onDeactivated"
:prevent-deactivation="preventDeactivate"
>
Click me to activate
</vue-draggable-resizable>
</template>
<script>
export default {
data() {
return {
isActive: false,
preventDeactivate: false
};
},
methods: {
onActivated() {
console.log('Component activated');
this.isActive = true;
// Show selection UI, enable keyboard shortcuts, etc.
},
onDeactivated() {
console.log('Component deactivated');
this.isActive = false;
// Hide selection UI, disable keyboard shortcuts, etc.
}
}
}
</script>V-Model Support:
<template>
<!-- Two-way binding with active state -->
<vue-draggable-resizable
v-model:active="isElementActive"
>
Element with v-model binding
</vue-draggable-resizable>
<p>Element is {{ isElementActive ? 'active' : 'inactive' }}</p>
</template>
<script>
export default {
data() {
return {
isElementActive: false
};
}
}
</script>Events fired during drag operations providing position feedback.
/**
* Drag operation events with position coordinates
*/
interface DragEvents {
/**
* Emitted continuously during drag operation
* @param left - Current X position (left coordinate)
* @param top - Current Y position (top coordinate)
*/
dragging(left: number, top: number): void;
/**
* Emitted when drag operation completes
* @param left - Final X position (left coordinate)
* @param top - Final Y position (top coordinate)
*/
dragStop(left: number, top: number): void;
}Usage Examples:
<template>
<vue-draggable-resizable
@dragging="onDragging"
@dragStop="onDragStop"
:x="elementX"
:y="elementY"
>
Drag me around
</vue-draggable-resizable>
<div>Position: {{ elementX }}, {{ elementY }}</div>
</template>
<script>
export default {
data() {
return {
elementX: 100,
elementY: 50,
dragHistory: []
};
},
methods: {
onDragging(left, top) {
// Update position display in real-time
this.elementX = left;
this.elementY = top;
// Track drag path
this.dragHistory.push({ x: left, y: top, timestamp: Date.now() });
// Send position updates to server
this.throttledPositionUpdate(left, top);
},
onDragStop(left, top) {
console.log(`Drag completed at: ${left}, ${top}`);
// Save final position
this.savePosition(left, top);
// Clear drag history
this.dragHistory = [];
},
throttledPositionUpdate: _.throttle(function(x, y) {
// Update server every 100ms during drag
this.$emit('position-changed', { x, y });
}, 100)
}
}
</script>Events fired during resize operations providing size and position feedback.
/**
* Resize operation events with dimension and position data
*/
interface ResizeEvents {
/**
* Emitted continuously during resize operation
* @param left - Current X position (left coordinate)
* @param top - Current Y position (top coordinate)
* @param width - Current width
* @param height - Current height
*/
resizing(left: number, top: number, width: number, height: number): void;
/**
* Emitted when resize operation completes
* @param left - Final X position (left coordinate)
* @param top - Final Y position (top coordinate)
* @param width - Final width
* @param height - Final height
*/
resizeStop(left: number, top: number, width: number, height: number): void;
}Usage Examples:
<template>
<vue-draggable-resizable
@resizing="onResizing"
@resizeStop="onResizeStop"
:w="elementWidth"
:h="elementHeight"
>
<div>Size: {{ elementWidth }}x{{ elementHeight }}</div>
</vue-draggable-resizable>
</template>
<script>
export default {
data() {
return {
elementWidth: 200,
elementHeight: 150,
minArea: 5000,
maxArea: 50000
};
},
methods: {
onResizing(left, top, width, height) {
// Update size display in real-time
this.elementWidth = width;
this.elementHeight = height;
// Validate size constraints
const area = width * height;
if (area < this.minArea) {
console.warn('Element too small!');
} else if (area > this.maxArea) {
console.warn('Element too large!');
}
// Update layout of other elements
this.updateLayout({ left, top, width, height });
},
onResizeStop(left, top, width, height) {
console.log(`Resize completed: ${width}x${height} at ${left},${top}`);
// Save dimensions
this.saveDimensions(width, height);
// Trigger layout recalculation
this.$nextTick(() => {
this.recalculateLayout();
});
},
updateLayout(bounds) {
// Update other components based on new size
this.$emit('element-resized', bounds);
}
}
}
</script>Common patterns for handling component events effectively.
/**
* Common event handling patterns and utilities
*/
interface EventPatterns {
/** Debounced event handler to reduce update frequency */
debouncedHandler: (delay: number, callback: Function) => Function;
/** Event handler with validation and constraints */
constrainedHandler: (validator: Function, callback: Function) => Function;
/** Event handler with state synchronization */
syncedHandler: (stateProperty: string, callback: Function) => Function;
}Usage Examples:
<template>
<vue-draggable-resizable
@dragging="debouncedDragHandler"
@resizing="constrainedResizeHandler"
@activated="syncActivationState"
>
Advanced event handling
</vue-draggable-resizable>
</template>
<script>
import { debounce } from 'lodash';
export default {
data() {
return {
position: { x: 0, y: 0 },
size: { width: 200, height: 150 },
isActive: false
};
},
methods: {
// Debounced drag handler to reduce server calls
debouncedDragHandler: debounce(function(left, top) {
this.position = { x: left, y: top };
this.saveToServer();
}, 300),
constrainedResizeHandler(left, top, width, height) {
// Apply business logic constraints
const aspectRatio = this.originalWidth / this.originalHeight;
const constrainedHeight = width / aspectRatio;
if (Math.abs(height - constrainedHeight) > 10) {
// Emit correction event if aspect ratio is wrong
this.$emit('aspect-ratio-corrected', {
original: { width, height },
corrected: { width, height: constrainedHeight }
});
}
this.size = { width, height };
},
syncActivationState() {
this.isActive = true;
// Sync with global state management
this.$store.commit('setActiveElement', this.$el);
}
}
}
</script>Install with Tessl CLI
npx tessl i tessl/npm-vue-draggable-resizable