> For AI agents: the complete documentation index is available at https://gultyayev.github.io/ngx-virtual-dnd/llms.txt, the full documentation bundle is available at https://gultyayev.github.io/ngx-virtual-dnd/llms-full.txt.

# Types, tokens and strategies

## Configuration

```ts
interface AutoScrollConfig {
  threshold: number; // distance from the edge that starts scrolling (px). Default 50
  maxSpeed: number; // max speed in px per 60 fps frame. Default 15
  accelerate: boolean; // faster closer to the edge. Default true
}

interface VdndAnimationConfig {
  shiftDuration?: number; // slide duration in ms. Default 200; 0 turns it off
  shiftEasing?: string; // CSS easing. Default 'cubic-bezier(0.2, 0, 0, 1)'
  dropDuration?: number; // drop animation duration in ms. Default 200; 0 turns it off
  dropEasing?: string; // CSS easing. Default 'cubic-bezier(0.2, 0, 0, 1)'
}
```

`VdndAnimationConfig` is provided through `VDND_ANIMATION_CONFIG` (added in 3.2.0). See [Animations and haptics](https://gultyayev.github.io/ngx-virtual-dnd/guide/features/animations.md).

## Template contexts

```ts
// <vdnd-sortable-list> / <vdnd-virtual-scroll> itemTemplate
interface VirtualScrollItemContext<T> {
  $implicit: T;
  index: number;
  isSticky: boolean;
}

// *vdndVirtualFor
interface VirtualForContext<T> {
  $implicit: T;
  index: number;
  first: boolean;
  last: boolean;
  count: number;
}

// <vdnd-drag-preview [previewTemplate]>
interface DragPreviewContext<T = unknown> {
  $implicit: T;
  draggableId: string;
  droppableId: string;
}

// <vdnd-placeholder [template]>
interface PlaceholderContext {
  $implicit: number; // height in px
  height: number;
}
```

## Drag state

```ts
interface DraggedItem {
  draggableId: string;
  droppableId: string;
  element: HTMLElement;
  clonedElement?: HTMLElement;
  height: number;
  width: number;
  data?: unknown;
}

interface CursorPosition {
  x: number;
  y: number;
}

interface GrabOffset {
  x: number;
  y: number;
}

interface DragState {
  isDragging: boolean;
  draggedItem: DraggedItem | null;
  sourceDroppableId: string | null;
  sourceIndex: number | null;
  activeDroppableId: string | null;
  placeholderId: string | null; // deprecated: always END_OF_LIST or null, use placeholderIndex
  placeholderIndex: number | null;
  cursorPosition: CursorPosition | null;
  grabOffset: GrabOffset | null;
  initialPosition: CursorPosition | null;
  lockAxis: 'x' | 'y' | null;
  isKeyboardDrag: boolean;
  keyboardTargetIndex: number | null;
}
```

## Strategies

Virtual scroll math is pluggable. The components pick a strategy from `dynamicItemHeight`, so you only need these to build your own container.

```ts
interface VirtualScrollStrategy {
  readonly version: Signal<number>;
  getTotalHeight(itemCount: number): number;
  getFirstVisibleIndex(scrollTop: number): number;
  getVisibleCount(startIndex: number, containerHeight: number): number;
  getOffsetForIndex(index: number): number;
  getItemHeight(index: number): number;
  setMeasuredHeight(key: unknown, height: number): void;
  setItemKeys(keys: unknown[]): void;
  setExcludedIndex(index: number | null): void;
  findIndexAtOffset(offset: number): number;
  getItemCount(): number;
}
```

| Class                   | Constructor                           | Behavior                                                           |
| ----------------------- | ------------------------------------- | ------------------------------------------------------------------ |
| `FixedHeightStrategy`   | `new FixedHeightStrategy(itemHeight)` | `index * itemHeight`; the default                                  |
| `DynamicHeightStrategy` | `new DynamicHeightStrategy(estimate)` | Measured heights, prefix sums and binary search (O(log n) lookups) |

## Injection tokens

| Token                   | Provides              | Provided by                                                                  |
| ----------------------- | --------------------- | ---------------------------------------------------------------------------- |
| `VDND_SCROLL_CONTAINER` | `VdndScrollContainer` | `VirtualViewportComponent`, `VirtualContentComponent`, `ScrollableDirective` |
| `VDND_VIRTUAL_VIEWPORT` | `VdndVirtualViewport` | `VirtualViewportComponent`, `VirtualContentComponent`                        |
| `VDND_GROUP_TOKEN`      | `VdndGroupContext`    | `DroppableGroupDirective`                                                    |
| `VDND_ANIMATION_CONFIG` | `VdndAnimationConfig` | You (opt-in), app-wide or in a component's `providers`                       |

```ts
interface VdndScrollContainer {
  readonly nativeElement: HTMLElement;
  scrollTop(): number;
  containerHeight(): number;
  scrollTo(options: ScrollToOptions): void;
}

interface VdndVirtualViewport {
  scrollTop(): number;
  containerHeight(): number;
  itemHeight(): number;
  contentOffset(): number;
  readonly nativeElement: HTMLElement;
  setRenderStartIndex(index: number): void;
  getOffsetForIndex(index: number): number;
  readonly strategy: VirtualScrollStrategy | null;
}

interface VdndGroupContext {
  readonly group: Signal<string>;
}
```

`scrollTop()` on both interfaces is the rendered scroll position, a reactive value for computing what to render. The built-in containers commit it once per animation frame, after a move of 5px or more, so it can lag the scrolling element. To compute a position (for example a scroll target), read that element's `scrollTop`. For `VirtualViewportComponent` and `ScrollableDirective` it is `nativeElement`. `VirtualContentComponent`'s host does not scroll, and its `scrollTop()` is relative to where its list starts: take the parent scroll container's element `scrollTop`, subtract the content offset, and clamp at 0.

## Constants

```ts
const END_OF_LIST = 'END_OF_LIST'; // deprecated: the value of every placeholderId; positions are indexes
const INITIAL_DRAG_STATE: DragState; // the idle state
```
