### Install DragDoll and Dependencies via npm Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/getting-started.md This command installs DragDoll and its core dependencies (Eventti, Tikki, Mezr) using npm, suitable for Node.js projects and bundler-based web development. ```bash npm install dragdoll eventti tikki mezr ``` -------------------------------- ### Configure DragDoll Dependencies with Import Map in Browser Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/getting-started.md This HTML snippet demonstrates how to set up an import map to load DragDoll and its dependencies directly from CDN for browser-based projects, enabling ES module imports without a build step. ```html ``` -------------------------------- ### Initialize Draggable with Sensors - TypeScript Example Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/draggable.md Demonstrates how to import and instantiate the Draggable class, providing it with PointerSensor and KeyboardSensor instances and configuring which elements to move. This example sets up a basic draggable element. ```ts import { PointerSensor, KeyboardSensor, Draggable } from 'dragdoll'; const element = document.querySelector('.draggable') as HTMLElement; const pointerSensor = new PointerSensor(element); const keyboardSensor = new KeyboardSensor(element); const draggable = new Draggable([pointerSensor, keyboardSensor], { elements: () => [element], }); ``` -------------------------------- ### Create a Draggable Element with Multiple Sensors in TypeScript Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/getting-started.md This TypeScript example demonstrates how to make an HTML element draggable using DragDoll. It shows the instantiation of PointerSensor and KeyboardSensor, combining them into a single Draggable instance, and managing the element's movement based on various input events. It also includes the necessary steps to clean up resources by destroying the sensors and draggable instance when no longer needed. ```typescript import { PointerSensor, KeyboardSensor, Draggable } from 'dragdoll'; // Let's assume that you have this element in DOM and you want to drag it // around. const element = document.querySelector('.draggable') as HTMLElement; // First we need to instantiate a new PointerSensor for the element, which // listens to DOM events and emits drag events for us to listen to. This does // not yet make the element move. const pointerSensor = new PointerSensor(element); // Let's also create a keyboard sensor, which listens to keyboard events and // emits drag events for us to listen to. const keyboardSensor = new KeyboardSensor(element); // Next, let's make the element move based on the events the PointerSensor // and KeyboardSensor are emitting. Note that you can feed multiple sensors to // a single draggable instance. const draggable = new Draggable([pointerSensor, keyboardSensor], { // Here we need to provide a function which returns an array of all the // elements that we want to move around based on the provided sensor's // events. In this case we just want to move the element which we are // monitoring. elements: () => [element], }); // Now you should be able to drag the element around using mouse, touch or // keyboard. // When you're done with your dragging needs you can destroy the sensors and // draggable. draggable.destroy(); pointerSensor.destroy(); keyboardSensor.destroy(); ``` -------------------------------- ### KeyboardSensor Basic Usage Example Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/keyboard-sensor.md Demonstrates how to import, instantiate, and use the KeyboardSensor and Draggable classes to listen for keyboard events and control an element's drag behavior. It shows event listeners for drag start, move, end, and cancel, and how to integrate the sensor with a Draggable instance. ```ts import { KeyboardSensor, Draggable } from 'dragdoll'; // Create a keyboard sensor instance that listens to keydown events and starts // emitting drag events when the provided element is focused and a start key // (enter or space) is pressed. const element = document.querySelector('.draggable') as HTMLElement; const keyboardSensor = new KeyboardSensor(element); // Listen to drag events. keyboardSensor.on('start', (e) => console.log('drag started', e)); keyboardSensor.on('move', (e) => console.log('drag move', e)); keyboardSensor.on('end', (e) => console.log('drag ended', e)); keyboardSensor.on('cancel', (e) => console.log('drag canceled', e)); // Use the sensor to move an element. const draggable = new Draggable([keyboardSensor], { elements: () => [element], }); ``` -------------------------------- ### KeyboardMotionSensor Basic Usage Example Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/keyboard-motion-sensor.md Demonstrates how to import, instantiate, and use KeyboardMotionSensor to listen for drag events ('start', 'move', 'end', 'cancel', 'tick') and integrate it with a Draggable instance for controlling element movement. ```ts import { KeyboardMotionSensor, Draggable } from 'dragdoll'; // Create a keyboard motion sensor instance that listens to keydown events and // starts emitting drag events when the provided element is focused and a start // key is pressed. const element = document.querySelector('.draggable') as HTMLElement; const sensor = new KeyboardMotionSensor(element); // Listen to drag events. sensor.on('start', (e) => console.log('drag started', e)); sensor.on('move', (e) => console.log('drag move', e)); sensor.on('end', (e) => console.log('drag ended', e)); sensor.on('cancel', (e) => console.log('drag canceled', e)); sensor.on('tick', () => console.log('tick')); // Use the sensor to move an element. const draggable = new Draggable([sensor], { elements: () => [element] }); ``` -------------------------------- ### Example Usage of createTouchDelayPredicate with Draggable Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/draggable-helpers.md This example demonstrates how to integrate `createTouchDelayPredicate` into a `Draggable` instance. It shows the setup with `PointerSensor` and `KeyboardSensor`, and how to configure `touchDelay` and `fallback` options for custom drag start logic. This helper is experimental and may not work reliably, especially with scroll prevention on touch devices or within iframes. ```ts import { Draggable, PointerSensor, KeyboardSensor, createTouchDelayPredicate } from 'draggable'; const element = document.querySelector('.draggable') as HTMLElement; const pointerSensor = new PointerSensor(element); const keyboardSensor = new KeyboardSensor(element); const draggable = new Draggable([pointerSensor, keyboardSensor], { elements: () => [element], startPredicate: createTouchDelayPredicate({ // The amount of time in milliseconds to wait before trying to start // dragging after the user has touched the pointer sensor element. touchDelay: 200, fallback: (data) => { // Here you can decide what to do with the other sensors' events. // For example, if you are using the keyboard sensor and pointer sensor // together and user uses both the keyboard and the pointer at the same // time, you can decide the logic for keyboard events here (when should // the dragging start). console.log(data); return true; } }) }); ``` -------------------------------- ### DragDoll Development Environment Setup Commands Source: https://github.com/niklasramo/dragdoll/blob/main/CONTRIBUTING.md This section outlines the essential `npm` commands required to set up and manage the DragDoll development environment. These commands facilitate building, formatting, linting, and testing the project locally. ```Shell npm ci ``` ```Shell npm run build ``` ```Shell npm run format ``` ```Shell npm run lint ``` ```Shell npm run test ``` ```Shell npm run test-local ``` -------------------------------- ### BaseSensor Protected Method: _start Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/base-sensor.md Initiates the drag process internally and emits a drag 'start' event with the provided data. This method is intended for use by extending classes to control the drag flow. ```ts // Type type _start = (data: { type: 'start'; x: number; y: number }) => void; ``` ```ts // Usage baseSensor._start({ type: 'start', x: 100, y: 200 }); ``` -------------------------------- ### BaseMotionSensor Methods Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/base-motion-sensor.md Documents the `on` method of `BaseMotionSensor`, detailing its type signature, parameters, return value, and an example of its usage for event listening. ```APIDOC on: ( type: 'start' | 'move' | 'cancel' | 'end' | 'destroy' | 'tick', listener: ( e: | { type: 'start' | 'move' | 'end' | 'cancel'; x: number; y: number; } | { type: 'tick'; time: number; deltaTime: number; } | { type: 'destroy'; }, ) => void, listenerId?: ListenerId, ) => ListenerId; type ListenerId = null | string | number | symbol | Function | Object; // Usage baseMotionSensor.on('start', (e) => { console.log('start', e); }); // Adds a listener to a sensor event. Returns a listener id, which can be used to remove this specific listener. By default this will always be a symbol unless manually provided. ``` -------------------------------- ### Draggable Element with Center-to-Pointer Alignment (Dragdoll) Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/examples.md This example demonstrates how to implement a draggable element using the 'dragdoll' library. It configures a custom 'positionModifier' to align the dragged element's center with the pointer's position at the start of the drag. The snippet includes TypeScript logic for draggable setup, HTML structure for the element, and CSS for styling and layout. ```ts import { Draggable, PointerSensor, KeyboardMotionSensor } from 'dragdoll'; const element = document.querySelector('.draggable') as HTMLElement; const pointerSensor = new PointerSensor(element); const keyboardSensor = new KeyboardMotionSensor(element); const draggable = new Draggable([pointerSensor, keyboardSensor], { elements: () => [element], positionModifiers: [ (change, { drag, item, phase }) => { // Align the dragged element so that the pointer // is in the center of the element. if ( // Only apply the alignment on the start phase. phase === 'start' && // Only apply the alignment for the pointer sensor. drag.sensor instanceof PointerSensor && // Only apply the alignment for the primary drag element. drag.items[0].element === item.element ) { const { clientRect } = item; const { x, y } = drag.startEvent; const targetX = clientRect.x + clientRect.width / 2; const targetY = clientRect.y + clientRect.height / 2; change.x = x - targetX; change.y = y - targetY; } return change; }, ], onStart: () => { element.classList.add('dragging'); }, onEnd: () => { element.classList.remove('dragging'); }, }); ``` ```html Draggable - Center To Pointer
``` ```css body { width: 100%; height: 100%; display: flex; flex-flow: row wrap; justify-content: center; align-items: center; align-content: center; gap: 10px 10px; } .card.draggable { position: relative; flex-grow: 0; flex-shrink: 0; } ``` ```css :root { --bg-color: #111; --color: rgba(255, 255, 245, 0.86); --theme-color: #ff5555; --card-color: rgba(0, 0, 0, 0.7); --card-bgColor: var(--theme-color); --card-color--focus: var(--card-color); --card-bgColor--focus: #db55ff; --card-color--drag: var(--card-color); --card-bgColor--drag: #55ff9c; } * { box-sizing: border-box; } html { height: 100%; background: var(--bg-color); color: var(--color); background-size: 40px 40px; background-image: linear-gradient(to right, rgba(255, 255, 255, 0.1) 1px, transparent 1px), linear-gradient(to bottom, rgba(255, 255, 255, 0.1) 1px, transparent 1px); } body { margin: 0; overflow: hidden; } .card { display: flex; justify-content: center; align-items: center; width: 100px; height: 100px; } ``` -------------------------------- ### onStart Callback Type Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/draggable.md A callback function invoked at the end of the drag start phase. This phase handles applying the initial positions to the dragged elements, setting up frozen styles, and performing any other initial DOM write operations. This callback is executed immediately after any 'start' events added via the 'on' method. ```ts type onStart = (drag: DraggableDrag, draggable: Draggable) => void; ``` -------------------------------- ### KeyboardMotionSensor updateSettings Method API Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/keyboard-motion-sensor.md Type definition and usage example for the `updateSettings` method. This method allows for dynamic modification of the sensor's configuration. Only the provided options will be updated. ```APIDOC updateSettings(options?: Partial): void; Usage: keyboardMotionSensor.updateSettings({ startPredicate: () => { if (Math.random() > 0.5) { return { x: 0, y: 0 }; } } }); ``` -------------------------------- ### onPrepareStart Callback Type Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/draggable.md A callback function invoked at the end of the drag start preparation phase. In this phase, draggable item instances are created, initial positions are computed, and all necessary DOM reading for the drag start is completed. This callback is executed immediately after any 'preparestart' events added via the 'on' method. ```ts type onPrepareStart = (drag: DraggableDrag, draggable: Draggable) => void; ``` -------------------------------- ### DraggableSettings Type - startPredicate Property Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/draggable.md Defines the `startPredicate` function within `DraggableSettings`. This function determines whether a drag operation should begin. It receives data about the draggable, sensor, and event, returning `true` to start, `false` to prevent, or `undefined` to defer the decision. ```APIDOC type startPredicate = (data: { draggable: Draggable; sensor: Sensor; event: SensorStartEvent | SensorMoveEvent; }) => boolean | undefined; Description: A function that determines if drag should start or not. Each sensor has its own predicate state. If one sensor's predicate resolves, others are rejected. Return: - true: Resolve predicate and start drag. - false: Reject predicate and prevent drag. - undefined: Keep pending, try again after next 'move' event. Default: () => true ``` -------------------------------- ### Basic PointerSensor and Draggable Usage in TypeScript Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/pointer-sensor.md Demonstrates how to create a `PointerSensor` instance, listen to its drag events (`start`, `move`, `end`, `cancel`), and integrate it with a `Draggable` instance to move a DOM element. ```ts import { PointerSensor, Draggable } from 'dragdoll'; // Create a pointer sensor instance which tracks pointer/touch/mouse events in // window and emits drag events. const pointerSensor = new PointerSensor(window); // Listen to events. pointerSensor.on('start', (e) => console.log('drag started', e)); pointerSensor.on('move', (e) => console.log('drag move', e)); pointerSensor.on('end', (e) => console.log('drag ended', e)); pointerSensor.on('cancel', (e) => console.log('drag canceled', e)); // Use the sensor to move an element. const dragElement = document.querySelector('.dragElement'); const draggable = new Draggable([pointerSensor], { elements: () => [dragElement], }); ``` -------------------------------- ### Configure DragDoll for Multiple Element Dragging Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/examples.md This example demonstrates how to set up DragDoll to enable dragging multiple HTML elements concurrently. It initializes `Draggable` instances for each element, using `PointerSensor` and `KeyboardMotionSensor`, and configures the `elements` callback to return the current element along with all other draggable elements. The `onStart` and `onEnd` callbacks manage a 'dragging' class for visual feedback. ```ts import { Draggable, PointerSensor, KeyboardMotionSensor } from 'dragdoll'; const draggableElements = [...document.querySelectorAll('.draggable')] as HTMLElement[]; draggableElements.forEach((element) => { const otherElements = draggableElements.filter((el) => el !== element); const pointerSensor = new PointerSensor(element); const keyboardSensor = new KeyboardMotionSensor(element); const draggable = new Draggable([pointerSensor, keyboardSensor], { elements: () => { return [element, ...otherElements]; }, startPredicate: () => { return !element.classList.contains('dragging'); }, onStart: (drag) => { drag.items.forEach((item) => { item.element.classList.add('dragging'); }); }, onEnd: (drag) => { drag.items.forEach((item) => { item.element.classList.remove('dragging'); }); } }); }); ``` ```html Draggable - Multiple Elements
100, }); const draggable = new Draggable([pointerSensor, keyboardSensor], { container: dragContainer, elements: () => [element], frozenStyles: () => ['left', 'top'], onStart: () => { element.classList.add('dragging'); }, onEnd: () => { element.classList.remove('dragging'); }, }).use( autoScrollPlugin({ targets: [ { element: window, axis: 'y', padding: { top: Infinity, bottom: Infinity }, }, ], }), ); ``` ```html Draggable - Transformed
``` ```css body { height: 300%; overflow-y: auto; } .drag-container-outer { position: fixed; left: 0; top: 0; width: 0px; height: 0px; transform: scale(0.3) skew(10deg, 10deg); transform-origin: 17px 37px; } .drag-container { position: absolute; left: 10px; top: 10px; } ``` -------------------------------- ### KeyboardMotionSensor startPredicate Setting API Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/keyboard-motion-sensor.md Type definition and default implementation for the `startPredicate` function. This function determines if a drag should start, returning an object with `x` and `y` coordinates for the initial drag position, or `null`/`undefined` to prevent drag. The default predicate checks if the sensor's element is focused. ```APIDOC type startPredicate = (e: KeyboardEvent, sensor: KeyboardMotionSensor) => { x: number; y: number } | null | undefined; Default: (_e, sensor) => { if (sensor.element && document.activeElement === sensor.element) { const { left, top } = sensor.element.getBoundingClientRect(); return { x: left, y: top }; } return null; }; ``` -------------------------------- ### PointerSensor Method: on (Add Listener) Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/pointer-sensor.md Documents the `on` method of `PointerSensor`, used to add a listener for specific sensor events like `start`, `move`, `cancel`, `end`, or `destroy`. Explains the return value (listener ID) and its usage for removal. ```APIDOC // Type type on = ( type: 'start' | 'move' | 'cancel' | 'end' | 'destroy', listener: ( e: | { type: 'start' | 'move' | 'end'; x: number; y: number; pointerId: number; pointerType: 'mouse' | 'pen' | 'touch'; srcEvent: PointerEvent | TouchEvent | MouseEvent; target: EventTarget | null; } | { type: 'cancel'; x: number; y: number; pointerId: number; pointerType: 'mouse' | 'pen' | 'touch'; srcEvent: PointerEvent | TouchEvent | MouseEvent | null; target: EventTarget | null; } | { type: 'destroy'; }, ) => void, listenerId?: ListenerId, ) => ListenerId; type ListenerId = null | string | number | symbol | Function | Object; // Usage pointerSensor.on('start', (e) => { console.log('start', e); }); type: 'start' | 'move' | 'cancel' | 'end' | 'destroy' - The type of event to listen for. listener: Function - The callback function to execute when the event occurs. listenerId?: ListenerId - An optional ID to assign to the listener for later removal. Returns ListenerId - A unique ID for the added listener. ``` -------------------------------- ### Update KeyboardSensor Settings (TypeScript) Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/keyboard-sensor.md Defines the `updateSettings` type for the KeyboardSensor and provides an example of how to use it to modify sensor behavior. It accepts a partial `KeyboardSensorSettings` object, allowing for selective updates to properties like `startPredicate`. ```TypeScript // Type type updateSettings = (options?: Partial) => void; // Usage keyboardSensor.updateSettings({ startPredicate: () => { if (Math.random() > 0.5) { return { x: 0, y: 0 }; } }, }); ``` -------------------------------- ### Implement Draggable with Custom Drag Handle using DragDoll Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/examples.md This example demonstrates how to create a draggable element with a specific drag handle using the DragDoll library. It utilizes `PointerSensor` for the handle and `KeyboardMotionSensor` for the main draggable element, showcasing how to initialize `Draggable` and handle `onStart` and `onEnd` events to apply CSS classes for visual feedback during dragging. The HTML provides the structure for the draggable card and its handle, while the CSS styles the elements and provides visual feedback for different dragging states. ```typescript import { Draggable, PointerSensor, KeyboardMotionSensor } from 'dragdoll'; const element = document.querySelector('.draggable') as HTMLElement; const handle = element.querySelector('.handle') as HTMLElement; const pointerSensor = new PointerSensor(handle); const keyboardSensor = new KeyboardMotionSensor(element); const draggable = new Draggable([pointerSensor, keyboardSensor], { elements: () => [element], onStart: () => { element.classList.add('dragging'); if (draggable.drag!.sensor instanceof PointerSensor) { element.classList.add('pointer-dragging'); } else { element.classList.add('keyboard-dragging'); } }, onEnd: () => { element.classList.remove('dragging', 'pointer-dragging', 'keyboard-dragging'); }, }); ``` ```html Draggable - Drag Handle
``` ```css body { width: 100%; height: 100%; display: flex; flex-flow: row wrap; justify-content: center; align-items: center; align-content: center; gap: 10px 10px; } .card.draggable { position: relative; flex-grow: 0; flex-shrink: 0; cursor: auto; touch-action: auto; & .handle { touch-action: none; display: flex; justify-content: center; align-items: center; cursor: grab; border-radius: 4px; background-color: rgba(0, 0, 0, 0.2); width: 40px; height: 40px; position: absolute; top: 4px; right: 4px; .card.pointer-dragging & { cursor: grabbing; } .card.keyboard-dragging & { cursor: auto; } & svg { width: 24px; height: 24px; } @media (hover: hover) and (pointer: fine) { ``` -------------------------------- ### Draggable Element with Auto Scroll and Container Constraints Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/examples.md This snippet showcases the setup of a draggable element using the `dragdoll` library. It includes configuring `PointerSensor` and `KeyboardMotionSensor` for input, defining a `drag-container`, freezing specific CSS properties (`left`, `top`) during drag, and integrating the `autoScrollPlugin` to enable viewport scrolling when the draggable element approaches the edges. The HTML provides the structural elements, while CSS handles their initial positioning and styling. ```TypeScript import { Draggable, PointerSensor, KeyboardMotionSensor, autoScrollPlugin } from 'dragdoll'; const element = document.querySelector('.draggable') as HTMLElement; const dragContainer = document.querySelector('.drag-container') as HTMLElement; const pointerSensor = new PointerSensor(element); const keyboardSensor = new KeyboardMotionSensor(element, { computeSpeed: () => 100, }); const draggable = new Draggable([pointerSensor, keyboardSensor], { container: dragContainer, elements: () => [element], frozenStyles: () => ['left', 'top'], onStart: () => { element.classList.add('dragging'); }, onEnd: () => { element.classList.remove('dragging'); }, }).use( autoScrollPlugin({ targets: [ { element: window, axis: 'y', padding: { top: Infinity, bottom: Infinity }, }, ], }), ); ``` ```HTML Draggable - Auto Scroll
``` ```CSS body { height: 300%; overflow-y: auto; } .drag-container { position: fixed; left: 0; top: 0; width: 0px; height: 0px; } .card-container { position: absolute; inset: 0; } .card.draggable { position: absolute; left: 50%; top: 50%; transform: translateX(-50%) translateY(-50%); } ``` ```CSS :root { --bg-color: #111; } ``` -------------------------------- ### Initialize Draggable with AutoScroll Plugin and Custom Targets Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/draggable-auto-scroll-plugin.md This example demonstrates how to import and initialize a Draggable instance with PointerSensor and KeyboardSensor. It integrates the autoScrollPlugin, configuring it to autoscroll the window vertically when the dragged element is within 100 pixels of the window's edge. It also shows how to dynamically update the plugin's settings after initialization. ```ts import { PointerSensor, KeyboardSensor, Draggable, autoScrollPlugin } from 'dragdoll'; const element = document.querySelector('.draggable') as HTMLElement; const pointerSensor = new PointerSensor(element); const keyboardSensor = new KeyboardSensor(element); const draggable = new Draggable([pointerSensor, keyboardSensor], { elements: () => [element], }).use( autoScrollPlugin({ targets: [ { // Autoscroll the window. element: window, // Vertically only. axis: 'y', // When the dragged element's edge is 100 pixels (or less) from the // window's edge. threshold: 100, }, ], }), ); // Update settings later if need be. draggable.plugins.autoscroll.updateSettings({ axis: 'x', threshold: 200, }); ``` -------------------------------- ### KeyboardSensor Settings Reference Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/keyboard-sensor.md Details the configurable settings for the KeyboardSensor, allowing fine-grained control over drag behavior. These settings include movement distance, automatic cancellation conditions, and custom predicate functions for starting, moving, ending, or canceling a drag. ```APIDOC moveDistance: number | { x: number; y: number } Description: The number of pixels the x and/or y values are shifted per "move" event. Can be a single number for both axes or an object for separate x/y shifts. Defaults to 25. cancelOnBlur: boolean Description: If true, the drag will be canceled when the element is blurred. Defaults to true. cancelOnVisibilityChange: boolean Description: If true, the drag will be canceled on document's visibility change (e.g., when the tab is hidden). Defaults to true. startPredicate: (e: KeyboardEvent, sensor: KeyboardSensor) => { x: number; y: number } | null | undefined Description: A function called on keydown when drag is not active. Should return drag start coordinates (client x and y) if drag should begin, otherwise null or undefined. movePredicate: (e: KeyboardEvent, sensor: KeyboardSensor) => { x: number; y: number } | null | undefined Description: A function called on keydown when drag is active. Should return drag's next coordinates (client x and y) if movement is needed, otherwise null or undefined. endPredicate: (e: KeyboardEvent, sensor: KeyboardSensor) => { x: number; y: number } | null | undefined Description: A function called on keydown when drag is active. Should return drag's end coordinates (client x and y) if drag needs to end, otherwise null or undefined. cancelPredicate: (e: KeyboardEvent, sensor: KeyboardSensor) => { x: number; y: number } | null | undefined Description: A function called on keydown when drag is active. Should return drag's end coordinates (client x and y) if drag needs to be canceled, otherwise null or undefined. ``` -------------------------------- ### Access and Update AutoScroll Plugin Settings at Runtime Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/draggable-auto-scroll-plugin.md This example demonstrates how to programmatically access the current settings of the `autoscroll` plugin via `draggable.plugins.autoscroll.settings`. It also shows how to update specific settings, such as the `speed`, using the `updateSettings` method, allowing for dynamic adjustments during application runtime. ```ts // Read current speed setting. const currentSpeed = draggable.plugins.autoscroll.settings.speed; // Update current speed setting. draggable.plugins.autoscroll.updateSettings({ speed: 1000, }); ``` -------------------------------- ### PointerSensor Settings: startPredicate Type Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/pointer-sensor.md Defines the `startPredicate` function type, which allows custom logic to determine whether a drag process should be initiated based on the initial event. Explains its return value and default behavior. ```APIDOC type startPredicate = (e: PointerEvent | TouchEvent | MouseEvent) => boolean; e: PointerEvent | TouchEvent | MouseEvent - The initial event. Returns boolean - true to allow drag, false to prevent. Defaults to (e) => ('button' in e && e.button > 0 ? false : true). ``` -------------------------------- ### Implement Basic Draggable Elements with Dragdoll Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/examples.md This snippet demonstrates how to set up basic draggable elements using the `dragdoll` library. It initializes `Draggable` instances for multiple HTML elements, combining `PointerSensor` and `KeyboardMotionSensor` for input. The `onStart` and `onEnd` callbacks manage CSS classes and z-index during dragging. ```ts import { Draggable, PointerSensor, KeyboardMotionSensor } from 'dragdoll'; let zIndex = 0; const draggableElements = [...document.querySelectorAll('.draggable')] as HTMLElement[]; draggableElements.forEach((element) => { const pointerSensor = new PointerSensor(element); const keyboardSensor = new KeyboardMotionSensor(element); const draggable = new Draggable([pointerSensor, keyboardSensor], { elements: () => [element], onStart: () => { element.classList.add('dragging'); element.style.zIndex = `${++zIndex}`; }, onEnd: () => { element.classList.remove('dragging'); }, }); }); ``` ```html Draggable - Basic
``` -------------------------------- ### Get Drag Container World Transform Matrix (TypeScript) Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/draggable-drag-item.md Returns the world transform matrix of the `dragContainer`, which is computed and cached at the start of the drag. The first matrix in the returned array is the world transform matrix and the second matrix is the inverse of the world transform matrix. ```TypeScript type getDragContainerMatrix = () => [DOMMatrix, DOMMatrix]; ``` -------------------------------- ### KeyboardMotionSensor Constructor API Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/keyboard-motion-sensor.md Defines the constructor for the `KeyboardMotionSensor` class. It accepts an optional `element` which must be focused to initiate drag with default settings, and an optional `options` object for initial configuration. ```APIDOC class KeyboardMotionSensor { constructor(element: Element | null, options?: Partial) element: The element which should be focused to start the drag. Can be null. options: Optional settings object for the sensor. ``` -------------------------------- ### Forcibly Stop Draggable's Current Drag Process Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/draggable.md Forcibly stops the draggable's current drag process. This method cannot be called within `preparestart` or `start` event listeners or callbacks, as the drag start process cannot be interrupted during those phases. It can be called before, after, or between those phases. ```APIDOC type stop = () => void; ``` ```ts draggable.stop(); ``` -------------------------------- ### PointerSensor Class Constructor API Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/pointer-sensor.md Documents the constructor for the `PointerSensor` class, explaining its `element` and `options` parameters, and their purpose in configuring event tracking. ```APIDOC class PointerSensor { constructor(element: HTMLElement | Window, options?: Partial) {} } element: HTMLElement | Window - The element (or window) which's events will be tracked. options?: Partial - An optional settings object, which you can also change later via updateSettings method. ``` -------------------------------- ### Instantiate Draggable and Attach Logger Plugin Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/draggable-plugins.md This TypeScript snippet demonstrates how to initialize a `Draggable` instance with a `KeyboardSensor` and then attach the custom `loggerPlugin`. It shows how to pass configuration options to the plugin during the `use` call and how to access the registered plugin instance via `draggable.plugins`. ```typescript // Now let's put the plugin to use. const element = document.querySelector('.draggable') as HTMLElement; const keyboardSensor = new KeyboardSensor(element); const draggable = new Draggable([keyboardSensor], { elements: () => [element], }).use(loggerPlugin({ events: ['start', 'end'] })); // You can also access the logger plugin instance any time you want. console.log(draggable.plugins.logger.name); ``` -------------------------------- ### Autoscroll Start Callback Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/draggable-auto-scroll-plugin.md A callback function that will be invoked when an autoscroll target begins autoscrolling. It receives the `scrollElement` (the scrolled element or window) and `scrollDirection` as arguments. Defaults to `null`. ```TypeScript type onStart = null | ( scrollElement: HTMLElement | Window, scrollDirection: 'none' | 'left' | 'right' | 'up' | 'down'; ) => void; ``` ```APIDOC onStart: Type: null | (scrollElement: HTMLElement | Window, scrollDirection: 'none' | 'left' | 'right' | 'up' | 'down') => void Default: null Description: A callback that will be called when an autoscroll target starts autoscrolling. Parameters: scrollElement: HTMLElement | Window Description: The scrolled element or window. scrollDirection: 'none' | 'left' | 'right' | 'up' | 'down' Description: The direction of the scroll. ``` -------------------------------- ### BaseSensor Constructor Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/base-sensor.md Initializes a new instance of the `BaseSensor` class. It accepts no arguments. ```ts class BaseSensor { constructor() {} } ``` -------------------------------- ### KeyboardMotionSensor startKeys Setting API Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/keyboard-motion-sensor.md Type definition and default value for the `startKeys` array. Specifies which keyboard keys can initiate a drag operation. ```APIDOC type startKeys = string[]; Default: [' ', 'Enter']; ``` -------------------------------- ### createTouchDelayPredicate Parameters and Return Value Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/draggable-helpers.md Detailed documentation for the parameters accepted by the `createTouchDelayPredicate` function, including `touchDelay` and `fallback`, their types, descriptions, default values, and optionality, along with the function's return type. ```APIDOC createTouchDelayPredicate: Parameters: touchDelay: Type: number Description: The amount of time in milliseconds to wait before trying to start dragging after the user has touched the pointer sensor element (applies for touch events only). The point of this delay is to allow users to scroll normally if they don't intend to drag and start dragging only after long press, which is a common pattern in mobile applications. Default: 250 Optional: true fallback: Type: DraggableStartPredicate Description: Fallback start predicate function that will be called for other sensors' events (e.g. keyboard sensor) if there are any. Default: () => true Optional: true Returns: A start predicate function that can be provided to the `startPredicate` option of the `Draggable` constructor. ``` -------------------------------- ### PointerSensor Method: updateSettings Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/pointer-sensor.md Documents the `updateSettings` method of `PointerSensor`, allowing partial updates to the sensor's configuration options. Explains that only provided options will be modified. ```APIDOC // Type type updateSettings = (options?: Partial) => void; // Usage pointerSensor.updateSettings({ startPredicate: () => Math.random() > 0.5, }); options?: Partial - An object containing the settings to update. Only the provided properties will be changed. ``` -------------------------------- ### Implement Draggable Element with Grid Snapping using Dragdoll Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/examples.md This snippet demonstrates how to create a draggable HTML element that snaps to a defined grid using the `dragdoll` library. It includes the TypeScript logic for initializing the `Draggable` instance with `PointerSensor` and `KeyboardSensor`, applying the `createSnapModifier`, and handling drag start/end events. The accompanying HTML and CSS define the structure and styling of the draggable component and the overall page, including a base stylesheet for global styles and a specific stylesheet for the draggable card. ```typescript import { Draggable, PointerSensor, KeyboardSensor, createSnapModifier } from 'dragdoll'; const GRID_WIDTH = 40; const GRID_HEIGHT = 40; const element = document.querySelector('.draggable') as HTMLElement; const pointerSensor = new PointerSensor(element); const keyboardSensor = new KeyboardSensor(element, { moveDistance: { x: GRID_WIDTH, y: GRID_HEIGHT }, }); const draggable = new Draggable([pointerSensor, keyboardSensor], { elements: () => [element], positionModifiers: [createSnapModifier(GRID_WIDTH, GRID_HEIGHT)], onStart: () => { element.classList.add('dragging'); }, onEnd: () => { element.classList.remove('dragging'); }, }); ``` ```html Draggable - Snap To Grid
``` ```css .card.draggable { position: absolute; left: 0; top: 0; width: 80px; height: 80px; } ``` ```css :root { --bg-color: #111; --color: rgba(255, 255, 245, 0.86); --theme-color: #ff5555; --card-color: rgba(0, 0, 0, 0.7); --card-bgColor: var(--theme-color); --card-color--focus: var(--card-color); --card-bgColor--focus: #db55ff; --card-color--drag: var(--card-color); --card-bgColor--drag: #55ff9c; } * { box-sizing: border-box; } html { height: 100%; background: var(--bg-color); color: var(--color); background-size: 40px 40px; background-image: linear-gradient(to right, rgba(255, 255, 255, 0.1) 1px, transparent 1px), linear-gradient(to bottom, rgba(255, 255, 255, 0.1) 1px, transparent 1px); } body { margin: 0; overflow: hidden; } .card { display: flex; justify-content: center; align-items: center; width: 100px; height: 100px; background-color: var(--card-bgColor); color: var(--card-color); border-radius: 7px; border: 1.5px solid var(--bg-color); font-size: 30px; & svg { width: 1em; height: 1em; fill: var(--card-color); } @media (hover: hover) and (pointer: fine) { &:hover, &:focus-visible { background-color: var(--card-bgColor--focus); color: var(--card-color--focus); & svg { fill: var(--card-color--focus); } } &:focus-visible { outline-offset: 4px; outline: 1px solid var(--card-bgColor--focus); } } &.draggable { cursor: grab; touch-action: none; } &.dragging { cursor: grabbing; background-color: var(--card-bgColor--drag); color: var(--card-color--drag); & svg { fill: var(--card-color--drag); } @media (hover: hover) and (pointer: fine) { &:focus-visible { outline: 1px solid var(--card-bgColor--drag); } } } } ``` -------------------------------- ### Draggable Initialization with Custom Position Modifiers Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/draggable-modifiers.md This example demonstrates how to initialize a Draggable instance and apply custom position modifiers. The first modifier restricts movement to the y-axis by setting 'change.x' to 0, while the second modifier inverts the y-axis movement. ```typescript import { Draggable, PointerSensor, KeyboardSensor } from 'draggable'; const element = document.querySelector('.draggable') as HTMLElement; const pointerSensor = new PointerSensor(element); const keyboardSensor = new KeyboardSensor(element); const draggable = new Draggable([pointerSensor, keyboardSensor], { elements: () => [element], positionModifiers: [ // First modifier, which resets the // x-axis change to allow only y-axis // change. (change) => { change.x = 0; return change; }, // Second modifier, which inverts // y-axis change. (change) => { change.y *= -1; return change; }, ], }); ``` -------------------------------- ### BaseSensor Method: on Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/base-sensor.md Adds a listener for a specific sensor event type ('start', 'move', 'cancel', 'end', 'destroy'). It returns a unique listener ID that can be used for removal. The listener receives an event object with type and coordinates, or just type for 'destroy' events. ```ts // Type type on = ( type: 'start' | 'move' | 'cancel' | 'end' | 'destroy', listener: ( e: | { type: 'start' | 'move' | 'end' | 'cancel'; x: number; y: number; } | { type: 'destroy'; }, ) => void, listenerId?: ListenerId, ) => ListenerId; type ListenerId = null | string | number | symbol | Function | Object; ``` ```ts // Usage baseSensor.on('start', (e) => { console.log('start', e); }); ``` -------------------------------- ### Example Usage of Containment Modifier with Draggable Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/draggable-containment-modifier.md Demonstrates how to integrate `createContainmentModifier` with the `Draggable` class to restrict an element's movement within the window's bounds. It initializes `PointerSensor` and `KeyboardSensor` and applies the modifier to the `positionModifiers` option of the Draggable instance. ```ts import { Draggable, PointerSensor, KeyboardSensor, createContainmentModifier } from 'draggable'; const element = document.querySelector('.draggable') as HTMLElement; const pointerSensor = new PointerSensor(element); const keyboardSensor = new KeyboardSensor(element); const draggable = new Draggable([pointerSensor, keyboardSensor], { elements: () => [element], positionModifiers: createContainmentModifier(() => { // Contain within window's bounds. return { x: 0, y: 0, width: window.innerWidth, height: window.innerHeight, }; }), }); ``` -------------------------------- ### DragDoll createTouchDelayPredicate Helper Reference Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/tips-and-tricks.md A helper function provided by DragDoll to implement a long-press-to-drag pattern. This pattern is familiar from mobile apps and can be used to delay drag activation on touch devices, helping to avoid immediate conflicts with native scrolling. ```APIDOC createTouchDelayPredicate(): Description: Helper function to enable long-press-to-drag behavior. Purpose: Addresses touch device scrolling interference by delaying drag activation. Caution: Test thoroughly across devices due to potential scroll prevention issues, especially within iframes. ``` -------------------------------- ### Recompute Draggable Element Positions Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/draggable.md Recomputes the positions of all dragged elements if they have drifted, for example, due to scrolling. This method should be called if a dragged element's position inadvertently goes out of sync. While Draggable automatically calls this during scrolling, manual realignment can be performed using this method. ```APIDOC type align = (instant?: boolean) => void; ``` ```ts // Usage: update asynchronously on the next animation frame. draggable.align(); // Usage: update instantly. May cause extra reflows (jank). draggable.align(true); ``` -------------------------------- ### KeyboardSensor Constructor Definition Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/keyboard-sensor.md Defines the constructor for the KeyboardSensor class, specifying the required element that must be focused to initiate a drag and an optional settings object for customization. The settings can also be updated later via the updateSettings method. ```APIDOC class KeyboardSensor { constructor(element: Element | null, options?: KeyboardSensorSettings) {} } Parameters: element: Element | null Description: The element which should be focused to start the drag. Can be null if a custom startPredicate is provided. options: KeyboardSensorSettings (optional) Description: An optional settings object to configure the sensor's behavior. ``` -------------------------------- ### Implement Draggable Containment with dragdoll Source: https://github.com/niklasramo/dragdoll/blob/main/docs/docs/examples.md This set of code snippets demonstrates how to initialize a Draggable instance with PointerSensor and KeyboardMotionSensor, applying a containment modifier to restrict the draggable element's movement within the browser window. It includes the TypeScript logic, the HTML structure for the draggable element, and the necessary CSS for styling. ```ts import { Draggable, PointerSensor, KeyboardMotionSensor, createContainmentModifier, } from 'dragdoll'; const element = document.querySelector('.draggable') as HTMLElement; const pointerSensor = new PointerSensor(element); const keyboardSensor = new KeyboardMotionSensor(element); const draggable = new Draggable([pointerSensor, keyboardSensor], { elements: () => [element], positionModifiers: [ createContainmentModifier(() => { return { x: 0, y: 0, width: window.innerWidth, height: window.innerHeight, }; }), ], onStart: () => { element.classList.add('dragging'); }, onEnd: () => { element.classList.remove('dragging'); }, }); ``` ```html Draggable - Containment
``` ```css body { width: 100%; height: 100%; display: flex; flex-flow: row wrap; justify-content: center; align-items: center; align-content: center; gap: 10px 10px; } .card.draggable { position: relative; flex-grow: 0; flex-shrink: 0; } ``` ```css :root { --bg-color: #111; --color: rgba(255, 255, 245, 0.86); --theme-color: #ff5555; --card-color: rgba(0, 0, 0, 0.7); --card-bgColor: var(--theme-color); } ```