or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

coordinates.mdcss-utilities.mdevent-handling.mdexecution-context.mdfocus-management.mdhooks.mdindex.mdmath-operations.mdtype-guards.mdtypescript-utilities.md
tile.json

event-handling.mddocs/

Event Handling

Type guards and utilities for working with DOM events, including touch and keyboard event detection with cross-frame compatibility.

Capabilities

hasViewportRelativeCoordinates

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 coordinates

isKeyboardEvent

Type 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>
  );
}

isTouchEvent

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>
  );
}