CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-vue-draggable-resizable

Vue3 Component for resizable and draggable elements

Pending
Overview
Eval results
Files

advanced-features.mddocs/

Advanced Features

Advanced functionality including aspect ratio locking, handle customization, programmatic control methods, and specialized configuration options.

Capabilities

Aspect Ratio Control

Lock aspect ratio during resize operations to maintain proportional dimensions.

/**
 * Aspect ratio locking configuration
 */
interface AspectRatioControl {
  /** Enable aspect ratio locking (default: false) */
  lockAspectRatio: boolean;
}

Usage Examples:

<template>
  <!-- Image with locked aspect ratio -->
  <vue-draggable-resizable
    :lock-aspect-ratio="true"
    :w="300"
    :h="200"
  >
    <img src="photo.jpg" style="width: 100%; height: 100%; object-fit: cover;" />
  </vue-draggable-resizable>
  
  <!-- Video player with 16:9 ratio -->
  <vue-draggable-resizable
    :lock-aspect-ratio="true"
    :w="480"
    :h="270"
    :min-width="320"
    :min-height="180"
  >
    <video controls style="width: 100%; height: 100%;">
      <source src="video.mp4" type="video/mp4">
    </video>
  </vue-draggable-resizable>
</template>

Handle Customization

Configure which resize handles are available and customize their appearance and behavior.

/**
 * Resize handle configuration and types
 */
interface HandleCustomization {
  /** Array of enabled resize handles (default: all 8 handles) */
  handles: HandleType[];
  
  /** Base CSS class for handles, used to create handle-specific classes */
  classNameHandle: string;
}

/** Available resize handle positions */
type HandleType = 'tl' | 'tm' | 'tr' | 'mr' | 'br' | 'bm' | 'bl' | 'ml';

/** Handle position meanings */
interface HandlePositions {
  'tl': 'top-left';     // Corner: top-left
  'tm': 'top-middle';   // Edge: top-middle  
  'tr': 'top-right';    // Corner: top-right
  'mr': 'middle-right'; // Edge: middle-right
  'br': 'bottom-right'; // Corner: bottom-right
  'bm': 'bottom-middle';// Edge: bottom-middle
  'bl': 'bottom-left';  // Corner: bottom-left
  'ml': 'middle-left';  // Edge: middle-left
}

Usage Examples:

<template>
  <!-- Only corner handles -->
  <vue-draggable-resizable
    :handles="['tl', 'tr', 'br', 'bl']"
    class-name-handle="corner-handle"
  >
    Corner resize only
  </vue-draggable-resizable>
  
  <!-- Only right and bottom handles -->
  <vue-draggable-resizable
    :handles="['mr', 'br', 'bm']"
  >
    Expand right and down only
  </vue-draggable-resizable>
  
  <!-- Custom handle slots -->
  <vue-draggable-resizable :handles="['br']">
    <template #br>
      <div class="custom-handle">
        <svg width="10" height="10">
          <path d="M0,0 L10,10 M0,10 L10,0" stroke="black" />
        </svg>
      </div>
    </template>
    Custom handle appearance
  </vue-draggable-resizable>
</template>

<style>
.corner-handle {
  border-radius: 50%;
  background: #ff6b6b;
}

.custom-handle {
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.7);
  border-radius: 3px;
}
</style>

Scale Factor Support

Handle parent element scaling transformations correctly.

/**
 * Scale factor configuration for transformed parent elements
 */
interface ScaleSupport {
  /** Scale factor - single number or [scaleX, scaleY] array (default: 1) */
  scale: number | [number, number];
}

Usage Examples:

<template>
  <!-- Parent container with CSS transform scale -->
  <div class="scaled-container">
    <vue-draggable-resizable
      :scale="0.75"
      :w="200"
      :h="150"
    >
      Handles parent scaling
    </vue-draggable-resizable>
  </div>
  
  <!-- Different X and Y scale factors -->
  <div class="stretched-container">
    <vue-draggable-resizable
      :scale="[0.8, 1.2]"
    >
      Different X/Y scaling
    </vue-draggable-resizable>
  </div>
</template>

<style>
.scaled-container {
  transform: scale(0.75);
  transform-origin: top left;
}

.stretched-container {
  transform: scaleX(0.8) scaleY(1.2);
  transform-origin: top left;
}
</style>

Custom Drag Handles

Specify custom elements within the component that act as drag handles or prevent dragging.

/**
 * Custom drag handle configuration
 */
interface CustomDragHandles {
  /** CSS selector for elements that act as drag handles */
  dragHandle?: string;
  
  /** CSS selector for elements that prevent dragging when clicked */
  dragCancel?: string;
}

Usage Examples:

