API REFERENCE · UTILITIES
@dojo-ng/dnd
Drag and drop for Dojo NG components, built on pointer events.
npm install @dojo-ng/dnd
In a Lit component, create a DragZoneController over the item container, and apply each move in onMove.
import { DragZoneController } from "@dojo-ng/dnd";
class MyList extends LitElement {
#zone = new DragZoneController(this, {
container: () => this.renderRoot.querySelector(".items"),
items: () => [...this.renderRoot.querySelectorAll("[data-key]")],
zoneId: "list",
axis: "y",
onMove: ({ key, fromIndex, toIndex }) => {
// reorder your data, then re-render
},
});
}
How it works
- It works inside shadow roots and with touch, mouse, and pen. It has no dependencies.
- Drops are controlled: the zone calls
onMove, and your code applies the change. - Zones that share a
groupaccept items from each other.
Accessibility
- Drag is an enhancement. A component that uses dnd must also offer a keyboard or menu way to move items, as WCAG 2.5.7 requires.
- For a component with no move controls of its own,
keyboardGrabModeadds keyboard moves. See the example below.
Examples
Keyboard grab mode
Connect keyboardGrabMode to a keydown handler. Your announce callback receives the messages for a live region.
- Space picks up the focused item, and the arrow keys move it.
- Space drops the item, and Escape cancels the move.
import { keyboardGrabMode } from "@dojo-ng/dnd";
const onKeydown = keyboardGrabMode({
zones: () => [this.zoneConfig],
announce: (msg) => this.liveRegion.textContent = msg,
});
Functions
cancelActiveDrag
function cancelActiveDrag(): void;
centerOf
function centerOf(rect: Rect, axis: Axis): number;
insertionIndex
function insertionIndex(centers: number[], pointer: number): number;
keyboardGrabMode
function keyboardGrabMode(cfg: {
zones: () => DragZoneConfig[];
announce: (msg: string) => void;
messages?: GrabMessages;
current?: () => {
zoneIndex: number;
index: number;
} | null;
onGrabChange?: (state: {
grabbed: boolean;
key: string | null;
zoneId: string | null;
index: number | null;
}) => void;
}): (e: KeyboardEvent) => void;
pointInRect
function pointInRect(x: number, y: number, r: Rect): boolean;
resolveMove
function resolveMove(m: MovePayload): MovePayload | null;
Classes
DragZoneController
class DragZoneController implements ReactiveController {
get zoneId(): string;
get axis(): Axis;
get config(): DragZoneConfig;
constructor(host: ReactiveControllerHost & HTMLElement, config: DragZoneConfig);
hostConnected(): void;
hostUpdated(): void;
hostDisconnected(): void;
commitDrag(p: PendingDrag, e: PointerEvent): void;
measureDrop(x: number, y: number, draggedKey: string): number;
containerRectContains(x: number, y: number): boolean;
drawIndicator(index: number, draggedKey: string): HTMLElement | null;
fromIndexOf(key: string): number;
autoScroll(x: number, y: number): void;
peersForActive(): DragZoneController[];
}
Types
Axis
type Axis = "x" | "y";
DragZoneConfig
interface DragZoneConfig {
container: () => HTMLElement;
items: () => HTMLElement[];
group?: string;
zoneId: string;
axis?: "x" | "y";
onMove(m: {
key: string;
from: string;
to: string;
fromIndex: number;
toIndex: number;
}): void;
}
GrabMessages
interface GrabMessages {
grabbed?: (key: string) => string;
moved?: (key: string, zoneId: string, position: number) => string;
dropped?: (key: string) => string;
cancelled?: (key: string) => string;
}
MovePayload
interface MovePayload {
key: string;
from: string;
to: string;
fromIndex: number;
toIndex: number;
}
Rect
interface Rect {
top: number;
bottom: number;
left: number;
right: number;
}