Type guards and utilities for working with DOM events, including touch and keyboard event detection with cross-frame compatibility.
Type guard that checks if an event has viewport-relative coordinate properties (clientX and clientY).
/**
* Type guard for events with viewport-relative coordinates
* @param event - DOM event to check
* @returns True if event has clientX and clientY properties
*/
function hasViewportRelativeCoordinates(
event: Event
): event is Event & Pick<PointerEvent, 'clientX' | 'clientY'>;Usage Examples:
import { hasViewportRelativeCoordinates } from "@dnd-kit/utilities";
function handleGenericEvent(event: Event) {
if (hasViewportRelativeCoordinates(event)) {
// TypeScript now knows event has clientX and clientY
console.log(`Coordinates: ${event.clientX}, ${event.clientY}`);
// Safe to use coordinate properties
const rect = event.target?.getBoundingClientRect();
if (rect) {
const relativeX = event.clientX - rect.left;
const relativeY = event.clientY - rect.top;
console.log(`Relative: ${relativeX}, ${relativeY}`);
}
} else {
console.log("Event does not have coordinate information");
}
}
// Use with multiple event types
element.addEventListener("mousedown", handleGenericEvent);
element.addEventListener("touchstart", handleGenericEvent);
element.addEventListener("keydown", handleGenericEvent); // Won't have coordinatesType guard that determines if an event is a KeyboardEvent. Uses cross-frame compatible detection.
/**
* Type guard for KeyboardEvent with cross-frame compatibility
* @param event - Event to check (can be undefined or null)
* @returns True if event is a KeyboardEvent
*/
function isKeyboardEvent(event: Event | undefined | null): event is KeyboardEvent;Usage Examples:
import { isKeyboardEvent } from "@dnd-kit/utilities";
function handleInteractionEvent(event: Event | null) {
if (isKeyboardEvent(event)) {
// TypeScript knows this is a KeyboardEvent
console.log(`Key pressed: ${event.key}`);
console.log(`Ctrl key: ${event.ctrlKey}`);
console.log(`Key code: ${event.code}`);
// Handle specific keys
if (event.key === "Enter") {
console.log("Enter key pressed");
}
if (event.key === "Escape") {
console.log("Escape key pressed");
}
}
}
// React example
import React from "react";
function KeyboardAwareComponent() {
const handleKeyEvent = (event: React.KeyboardEvent | Event) => {
const nativeEvent = 'nativeEvent' in event ? event.nativeEvent : event;
if (isKeyboardEvent(nativeEvent)) {
// Handle keyboard-specific logic
if (nativeEvent.key === "ArrowUp") {
// Move up
} else if (nativeEvent.key === "ArrowDown") {
// Move down
}
}
};
return (
<div tabIndex={0} onKeyDown={handleKeyEvent}>
Press arrow keys to navigate
</div>
);
}Type guard that determines if an event is a TouchEvent. Uses cross-frame compatible detection.
/**
* Type guard for TouchEvent with cross-frame compatibility
* @param event - Event to check (can be undefined or null)
* @returns True if event is a TouchEvent
*/
function isTouchEvent(event: Event | undefined | null): event is TouchEvent;Usage Examples:
import { isTouchEvent } from "@dnd-kit/utilities";
function handlePointerEvent(event: Event) {
if (isTouchEvent(event)) {
// TypeScript knows this is a TouchEvent
console.log(`Number of touches: ${event.touches.length}`);
// Access touch-specific properties
if (event.touches.length > 0) {
const firstTouch = event.touches[0];
console.log(`Touch at: ${firstTouch.clientX}, ${firstTouch.clientY}`);
}
// Handle multi-touch
if (event.touches.length > 1) {
console.log("Multi-touch detected");
const touch1 = event.touches[0];
const touch2 = event.touches[1];
const distance = Math.sqrt(
Math.pow(touch2.clientX - touch1.clientX, 2) +
Math.pow(touch2.clientY - touch1.clientY, 2)
);
console.log(`Distance between touches: ${distance}`);
}
} else {
// Likely a mouse event
console.log("Non-touch event detected");
}
}
// Unified event handling
function createUnifiedHandler(element: HTMLElement) {
const handleStart = (event: Event) => {
if (isTouchEvent(event)) {
// Touch-specific handling
event.preventDefault(); // Prevent scrolling
const touch = event.touches[0];
startDrag(touch.clientX, touch.clientY);
} else {
// Mouse event handling
const mouseEvent = event as MouseEvent;
startDrag(mouseEvent.clientX, mouseEvent.clientY);
}
};
element.addEventListener("touchstart", handleStart);
element.addEventListener("mousedown", handleStart);
}
function startDrag(x: number, y: number) {
console.log(`Starting drag at: ${x}, ${y}`);
}
// React drag and drop example
import React from "react";
function TouchAwareDraggable() {
const handleDragStart = (event: React.TouchEvent | React.MouseEvent) => {
const nativeEvent = event.nativeEvent;
if (isTouchEvent(nativeEvent)) {
// Touch drag
const touch = nativeEvent.touches[0];
console.log("Touch drag started at:", touch.clientX, touch.clientY);
} else {
// Mouse drag
const mouseEvent = nativeEvent as MouseEvent;
console.log("Mouse drag started at:", mouseEvent.clientX, mouseEvent.clientY);
}
};
return (
<div
onTouchStart={handleDragStart}
onMouseDown={handleDragStart}
style={{ touchAction: "none" }} // Prevent default touch behaviors
>
Touch or click to drag
</div>
);
}