ComponentUpdated in v15

Sortable List

A list the user reorders by drag or keyboard.

Installation

Import the class, and @use the entry point in your global styles: without it the component renders unstyled.

import { Component } from '@angular/core';
import { WrDragHandle, WrSortableItem, WrSortableList } from 'ngwr/sortable-list';

@Component({ imports: [WrDragHandle, WrSortableItem, WrSortableList] })
export class MyComponent {}

Peer dependencies

Needs @angular/cdk.

pnpm add @angular/cdk

Basic usage

Bind [(items)] and write one <wr-sortable-item> per item in your own loop. A row has no padding, border or background: style it yourself.

<wr-sortable-list [(items)]="rows" (reorder)="onReorder($event)">
  @for (row of rows(); track row.id; let i = $index) {
    <wr-sortable-item>
      <div class="row">
        <span class="row__index">#{{ i + 1 }}</span>
        <span>{{ row.label }}</span>
      </div>
    </wr-sortable-item>
  }
</wr-sortable-list>
.row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.75rem 1rem;
  border: 1px solid var(--wr-border-base);
  border-radius: var(--wr-border-radius-base);
  background: var(--wr-color-surface);
}
#1Design the empty state
#2Wire the new sidebar config
#3Run lighthouse on the homepage
#4Write release notes
#5Bump the version

Press Space to pick this item up, then use the arrow keys to move it. Press Space again to drop it, or Escape to put it back.

Last reorder: —

With a drag handle

Put wrDragHandle on one element in the row, and only that element starts a drag.

<wr-sortable-list [(items)]="rows">
  @for (row of rows(); track row.id) {
    <wr-sortable-item>
      <div class="row">
        <span wrDragHandle aria-label="Reorder">≡</span>
        <span>{{ row.label }}</span>
      </div>
    </wr-sortable-item>
  }
</wr-sortable-list>
≡Design the empty state
≡Wire the new sidebar config
≡Run lighthouse on the homepage
≡Write release notes
≡Bump the version

Press Space to pick this item up, then use the arrow keys to move it. Press Space again to drop it, or Escape to put it back.

Only the ≡ handle starts a drag.

Keyboard

Every row is a tab stop.

NameDescriptionTypeDefault
Space / EnterPick the focused row up, or drop the one being held.key—
Arrow keysMove the held row. Follows the reading direction when horizontal.key—
EscapePut the held row back where it started.key—

API

NameDescriptionTypeDefault
itemsrequiredItems to render. Two-way — emits the reordered array on drop.T[]—
orientationLayout direction. Drives CDK's cdkDropListOrientation.'vertical' | 'horizontal''vertical'
disabledDisable all dragging.booleanfalse
lockAxisLocked axis — restrict drag movement to one axis even diagonally.'x' | 'y' | undefinedundefined
dragStartDelayDelay (ms) before a drag begins after the pointer goes down. The touch delay is the fix for the classic CDK touch snag: without it, the touch-action: none CDK puts on each item blocks scrolling the list on a phone. With a small touch delay, a quick swipe scrolls and a brief hold starts the drag; mouse stays instant. Pass a single number to apply one delay to both pointers.number | { touch: number; mouse: number }{ touch: 150, mouse: 0 }
(reorder)Fired after a successful reorder with the new array + indices.WrSortableReorderEvent<T>—

Related

NameDescriptionTypeDefault
<wr-sortable-item>One row. Write one per item in your own loop.component—
[wrDragHandle]Only this element starts a drag.directive—

CSS variables

Set these on the component's selector or a wrapper, not on :root: the defaults are declared on ngwr/sortable-list's own element.

VariableDefaultDeclared on
--wr-sortable-list-gapvar(--wr-space-sm).wr-sortable-list
--wr-sortable-list-item-font-familyvar(--wr-font-family-base).wr-sortable-list__item
--wr-sortable-list-placeholder-radiusvar(--wr-border-radius-base).wr-sortable-list

Dependencies

Angular CDKdrag-drop

See also