CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-dnd-kit--utilities

Internal utilities shared between @dnd-kit packages providing React hooks, coordinate manipulation, and DOM utilities.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

typescript-utilities.mddocs/

TypeScript Utilities

Generic type manipulation helpers for advanced TypeScript patterns, useful for building type-safe drag and drop interfaces and utility functions.

Types

Arguments

Extracts function argument types as a tuple. Useful for creating wrapper functions that need to preserve exact parameter types.

/**
 * Extracts function argument types as a tuple
 * @template T - Function type to extract arguments from
 */
type Arguments<T> = T extends (...args: infer U) => any ? U : never;

Usage Examples:

import { type Arguments } from "@dnd-kit/utilities";

// Extract arguments from existing function
function createDragHandler(
  element: HTMLElement,
  options: { threshold: number; axis?: "x" | "y" },
  callback: (event: MouseEvent) => void
) {
  // Implementation here
}

// Create a type for the arguments
type DragHandlerArgs = Arguments<typeof createDragHandler>;
// Result: [HTMLElement, { threshold: number; axis?: "x" | "y" }, (event: MouseEvent) => void]

// Use in wrapper function
function createEnhancedDragHandler(...args: DragHandlerArgs) {
  console.log("Creating enhanced drag handler with args:", args);
  return createDragHandler(...args);
}

// Generic wrapper factory
function createWrapper<T extends (...args: any[]) => any>(
  originalFn: T
): (...args: Arguments<T>) => ReturnType<T> {
  return (...args: Arguments<T>) => {
    console.log("Calling function with args:", args);
    return originalFn(...args);
  };
}

// Event handler argument extraction
type MouseEventHandler = (event: MouseEvent) => void;
type MouseEventArgs = Arguments<MouseEventHandler>;
// Result: [MouseEvent]

function createEventProxy<T extends (...args: any[]) => any>(
  handler: T
): (...args: Arguments<T>) => void {
  return (...args: Arguments<T>) => {
    // Perform validation, logging, etc.
    console.log("Event proxy called with:", args);
    handler(...args);
  };
}

DeepRequired

Makes all properties and nested properties required, removing all optional flags recursively through the type hierarchy.

/**
 * Makes all properties and nested properties required
 * @template T - Type to make deeply required
 */
type DeepRequired<T> = {
  [K in keyof T]-?: Required<T[K]>;
};

Usage Examples:

import { type DeepRequired } from "@dnd-kit/utilities";

// Original interface with optional properties
interface DragConfig {
  element?: HTMLElement;
  options?: {
    threshold?: number;
    axis?: "x" | "y";
    constraints?: {
      minX?: number;
      maxX?: number;
      minY?: number;
      maxY?: number;
    };
  };
  callbacks?: {
    onStart?: (event: MouseEvent) => void;
    onMove?: (event: MouseEvent) => void;
    onEnd?: (event: MouseEvent) => void;
  };
}

// Make all properties required
type RequiredDragConfig = DeepRequired<DragConfig>;
/* Result:
{
  element: HTMLElement;
  options: {
    threshold: number;
    axis: "x" | "y";
    constraints: {
      minX: number;
      maxX: number;
      minY: number;
      maxY: number;
    };
  };
  callbacks: {
    onStart: (event: MouseEvent) => void;
    onMove: (event: MouseEvent) => void;
    onEnd: (event: MouseEvent) => void;
  };
}
*/

// Function that requires complete configuration
function initializeDragSystem(config: RequiredDragConfig) {
  // All properties are guaranteed to exist
  config.element.addEventListener("mousedown", config.callbacks.onStart);
  
  // No need for optional chaining
  const threshold = config.options.threshold;
  const constraints = config.options.constraints;
  
  console.log("Drag system initialized with complete config");
}

// Default configuration provider
function createDefaultDragConfig(): RequiredDragConfig {
  return {
    element: document.createElement("div"),
    options: {
      threshold: 5,
      axis: "x",
      constraints: {
        minX: 0,
        maxX: window.innerWidth,
        minY: 0,
        maxY: window.innerHeight
      }
    },
    callbacks: {
      onStart: () => console.log("Drag started"),
      onMove: () => console.log("Drag moved"),
      onEnd: () => console.log("Drag ended")
    }
  };
}

