Responsive Draggable
Ecosistema completo para organización y gestión de contenido con interfaces espaciales
ResponsiveDraggable Directive
Description
The ResponsiveDraggable directive implements fully responsive drag functionality for widgets in the visual editor. This directive is essential for allowing users to interact with widgets through drag and drop, respecting different breakpoints and layout systems.
Key Features
- Complete support for responsive design with different breakpoints
- Compatibility with different positioning units (px, %, vw, vh)
- Integration with different layout systems (standard, grid, flex)
- Bidirectional updates between UI and data model
- Optimized management of touch and mouse events
- Configurable constraint and boundary systems
Import
import { ResponsiveDraggableDirective } from '@widgets-editor/shared-ui';
API
Inputs
| Name | Type | Default Value | Description |
|---|---|---|---|
widget |
Widget |
- | Widget to control (required) |
activeBreakpoint |
BreakpointKey |
'base' |
Current active breakpoint |
dragEnabled |
boolean |
true |
Enables/disables drag functionality |
dragHandle |
string | undefined |
undefined |
CSS selector for drag handle element |
dragConstraint |
'parent' | 'window' | undefined |
undefined |
Boundary constraint for dragging |
dragSnapToGrid |
boolean |
false |
Enables/disables snap to grid |
dragGridSize |
number |
8 |
Grid size for snapping |
dragAxis |
'x' | 'y' | 'xy' |
'xy' |
Permitted axis for dragging |
dragInitialTransform |
boolean |
true |
Preserves initial transformation |
parentLayoutType |
'standard' | 'grid' | 'flex' |
'standard' |
Parent container layout type |
positionUnits |
'px' | '%' | 'vw' | 'vh' | 'auto' |
'px' |
Units for positioning |
gridArea |
string | null |
null |
Grid area for grid layouts |
flexOrder |
number | null |
null |
Order for flex layouts |
Outputs
| Name | Type | Description |
|---|---|---|
dragStart |
EventEmitter<ResponsiveDragStartEvent> |
Emitted when dragging starts |
dragMove |
EventEmitter<ResponsiveDragMoveEvent> |
Emitted during drag movement |
dragEnd |
EventEmitter<ResponsiveDragEndEvent> |
Emitted when dragging ends |
Public Methods
| Name | Parameters | Return | Description |
|---|---|---|---|
setPosition |
(x: number, y: number): void |
void |
Sets the element's position |
setPositionWithEvent |
(x: number, y: number, emitEvent?: boolean): void |
void |
Sets position and optionally emits an event |
getPosition |
(): { x: number; y: number } |
{ x: number; y: number } |
Gets the current element position |
updateElementStyles |
(styles: Record<string, string>): void |
void |
Directly updates element CSS styles |
reset |
(): void |
void |
Resets position to initial transformation |
Interfaces
ResponsiveDragStartEvent
interface ResponsiveDragStartEvent {
source: ResponsiveDraggableDirective;
element: HTMLElement;
event: MouseEvent | TouchEvent;
widget: Widget;
breakpoint: BreakpointKey;
}
ResponsiveDragMoveEvent
interface ResponsiveDragMoveEvent {
source: ResponsiveDraggableDirective;
element: HTMLElement;
event: MouseEvent | TouchEvent;
deltaX: number;
deltaY: number;
offsetX: number;
offsetY: number;
widget: Widget;
breakpoint: BreakpointKey;
}
ResponsiveDragEndEvent
interface ResponsiveDragEndEvent {
source: ResponsiveDraggableDirective;
element: HTMLElement;
event: MouseEvent | TouchEvent;
deltaX: number;
deltaY: number;
widget: Widget;
breakpoint: BreakpointKey;
}
Internal Functionality
State Management with Signals
The directive uses Angular's Signal system to manage internal state and maintain synchronization between the data model and visual presentation:
private _widgetSignal = signal<Widget | null>(null);
private _activeBreakpointSignal = signal<BreakpointKey>('base');
private _widgetPosition = computed(() => {
const widget = this._widgetSignal();
const breakpoint = this._activeBreakpointSignal();
if (!widget) return { x: 0, y: 0, constrainToParent: false };
if (this.isResponsivePosition(widget.position)) {
const responsivePosition = widget.position as ResponsiveValue<Position>;
return responsivePosition[breakpoint] || responsivePosition.base;
} else {
return widget.position as Position;
}
});
Responsiveness Handling
The directive automatically detects whether the widget position is responsive or not, and applies the corresponding behavior:
private isResponsivePosition(position: Position | ResponsiveValue<Position>): boolean {
return (position as ResponsiveValue<Position>).base !== undefined;
}
Bidirectional Updates
After each drag, the directive updates the widget's data model:
private updateWidgetPositionModel(deltaX: number, deltaY: number): void {
const widget = this._widgetSignal();
const breakpoint = this._activeBreakpointSignal();
if (!widget) return;
const updatedWidget = { ...widget };
if (this.isResponsivePosition(updatedWidget.position)) {
// Update responsive position only for current breakpoint
// ...
} else {
// Update standard position
// ...
}
this._widgetSignal.set(updatedWidget);
}
Usage Examples
Basic Usage
<div
libResponsiveDraggable
[widget]="widget"
[activeBreakpoint]="currentBreakpoint"
(dragEnd)="onDragEnd($event)"
>
<!-- Widget content -->
</div>
With Constraints and Custom Handle
<div
libResponsiveDraggable
[widget]="widget"
[activeBreakpoint]="currentBreakpoint"
[dragHandle]="'.handle'"
[dragConstraint]="'parent'"
[dragSnapToGrid]="true"
[dragGridSize]="10"
(dragEnd)="onDragEnd($event)"
>
<div class="handle">Drag here</div>
<!-- Widget content -->
</div>
Usage in Grid Layout
<div
libResponsiveDraggable
[widget]="widget"
[activeBreakpoint]="currentBreakpoint"
[parentLayoutType]="'grid'"
[gridArea]="'content'"
[positionUnits]="'%'"
(dragEnd)="onDragEnd($event)"
>
<!-- Widget content -->
</div>
Performance Considerations
- The directive uses
ngZone.runOutsideAngular()for drag event management, avoiding unnecessary change detections during dragging. - Touch events are configured with
{ passive: false }to prevent browser scrolling during dragging on mobile devices. - Change detection is limited to the moments of drag start, movement, and end through
ngZone.run().
Integration with the Responsive System
The directive is designed to work closely with the editor's responsive system:
- Automatically updates the data model according to the active breakpoint
- Supports different units for different devices
- Maintains state consistency between the interface and the model
Usage with Advanced Layout Systems
Although the directive is compatible with Grid and Flex layouts, it currently uses CSS transformations for visual positioning. In future versions, a more advanced system will be implemented to directly modify Grid and Flex properties.