<template>
  <!-- Custom drag handle -->
  <vue-draggable-resizable
    drag-handle=".drag-handle"
    drag-cancel=".no-drag"
  >
    <div class="drag-handle" style="background: #eee; padding: 10px; cursor: move;">
      📐 Drag me here
    </div>
    <div style="padding: 10px;">
      <p>Main content area</p>
      <button class="no-drag">Don't drag when clicking this button</button>
    </div>
  </vue-draggable-resizable>
  
  <!-- Multiple drag handles -->
  <vue-draggable-resizable drag-handle=".handle">
    <div class="handle title-bar">Title Bar (drag here)</div>
    <div>Content area</div>
    <div class="handle status-bar">Status Bar (or here)</div>
  </vue-draggable-resizable>
</template>

<style>
.drag-handle {
  cursor: move;
  user-select: none;
}

.title-bar {
  background: #333;
  color: white;
  padding: 8px;
}

.status-bar {
  background: #f0f0f0;
  padding: 4px 8px;
  font-size: 0.9em;
}

.no-drag {
  cursor: pointer;
}
</style>

Programmatic Control Methods

Methods for controlling component position and size programmatically.

/**
 * Programmatic control methods available on component instance
 */
interface ProgrammaticMethods {
  /** 
   * Set horizontal position programmatically
   * @param val - New X position in pixels
   */
  moveHorizontally(val: number): void;
  
  /** 
   * Set vertical position programmatically  
   * @param val - New Y position in pixels
   */
  moveVertically(val: number): void;
  
  /** 
   * Set width programmatically
   * @param val - New width in pixels
   */
  changeWidth(val: number): void;
  
  /** 
   * Set height programmatically
   * @param val - New height in pixels
   */
  changeHeight(val: number): void;
}

Usage Examples:

<template>
  <div>
    <vue-draggable-resizable 
      ref="draggableElement"
      :w="200" 
      :h="150"
    >
      Programmable element
    </vue-draggable-resizable>
    
    <div class="controls">
      <button @click="moveToCenter">Center</button>
      <button @click="resetSize">Reset Size</button>
      <button @click="animateToCorner">Animate to Corner</button>
    </div>
  </div>
</template>

<script>
export default {
  methods: {
    moveToCenter() {
      const container = this.$el;
      const element = this.$refs.draggableElement;
      
      const centerX = (container.offsetWidth - 200) / 2;
      const centerY = (container.offsetHeight - 150) / 2;
      
      element.moveHorizontally(centerX);
      element.moveVertically(centerY);
    },
    
    resetSize() {
      const element = this.$refs.draggableElement;
      element.changeWidth(200);
      element.changeHeight(150);
    },
    
    async animateToCorner() {
      const element = this.$refs.draggableElement;
      const steps = 20;
      const targetX = 0;
      const targetY = 0;
      
      // Simple animation by stepping through positions
      for (let i = 0; i <= steps; i++) {
        const progress = i / steps;
        const currentX = element.left * (1 - progress) + targetX * progress;
        const currentY = element.top * (1 - progress) + targetY * progress;
        
        element.moveHorizontally(currentX);
        element.moveVertically(currentY);
        
        await new Promise(resolve => setTimeout(resolve, 20));
      }
    }
  }
}
</script>

Grid Snapping

Configure grid-based snapping for precise positioning and sizing.

/**
 * Grid snapping configuration
 */
interface GridSnapping {
  /** Grid intervals [x, y] in pixels (default: [1, 1]) */
  grid: [number, number];
}

Usage Examples:

<template>
  <!-- 20x20 pixel grid -->
  <vue-draggable-resizable
    :grid="[20, 20]"
    :w="200"
    :h="160"
  >
    Snaps to 20px grid
  </vue-draggable-resizable>
  
  <!-- Different X and Y grid sizes -->
  <vue-draggable-resizable
    :grid="[25, 15]"
  >
    25px horizontal, 15px vertical grid
  </vue-draggable-resizable>
  
  <!-- Fine grid for precision -->
  <vue-draggable-resizable
    :grid="[5, 5]"
  >
    Fine 5px grid
  </vue-draggable-resizable>
</template>

Slot System

Comprehensive slot system allowing content customization and custom handle designs.

/**
 * Available slots for content and handle customization
 */
interface SlotSystem {
  /** Default slot for main component content */
  default: Slot;
  
  /** Named slots for each resize handle position */
  'tl': Slot;      // Top-left handle slot
  'tm': Slot;      // Top-middle handle slot  
  'tr': Slot;      // Top-right handle slot
  'mr': Slot;      // Middle-right handle slot
  'br': Slot;      // Bottom-right handle slot
  'bm': Slot;      // Bottom-middle handle slot
  'bl': Slot;      // Bottom-left handle slot
  'ml': Slot;      // Middle-left handle slot
}

Usage Examples:

<template>
  <!-- Custom handle designs -->
  <vue-draggable-resizable :handles="['tl', 'br', 'mr']">
    <!-- Default slot content -->
    <div class="content">
      <h3>Resizable Card</h3>
      <p>Content goes here</p>
    </div>
    
    <!-- Custom corner handle -->
    <template #tl>
      <div class="corner-handle top-left">
        <svg width="12" height="12">
          <path d="M2,2 L10,2 L10,10" stroke="currentColor" fill="none" stroke-width="2"/>
        </svg>
      </div>
    </template>
    
    <!-- Custom corner handle -->
    <template #br>
      <div class="corner-handle bottom-right">
        <svg width="12" height="12">
          <path d="M2,10 L10,10 L10,2" stroke="currentColor" fill="none" stroke-width="2"/>
        </svg>
      </div>
    </template>
    
    <!-- Custom edge handle -->
    <template #mr>
      <div class="edge-handle">
        <div class="handle-dots">
          <span></span>
          <span></span>
          <span></span>
        </div>
      </div>
    </template>
  </vue-draggable-resizable>
</template>

<style>
.content {
  padding: 16px;
  background: white;
  border: 1px solid #ddd;
  border-radius: 4px;
  height: 100%;
  box-sizing: border-box;
}

.corner-handle {
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 123, 255, 0.1);
  border: 2px solid #007bff;
  border-radius: 6px;
  color: #007bff;
  cursor: nw-resize;
}

.corner-handle.bottom-right {
  cursor: se-resize;
}

.edge-handle {
  display: flex;
  align-items: center;
  justify-content: center;
  background: #f8f9fa;
  border: 1px solid #dee2e6;
  border-radius: 4px;
  cursor: ew-resize;
}

.handle-dots {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.handle-dots span {
  width: 3px;
  height: 3px;
  background: #6c757d;
  border-radius: 50%;
}
</style>

Themed Handle Examples:

<template>
  <!-- Dark theme handles -->
  <vue-draggable-resizable class="dark-theme">
    <div class="dark-content">Dark themed content</div>
    
    <template #tl>
      <div class="dark-handle corner">↖</div>
    </template>
    <template #tr>
      <div class="dark-handle corner">↗</div>
    </template>
    <template #bl>
      <div class="dark-handle corner">↙</div>
    </template>
    <template #br>
      <div class="dark-handle corner">↘</div>
    </template>
  </vue-draggable-resizable>
  
  <!-- Minimal handles -->
  <vue-draggable-resizable :handles="['br']">
    <div class="minimal-content">Minimal resize</div>
    
    <template #br>
      <div class="minimal-handle">
        <div class="resize-icon">⤢</div>
      </div>
    </template>
  </vue-draggable-resizable>
</template>

<style>
.dark-theme {
  background: #343a40;
  border: 1px solid #495057;
}

.dark-content {
  color: white;
  padding: 16px;
}

.dark-handle {
  background: #495057;
  color: #adb5bd;
  border: 1px solid #6c757d;
  border-radius: 3px;
  font-size: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.minimal-handle {
  background: transparent;
  color: #6c757d;
  display: flex;
  align-items: flex-end;
  justify-content: flex-end;
  cursor: se-resize;
}

.resize-icon {
  font-size: 14px;
  line-height: 1;
}
</style>

Touch Device Support

Built-in support for touch devices with optimized touch event handling.

/**
 * Touch device support is automatically enabled
 * Component handles both mouse and touch events seamlessly
 */
interface TouchSupport {
  /** Touch events are automatically detected and handled */
  touchEnabled: true;
  
  /** Enhanced touch targets on mobile devices via CSS media queries */
  mobileFriendly: true;
}

Usage Examples:

<template>
  <!-- Works automatically on touch devices -->
  <vue-draggable-resizable
    :w="250"
    :h="200"
    class="touch-optimized"
  >
    Touch and drag on mobile
    
    <!-- Larger handles for better touch experience -->
    <template #br>
      <div class="touch-handle">
        <div class="touch-indicator"></div>
      </div>
    </template>
  </vue-draggable-resizable>
</template>

<style>
.touch-optimized {
  /* Component automatically includes mobile-friendly handle sizing */
}

.touch-handle {
  width: 20px;
  height: 20px;
  background: rgba(0, 123, 255, 0.2);
  border: 2px solid #007bff;
  border-radius: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.touch-indicator {
  width: 8px;
  height: 8px;
  background: #007bff;
  border-radius: 2px;
}

/* Additional mobile optimizations */
@media (max-width: 768px) {
  .touch-optimized {
    min-width: 100px;
    min-height: 80px;
  }
  
  .touch-handle {
    width: 24px;
    height: 24px;
  }
}
</style>

Install with Tessl CLI

npx tessl i tessl/npm-vue-draggable-resizable

docs

advanced-features.md

component-events.md

component-props.md

index.md

tile.json