// Merge partial config with defaults
function mergeDragConfig(
  partial: DragConfig,
  defaults: RequiredDragConfig
): RequiredDragConfig {
  return {
    element: partial.element ?? defaults.element,
    options: {
      threshold: partial.options?.threshold ?? defaults.options.threshold,
      axis: partial.options?.axis ?? defaults.options.axis,
      constraints: {
        minX: partial.options?.constraints?.minX ?? defaults.options.constraints.minX,
        maxX: partial.options?.constraints?.maxX ?? defaults.options.constraints.maxX,
        minY: partial.options?.constraints?.minY ?? defaults.options.constraints.minY,
        maxY: partial.options?.constraints?.maxY ?? defaults.options.constraints.maxY
      }
    },
    callbacks: {
      onStart: partial.callbacks?.onStart ?? defaults.callbacks.onStart,
      onMove: partial.callbacks?.onMove ?? defaults.callbacks.onMove,
      onEnd: partial.callbacks?.onEnd ?? defaults.callbacks.onEnd
    }
  };
}

FirstArgument

Extracts the type of the first argument from a function. Useful for creating utilities that work with the primary parameter of callback functions.

/**
 * Extracts the type of the first argument from a function
 * @template T - Function type to extract first argument from
 */
type FirstArgument<T> = T extends (firstArg: infer U, ...args: Array<any>) => any
  ? U
  : never;

Usage Examples:

import { type FirstArgument } from "@dnd-kit/utilities";

// Extract first argument type from event handlers
type MouseEventHandler = (event: MouseEvent, extra: string) => void;
type MouseEventType = FirstArgument<MouseEventHandler>;
// Result: MouseEvent

type DragHandler = (data: { x: number; y: number }, element: HTMLElement) => void;
type DragData = FirstArgument<DragHandler>;
// Result: { x: number; y: number }

// Create type-safe event proxy
function createEventProxy<T extends (event: any, ...args: any[]) => any>(
  handler: T
): (event: FirstArgument<T>) => void {
  return (event: FirstArgument<T>) => {
    // Validate or transform the event
    console.log("Proxying event:", event);
    handler(event, "additional", "arguments");
  };
}

// React component prop extraction
interface ButtonProps {
  onClick: (event: React.MouseEvent<HTMLButtonElement>) => void;
  onDoubleClick: (event: React.MouseEvent<HTMLButtonElement>, target: HTMLElement) => void;
}

type ClickEvent = FirstArgument<ButtonProps['onClick']>;
// Result: React.MouseEvent<HTMLButtonElement>

type DoubleClickEvent = FirstArgument<ButtonProps['onDoubleClick']>;
// Result: React.MouseEvent<HTMLButtonElement>

// Generic callback wrapper
function createCallbackWrapper<T extends (first: any, ...rest: any[]) => any>(
  callback: T,
  middleware: (data: FirstArgument<T>) => FirstArgument<T>
): T {
  return ((...args: any[]) => {
    const [first, ...rest] = args;
    const processedFirst = middleware(first);
    return callback(processedFirst, ...rest);
  }) as T;
}

// Usage with drag handlers
const originalDragHandler = (data: { x: number, y: number }, element: HTMLElement) => {
  console.log("Dragging to:", data);
};

const wrappedDragHandler = createCallbackWrapper(
  originalDragHandler,
  (data) => ({
    x: Math.round(data.x),
    y: Math.round(data.y)
  })
);

// Event handler factory
function createEventHandler<T extends (event: any, ...args: any[]) => any>(
  handler: T,
  validator: (event: FirstArgument<T>) => boolean
): T {
  return ((...args: any[]) => {
    const [event, ...rest] = args;
    if (validator(event)) {
      return handler(event, ...rest);
    }
    console.warn("Event validation failed");
  }) as T;
}

