ngx-virtual-dnd 3.1.3 → 3.2.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -15,6 +15,7 @@ Inspired by [react-virtualized-dnd](https://github.com/forecast-it/react-virtual
15
15
  - **Container Constraints** - Constrain drag preview to container boundaries
16
16
  - **Axis Locking** - Lock dragging to horizontal or vertical axis
17
17
  - **Custom Previews** - Template-based drag preview and placeholder customization
18
+ - **Shift Animations** - Opt-in sliding of displaced items, plus a per-step event for haptics
18
19
  - **Keyboard Accessible** - Space to grab, arrows to move, Escape to cancel
19
20
  - **Touch Support** - Works with mouse and touch, with configurable delay/threshold
20
21
  - **Angular 21+** - Signals, standalone components, modern patterns
@@ -45,15 +46,15 @@ import {
45
46
  DragPreviewComponent,
46
47
  ],
47
48
  template: `
48
- <!-- Item template -->
49
- <ng-template #itemTpl let-item>
50
- <div class="item" [vdndDraggable]="item.id" [vdndDraggableData]="item">
51
- {{ item.name }}
52
- </div>
53
- </ng-template>
54
-
55
49
  <!-- Lists wrapped in a group -->
56
50
  <div vdndGroup="my-group">
51
+ <!-- Item template: declare it inside vdndGroup so the draggables inherit the group -->
52
+ <ng-template #itemTpl let-item>
53
+ <div class="item" [vdndDraggable]="item.id" [vdndDraggableData]="item">
54
+ {{ item.name }}
55
+ </div>
56
+ </ng-template>
57
+
57
58
  <vdnd-sortable-list
58
59
  droppableId="list-1"
59
60
  group="my-group"
@@ -98,6 +99,8 @@ export class MyComponent {
98
99
 
99
100
  **That's it!** `VirtualSortableListComponent` handles placeholder positioning, sticky items during drag, and virtual scroll integration automatically.
100
101
 
102
+ > Every draggable and droppable needs a group (from a `vdndGroup` ancestor or `vdndDraggableGroup` / `vdndDroppableGroup`), even for a single list — without one, drag is disabled. Templates resolve the group from where they are **declared**, so keep the `<ng-template>` inside the `vdndGroup` element.
103
+
101
104
  ## API Overview
102
105
 
103
106
  The library exports these main pieces (use IDE completion for full details):
@@ -300,6 +303,40 @@ Configure auto-scroll behavior when dragging near container edges:
300
303
 
301
304
  Set `[autoScrollEnabled]="false"` to disable auto-scroll entirely. These options are available on `VirtualSortableListComponent`, `DroppableDirective`, and `ScrollableDirective`.
302
305
 
306
+ ### Shift Animations & Haptics
307
+
308
+ Items displaced by the placeholder jump into place by default. Provide `VDND_ANIMATION_CONFIG` to make them slide instead (a compositor-only `transform` animation, works with virtual scrolling and dynamic heights):
309
+
310
+ ```typescript
311
+ import { VDND_ANIMATION_CONFIG } from 'ngx-virtual-dnd';
312
+
313
+ // app.config.ts (app-wide) or any component's `providers` (that subtree only)
314
+ providers: [{ provide: VDND_ANIMATION_CONFIG, useValue: { shiftDuration: 200 } }];
315
+ ```
316
+
317
+ | Option | Default | Description |
318
+ | --------------- | ---------------------------- | ----------------------------------- |
319
+ | `shiftDuration` | `200` | Slide duration in ms (`0` disables) |
320
+ | `shiftEasing` | `cubic-bezier(0.2, 0, 0, 1)` | CSS easing function |
321
+
322
+ The animation is skipped when the user prefers reduced motion. If the placeholder moves again mid-slide, items continue from where they currently are. Values are read each time an animation starts, so a getter can toggle it at runtime.
323
+
324
+ For haptic feedback on every step, listen to `(placeholderMove)` on `vdndDroppable` or `vdnd-sortable-list`. It fires each time the placeholder moves within that list (every item displacement), including when it enters the list — not for the initial pick-up or when it leaves:
325
+
326
+ ```html
327
+ <vdnd-sortable-list ... (placeholderMove)="onPlaceholderMove($event)" />
328
+ ```
329
+
330
+ ```typescript
331
+ import { Haptics } from '@capacitor/haptics';
332
+
333
+ onPlaceholderMove(event: PlaceholderMoveEvent): void {
334
+ Haptics.selectionChanged(); // or navigator.vibrate?.(10) on the web
335
+ }
336
+ ```
337
+
338
+ `PlaceholderMoveEvent` carries `draggableId`, `sourceDroppableId`, `droppableId`, `previousIndex` (`null` when the placeholder just entered the list) and `currentIndex`, using the same index convention as `DropEvent.destination.index`.
339
+
303
340
  ### Disabling Drag & Drop
304
341
 
305
342
  Use the `disabled` input to conditionally disable draggables, droppables, or entire lists:
@@ -339,13 +376,13 @@ For maximum control, use individual components instead of `VirtualSortableListCo
339
376
  DragPreviewComponent,
340
377
  ],
341
378
  template: `
342
- <ng-template #itemTpl let-item>
343
- <div class="item" [vdndDraggable]="item.id" [vdndDraggableData]="item">
344
- {{ item.name }}
345
- </div>
346
- </ng-template>
347
-
348
379
  <div vdndGroup="demo">
380
+ <ng-template #itemTpl let-item>
381
+ <div class="item" [vdndDraggable]="item.id" [vdndDraggableData]="item">
382
+ {{ item.name }}
383
+ </div>
384
+ </ng-template>
385
+
349
386
  <div vdndDroppable="list-1" (drop)="onDrop($event)">
350
387
  <vdnd-virtual-scroll
351
388
  droppableId="list-1"
@@ -433,6 +470,7 @@ The library emits events with position data. Implement announcements in your app
433
470
  ```typescript
434
471
  @Component({
435
472
  template: `
473
+ <!-- Inside a vdndGroup, like any draggable -->
436
474
  <div
437
475
  vdndDraggable="item-1"
438
476
  (dragStart)="announce('Grabbed ' + item.name)"
@@ -490,11 +528,12 @@ ARIA attributes (`aria-grabbed`, `aria-dropeffect`, `tabindex`) are managed auto
490
528
 
491
529
  All event types are importable from `ngx-virtual-dnd`.
492
530
 
493
- | Output | Event Type | Emitted By |
494
- | ------------- | ---------------- | ---------------------------------------------------- |
495
- | `(dragStart)` | `DragStartEvent` | `DraggableDirective` |
496
- | `(dragEnd)` | `DragEndEvent` | `DraggableDirective` |
497
- | `(drop)` | `DropEvent` | `DroppableDirective`, `VirtualSortableListComponent` |
531
+ | Output | Event Type | Emitted By |
532
+ | ------------------- | ---------------------- | ---------------------------------------------------- |
533
+ | `(dragStart)` | `DragStartEvent` | `DraggableDirective` |
534
+ | `(dragEnd)` | `DragEndEvent` | `DraggableDirective` |
535
+ | `(drop)` | `DropEvent` | `DroppableDirective`, `VirtualSortableListComponent` |
536
+ | `(placeholderMove)` | `PlaceholderMoveEvent` | `DroppableDirective`, `VirtualSortableListComponent` |
498
537
 
499
538
  `DragEndEvent.destinationIndex` is `null` when no drop occurred — an Escape cancel, a release outside every droppable, or a release over a disabled droppable — so branch on `destinationIndex === null` to detect that. The `cancelled` boolean is `true` only for an active Escape cancel.
500
539