// Create validated mouse handler
const validatedMouseHandler = createEventHandler(
  (event: MouseEvent) => console.log("Valid mouse event"),
  (event) => event instanceof MouseEvent && event.button === 0
);

Without

Creates a type by excluding specified keys from another type. Useful for creating modified versions of interfaces or for implementing omit-like functionality.

/**
 * Creates a type by excluding specified keys from T
 * @template T - Source type
 * @template K - Keys to exclude from T
 */
type Without<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

Usage Examples:

import { type Without } from "@dnd-kit/utilities";

// Original interface
interface FullDragConfig {
  element: HTMLElement;
  threshold: number;
  axis: "x" | "y" | "both";
  onStart: (event: MouseEvent) => void;
  onMove: (event: MouseEvent) => void;
  onEnd: (event: MouseEvent) => void;
  disabled: boolean;
}

// Create type without event handlers
type DragConfigWithoutEvents = Without<FullDragConfig, 'onStart' | 'onMove' | 'onEnd'>;
/* Result:
{
  element: HTMLElement;
  threshold: number;
  axis: "x" | "y" | "both";
  disabled: boolean;
}
*/

// Create type without element (for configuration only)
type DragOptions = Without<FullDragConfig, 'element'>;
/* Result:
{
  threshold: number;
  axis: "x" | "y" | "both";
  onStart: (event: MouseEvent) => void;
  onMove: (event: MouseEvent) => void;
  onEnd: (event: MouseEvent) => void;
  disabled: boolean;
}
*/

// Function that accepts partial configuration
function createDragElement(
  element: HTMLElement,
  config: Without<FullDragConfig, 'element'>
) {
  const fullConfig: FullDragConfig = {
    element,
    ...config
  };
  
  return initializeDrag(fullConfig);
}

// Create immutable update functions
function updateDragConfig<K extends keyof FullDragConfig>(
  config: FullDragConfig,
  key: K,
  value: FullDragConfig[K]
): FullDragConfig {
  return { ...config, [key]: value };
}

// Create configuration builder
class DragConfigBuilder {
  private config: Partial<FullDragConfig> = {};
  
  element(el: HTMLElement): DragConfigBuilder {
    this.config.element = el;
    return this;
  }
  
  threshold(val: number): DragConfigBuilder {
    this.config.threshold = val;
    return this;
  }
  
  // Method that returns config without builder methods
  build(): Without<FullDragConfig, never> {
    return this.config as FullDragConfig;
  }
  
  // Get partial config for validation
  getPartial(): Without<FullDragConfig, 'element'> | Partial<FullDragConfig> {
    return this.config;
  }
}

// React props manipulation
interface ComponentProps {
  className?: string;
  style?: React.CSSProperties;
  onClick?: (event: React.MouseEvent) => void;
  onDrag?: (event: React.DragEvent) => void;
  children: React.ReactNode;
  disabled?: boolean;
}

// Create props without event handlers for styling-only component
type StyleOnlyProps = Without<ComponentProps, 'onClick' | 'onDrag'>;

function StyleOnlyComponent(props: StyleOnlyProps) {
  return <div className={props.className} style={props.style}>{props.children}</div>;
}

// Create props without children for wrapper component
type WrapperProps = Without<ComponentProps, 'children'>;

function WrapperComponent(props: WrapperProps) {
  return (
    <div {...props}>
      <div>Wrapper content</div>
    </div>
  );
}

// API response type manipulation
interface APIResponse {
  data: any;
  status: number;
  headers: Record<string, string>;
  timestamp: string;
  requestId: string;
}

// Client-side type without server metadata
type ClientResponse = Without<APIResponse, 'headers' | 'requestId'>;

function processClientResponse(response: ClientResponse) {
  // Only access client-relevant properties
  console.log("Status:", response.status);
  console.log("Data:", response.data);
  console.log("Time:", response.timestamp);
}

Install with Tessl CLI

npx tessl i tessl/npm-dnd-kit--utilities

docs

coordinates.md

css-utilities.md

event-handling.md

execution-context.md

focus-management.md

hooks.md

index.md

math-operations.md

type-guards.md

typescript-utilities.md

tile.json