AI-Generated forms in seconds.
KickStart your next FormKit form in seconds. Generate from a prompt, image, or text attachment. Copy & paste as Vue components or FormKit schema.
Introduction
FormKit’s Drag and Drop is a small library for adding drag and drop functionality to your app. It’s simple, flexible, framework agnostic, and clocks in at only ~5kb gzipped. Drag and Drop works with React, Solid, Vue, Svelte, or any JavaScript framework. This library differs from others in the way the drag and drop operations are performed. Rather than moving elements around the DOM manually, Drag and Drop updates the reactive data model used by the provided render function.
Getting Started
Install
Drag and Drop is published as @formkit/drag-and-drop on npm. Click to copy the install command for your package manager of choice:
- npm install @formkit/drag-and-drop
- pnpm add @formkit/drag-and-drop
- yarn add @formkit/drag-and-drop
- bun install @formkit/drag-and-drop
Usage
Drag and drop ships two main functions: dragAndDrop and useDragAndDrop. These can be imported for your framework of choice by using the subpath import @formkit/drag-and-drop/{react/vue/solid}. A native JavaScript version of the dragAndDrop function is also available at @formkit/drag-and-drop.
Whichever method you choose, the core concept remains the same: FormKit's Drag and Drop accepts two main arguments — the parent element containing the draggable items and the data that those items represent.
useDragAndDrop
The useDragAndDrop hook is the most convenient way to add Drag and Drop to your app and is available for React, Vue, and Solid. Call this function with an initial array of values and the configuration options. It returns an array of values containing a a template ref, a reactive set of values (and a setter in the case of React), as well as a function to update the parent's config. The template ref should be assigned to the parent DOM element of the items you wish to make draggable. The reactive array of values should be used in your template to render the draggable elements.
// Example parent configuration object:const config = { sortable: false }
// React:const [parentRef, values, setValues, updateConfig] = useDragAndDrop(
['Item 1', 'Item2', 'Item3'],
config
)
// Vue:const [parentRef, values, updateConfig] = useDragAndDrop(
['Item 1', 'Item2', 'Item3'],
config
) dragAndDrop
The dragAndDrop hook allows you to define your own ref and list state. This is useful if you need to integrate with a pre-existing app without changing your component structure.
// Example parent configuration object:const config = { sortable: false }
// React:dragAndDrop({
parent: parentRef,
state: [values, setValues],
config
})
// Vue:dragAndDrop({
parent: parentRef,
values: valueRef,
config
})
// Vanilla JS: (import from core, not subpath)
dragAndDrop({
parent: HTMLElement,
getValues: () => {
// Return array of values
},
setValues: (newValues) =>
// Do something with updated values
},
config
}) Core Features
Sortability
Using useDragAndDrop or dragAndDrop automatically makes a lists sortable. Dragging items within your list will automatically change your list’s state to reflect the new order (which in turn allows the framework to re-render to the correct order):
- React
- Vue
- Solid
- Native
import React from "react";
import { function useDragAndDrop<E extends HTMLElement, T = unknown>(list: T[], options?: Partial<ParentConfig<T>>): [React.RefObject<E>, T[], React.Dispatch<React.SetStateAction<T[]>>, (config: Partial<ParentConfig<...>>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop } from "@formkit/drag-and-drop/react";
export function function myComponent(): React.JSX.ElementmyComponent() {
const [const parent: React.RefObject<HTMLUListElement>parent, const tapes: string[]tapes] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>([
"Depeche Mode",
"Duran Duran",
"Pet Shop Boys",
"Kraftwerk",
"Tears for Fears",
"Spandau Ballet",
]);
return (
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const parent: React.RefObject<HTMLUListElement>parent}>
{const tapes: string[]tapes.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((tape: stringtape) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<HTMLLIElement>.className?: string | undefinedclassName="cassette" data-label: stringdata-label={tape: stringtape} React.Attributes.key?: React.Key | null | undefinedkey={tape: stringtape}>
{tape: stringtape}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
);
}
import React from "react";
import { function useDragAndDrop<E extends HTMLElement, T = unknown>(list: T[], options?: Partial<ParentConfig<T>>): [React.RefObject<E>, T[], React.Dispatch<React.SetStateAction<T[]>>, (config: Partial<ParentConfig<...>>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop } from "@formkit/drag-and-drop/react";
export function function myComponent(): React.JSX.ElementmyComponent() {
const [const parent: React.RefObject<HTMLUListElement>parent, const tapes: string[]tapes] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>([
"Depeche Mode",
"Duran Duran",
"Pet Shop Boys",
"Kraftwerk",
"Tears for Fears",
"Spandau Ballet",
]);
return (
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const parent: React.RefObject<HTMLUListElement>parent}>
{const tapes: string[]tapes.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((tape: stringtape) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<HTMLLIElement>.className?: string | undefinedclassName="cassette" data-label: stringdata-label={tape: stringtape} React.Attributes.key?: React.Key | null | undefinedkey={tape: stringtape}>
{tape: stringtape}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
);
}

- Depeche Mode

- Duran Duran

- Pet Shop Boys

- Kraftwerk

- Tears for Fears

- Spandau Ballet

["Depeche Mode","Duran Duran","Pet Shop Boys","Kraftwerk","Tears for Fears","Spandau Ballet"]
Draggable
For drag-and-drop to function correctly, the length of array of values provided must be equivalent to the number of immediate children to the parent element. If they are not, a console warning will appear: The number of draggable items in the parent does not match the number of values, which may lead to unexpected behavior." The most common cause of this is when the parent element has an immediate child that is not meant to be draggable. In this case, the configuration property draggable can be set to a callback function that lets you determine whether or not a given element should be draggable:
- React
- Vue
- Solid
- Native
import React from "react";
import { function useDragAndDrop<E extends HTMLElement, T = unknown>(list: T[], options?: Partial<ParentConfig<T>>): [React.RefObject<E>, T[], React.Dispatch<React.SetStateAction<T[]>>, (config: Partial<ParentConfig<...>>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop } from "@formkit/drag-and-drop/react";
export function function myComponent(): React.JSX.ElementmyComponent() {
const [const parent: React.RefObject<HTMLUListElement>parent, const tapes: string[]tapes] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
[
"Depeche Mode",
"Duran Duran",
"Pet Shop Boys",
"Kraftwerk",
"Tears for Fears",
"Spandau Ballet",
],
{
draggable?: ((child: HTMLElement) => boolean) | undefinedA function that returns whether a given node is draggable.draggable: (el: HTMLElementel) => {
return el: HTMLElementel.Element.id: stringReturns the value of element's id content attribute. Can be set to change it.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/id)id !== "no-drag";
},
}
);
return (
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const parent: React.RefObject<HTMLUListElement>parent}>
{const tapes: string[]tapes.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((tape: stringtape) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<HTMLLIElement>.className?: string | undefinedclassName="cassette" data-label: stringdata-label={tape: stringtape} React.Attributes.key?: React.Key | null | undefinedkey={tape: stringtape}>
{tape: stringtape}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.id?: string | undefinedid="no-drag">I am NOT draggable</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
);
}
import React from "react";
import { function useDragAndDrop<E extends HTMLElement, T = unknown>(list: T[], options?: Partial<ParentConfig<T>>): [React.RefObject<E>, T[], React.Dispatch<React.SetStateAction<T[]>>, (config: Partial<ParentConfig<...>>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop } from "@formkit/drag-and-drop/react";
export function function myComponent(): React.JSX.ElementmyComponent() {
const [const parent: React.RefObject<HTMLUListElement>parent, const tapes: string[]tapes] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
[
"Depeche Mode",
"Duran Duran",
"Pet Shop Boys",
"Kraftwerk",
"Tears for Fears",
"Spandau Ballet",
],
{
draggable?: ((child: HTMLElement) => boolean) | undefinedA function that returns whether a given node is draggable.draggable: (el: HTMLElementel) => {
return el: HTMLElementel.Element.id: stringReturns the value of element's id content attribute. Can be set to change it.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/id)id !== "no-drag";
},
}
);
return (
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const parent: React.RefObject<HTMLUListElement>parent}>
{const tapes: string[]tapes.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((tape: stringtape) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<HTMLLIElement>.className?: string | undefinedclassName="cassette" data-label: stringdata-label={tape: stringtape} React.Attributes.key?: React.Key | null | undefinedkey={tape: stringtape}>
{tape: stringtape}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.id?: string | undefinedid="no-drag">I am NOT draggable</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
);
}

- Depeche Mode

- Duran Duran

- Pet Shop Boys

- Kraftwerk

- Tears for Fears

- Spandau Ballet

- I am NOT draggable
["Depeche Mode","Duran Duran","Pet Shop Boys","Kraftwerk","Tears for Fears","Spandau Ballet"]
Transferability
In addition to sorting, drag-and-drop allows transferring values between parents (lists). To enable this, set the configuration property group to the same value for each list that should allow value transfers. Notice in the example below that items can be appended to the end of the list by hovering over the list itself, or inserted into the list by hovering over the target parent's draggable items.
- React
- Vue
- Solid
- Native
import React from "react";
import { function useDragAndDrop<E extends HTMLElement, T = unknown>(list: T[], options?: Partial<ParentConfig<T>>): [React.RefObject<E>, T[], React.Dispatch<React.SetStateAction<T[]>>, (config: Partial<ParentConfig<...>>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop } from "@formkit/drag-and-drop/react";
export function function myComponent(): React.JSX.ElementmyComponent() {
const const todoItems: string[]todoItems = [
"Schedule perm",
"Rewind VHS tapes",
"Make change for the arcade",
"Get disposable camera developed",
"Learn C++",
"Return Nintendo Power Glove",
];
const const doneItems: string[]doneItems = ["Pickup new mix-tape from Beth"];
const [const todoList: React.RefObject<HTMLUListElement>todoList, const todos: string[]todos] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
const todoItems: string[]todoItems,
{ group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group: "todoList" }
);
const [const doneList: React.RefObject<HTMLUListElement>doneList, const dones: string[]dones] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
const doneItems: string[]doneItems,
{ group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group: "todoList" }
);
return (
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="kanban-board">
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const todoList: React.RefObject<HTMLUListElement>todoList}>
{const todos: string[]todos.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((todo: stringtodo) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<T>.className?: string | undefinedclassName="kanban-item" React.Attributes.key?: React.Key | null | undefinedkey={todo: stringtodo}>
{todo: stringtodo}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const doneList: React.RefObject<HTMLUListElement>doneList}>
{const dones: string[]dones.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((done: stringdone) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<T>.className?: string | undefinedclassName="kanban-item" React.Attributes.key?: React.Key | null | undefinedkey={done: stringdone}>
{done: stringdone}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
);
}
import React from "react";
import { function useDragAndDrop<E extends HTMLElement, T = unknown>(list: T[], options?: Partial<ParentConfig<T>>): [React.RefObject<E>, T[], React.Dispatch<React.SetStateAction<T[]>>, (config: Partial<ParentConfig<...>>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop } from "@formkit/drag-and-drop/react";
export function function myComponent(): React.JSX.ElementmyComponent() {
const const todoItems: string[]todoItems = [
"Schedule perm",
"Rewind VHS tapes",
"Make change for the arcade",
"Get disposable camera developed",
"Learn C++",
"Return Nintendo Power Glove",
];
const const doneItems: string[]doneItems = ["Pickup new mix-tape from Beth"];
const [const todoList: React.RefObject<HTMLUListElement>todoList, const todos: string[]todos] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
const todoItems: string[]todoItems,
{ group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group: "todoList" }
);
const [const doneList: React.RefObject<HTMLUListElement>doneList, const dones: string[]dones] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
const doneItems: string[]doneItems,
{ group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group: "todoList" }
);
return (
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="kanban-board">
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const todoList: React.RefObject<HTMLUListElement>todoList}>
{const todos: string[]todos.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((todo: stringtodo) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<T>.className?: string | undefinedclassName="kanban-item" React.Attributes.key?: React.Key | null | undefinedkey={todo: stringtodo}>
{todo: stringtodo}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const doneList: React.RefObject<HTMLUListElement>doneList}>
{const dones: string[]dones.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((done: stringdone) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<T>.className?: string | undefinedclassName="kanban-item" React.Attributes.key?: React.Key | null | undefinedkey={done: stringdone}>
{done: stringdone}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
);
}
ToDos
- Schedule perm
- Rewind VHS tapes
- Make change for the arcade
- Get disposable camera developed
- Learn C++
- Return Nintendo Power Glove
["Schedule perm","Rewind VHS tapes","Make change for the arcade","Get disposable camera developed","Learn C++","Return Nintendo Power Glove"]
Complete
- Pickup new mix-tape from Beth
["Pickup new mix-tape from Beth"]
Disable sort
Sortability within a given parent can be toggled on and off by setting the sortable property to in the configuration.
- React
- Vue
- Solid
- Native
import React from "react";
import { function useDragAndDrop<E extends HTMLElement, T = unknown>(list: T[], options?: Partial<ParentConfig<T>>): [React.RefObject<E>, T[], React.Dispatch<React.SetStateAction<T[]>>, (config: Partial<ParentConfig<...>>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop } from "@formkit/drag-and-drop/react";
export function function myComponent(): React.JSX.ElementmyComponent() {
const const todoItems: string[]todoItems = ["Schedule perm", "Rewind VHS tapes", "Make change for the arcade", "Get disposable camera developed", "Learn C++", "Return Nintendo Power Glove"];
const const doneItems: string[]doneItems = ["Pickup new mix-tape from Beth"];
const [const todoList: React.RefObject<HTMLUListElement>todoList, const todos: string[]todos] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
const todoItems: string[]todoItems,
{
group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group: "todoList",
sortable?: boolean | undefinedFlag for whether or not to allow sorting within a given parent.sortable: false
}
);
const [const doneList: React.RefObject<HTMLUListElement>doneList, const dones: string[]dones] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
const doneItems: string[]doneItems,
{
group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group: "todoList",
sortable?: boolean | undefinedFlag for whether or not to allow sorting within a given parent.sortable: false
}
);
return (
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="kanban-board">
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const todoList: React.RefObject<HTMLUListElement>todoList}>
{const todos: string[]todos.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((todo: stringtodo) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<T>.className?: string | undefinedclassName="kanban-item" React.Attributes.key?: React.Key | null | undefinedkey={todo: stringtodo}>
{todo: stringtodo}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const doneList: React.RefObject<HTMLUListElement>doneList}>
{const dones: string[]dones.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((done: stringdone) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<T>.className?: string | undefinedclassName="kanban-item" React.Attributes.key?: React.Key | null | undefinedkey={done: stringdone}>
{done: stringdone}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
);
}
import React from "react";
import { function useDragAndDrop<E extends HTMLElement, T = unknown>(list: T[], options?: Partial<ParentConfig<T>>): [React.RefObject<E>, T[], React.Dispatch<React.SetStateAction<T[]>>, (config: Partial<ParentConfig<...>>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop } from "@formkit/drag-and-drop/react";
export function function myComponent(): React.JSX.ElementmyComponent() {
const const todoItems: string[]todoItems = ["Schedule perm", "Rewind VHS tapes", "Make change for the arcade", "Get disposable camera developed", "Learn C++", "Return Nintendo Power Glove"];
const const doneItems: string[]doneItems = ["Pickup new mix-tape from Beth"];
const [const todoList: React.RefObject<HTMLUListElement>todoList, const todos: string[]todos] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
const todoItems: string[]todoItems,
{
group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group: "todoList",
sortable?: boolean | undefinedFlag for whether or not to allow sorting within a given parent.sortable: false
}
);
const [const doneList: React.RefObject<HTMLUListElement>doneList, const dones: string[]dones] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
const doneItems: string[]doneItems,
{
group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group: "todoList",
sortable?: boolean | undefinedFlag for whether or not to allow sorting within a given parent.sortable: false
}
);
return (
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="kanban-board">
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const todoList: React.RefObject<HTMLUListElement>todoList}>
{const todos: string[]todos.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((todo: stringtodo) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<T>.className?: string | undefinedclassName="kanban-item" React.Attributes.key?: React.Key | null | undefinedkey={todo: stringtodo}>
{todo: stringtodo}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const doneList: React.RefObject<HTMLUListElement>doneList}>
{const dones: string[]dones.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((done: stringdone) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<T>.className?: string | undefinedclassName="kanban-item" React.Attributes.key?: React.Key | null | undefinedkey={done: stringdone}>
{done: stringdone}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
);
}
ToDos
- Schedule perm
- Rewind VHS tapes
- Make change for the arcade
- Get disposable camera developed
- Learn C++
- Return Nintendo Power Glove
["Schedule perm","Rewind VHS tapes","Make change for the arcade","Get disposable camera developed","Learn C++","Return Nintendo Power Glove"]
Complete
- Pickup new mix-tape from Beth
["Pickup new mix-tape from Beth"]
Accepts
For a more nuanced transfer experience, define the accepts property in the configuration. This property is a callback function that lets you determine whether or not a given element can be transferred into the given list.
- React
- Vue
- Solid
- Native
import React from "react";
import type { interface ParentConfig<T>The configuration object for a given parent.ParentConfig } from "@formkit/drag-and-drop";
import { function useDragAndDrop<E extends HTMLElement, T = unknown>(list: T[], options?: Partial<ParentConfig<T>>): [React.RefObject<E>, T[], React.Dispatch<React.SetStateAction<T[]>>, (config: Partial<ParentConfig<...>>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop } from "@formkit/drag-and-drop/react";
export default function function myComponent(): React.JSX.ElementmyComponent() {
const [const source: React.RefObject<HTMLUListElement>source, const items1: string[]items1] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
["dungeon_master.exe", "map_1.dat", "map_2.dat", "character1.txt", "character2.txt", "shell32.dll", "README.txt",],
{
draggable?: ((child: HTMLElement) => boolean) | undefinedA function that returns whether a given node is draggable.draggable: (el: HTMLElementel) => {
return el: HTMLElementel.Element.id: stringReturns the value of element's id content attribute. Can be set to change it.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/id)id !== "no-drag";
},
}
);
const const config1: Partial<ParentConfig<string>>config1: type Partial<T> = { [P in keyof T]?: T[P] | undefined; }Make all properties in T optionalPartial<interface ParentConfig<T>The configuration object for a given parent.ParentConfig<string>> = {};
const config1: Partial<ParentConfig<string>>config1.accepts?: ((targetParentData: ParentRecord<string>, initialParentData: ParentRecord<string>, currentParentData: ParentRecord<string>, state: BaseDragState<...>) => boolean) | undefinedA function that returns whether a given parent accepts a given node.accepts = (_parent: ParentRecord<string>_parent, lastParent: ParentRecord<string>lastParent) => {
if (lastParent: ParentRecord<string>lastParent.ParentRecord<string>.el: HTMLElementel === const target2: React.RefObject<HTMLElement>target2.React.RefObject<HTMLElement>.current: HTMLElement | nullThe current value of the ref.current) {
return false;
}
return const items2: string[]items2.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length < 3;
};
const const config2: Partial<ParentConfig<string>>config2: type Partial<T> = { [P in keyof T]?: T[P] | undefined; }Make all properties in T optionalPartial<interface ParentConfig<T>The configuration object for a given parent.ParentConfig<string>> = {};
const config2: Partial<ParentConfig<string>>config2.accepts?: ((targetParentData: ParentRecord<string>, initialParentData: ParentRecord<string>, currentParentData: ParentRecord<string>, state: BaseDragState<...>) => boolean) | undefinedA function that returns whether a given parent accepts a given node.accepts = (_parent: ParentRecord<string>_parent, lastParent: ParentRecord<string>lastParent) => {
if (lastParent: ParentRecord<string>lastParent.ParentRecord<string>.el: HTMLElementel === const target1: React.RefObject<HTMLElement>target1.React.RefObject<HTMLElement>.current: HTMLElement | nullThe current value of the ref.current) {
return false;
}
return const items3: string[]items3.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length < 5;
};
const [const target1: React.RefObject<HTMLElement>target1, const items2: string[]items2] = useDragAndDrop<HTMLElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop(["knight.bmp", "dragon.bmp"], const config1: Partial<ParentConfig<string>>config1);
const [const target2: React.RefObject<HTMLElement>target2, const items3: string[]items3] = useDragAndDrop<HTMLElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop(["brick.bmp", "moss.bmp"], const config2: Partial<ParentConfig<string>>config2);
return (
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const source: React.RefObject<HTMLUListElement>source}>
{const items1: string[]items1.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((item: stringitem) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.Attributes.key?: React.Key | null | undefinedkey={item: stringitem}>{item: stringitem}</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref="target1">
{const items2: string[]items2.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((item: stringitem) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.Attributes.key?: React.Key | null | undefinedkey={item: stringitem}>{item: stringitem}</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref="target2">
{const items3: string[]items3.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((item: stringitem) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.Attributes.key?: React.Key | null | undefinedkey={item: stringitem}>{item: stringitem}</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
);
}import React from "react";
import type { interface ParentConfig<T>The configuration object for a given parent.ParentConfig } from "@formkit/drag-and-drop";
import { function useDragAndDrop<E extends HTMLElement, T = unknown>(list: T[], options?: Partial<ParentConfig<T>>): [React.RefObject<E>, T[], React.Dispatch<React.SetStateAction<T[]>>, (config: Partial<ParentConfig<...>>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop } from "@formkit/drag-and-drop/react";
export default function function myComponent(): React.JSX.ElementmyComponent() {
const [const source: React.RefObject<HTMLUListElement>source, const items1: string[]items1] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
["dungeon_master.exe", "map_1.dat", "map_2.dat", "character1.txt", "character2.txt", "shell32.dll", "README.txt",],
{
draggable?: ((child: HTMLElement) => boolean) | undefinedA function that returns whether a given node is draggable.draggable: (el: HTMLElementel) => {
return el: HTMLElementel.Element.id: stringReturns the value of element's id content attribute. Can be set to change it.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/id)id !== "no-drag";
},
}
);
const const config1: Partial<ParentConfig<string>>config1: type Partial<T> = { [P in keyof T]?: T[P] | undefined; }Make all properties in T optionalPartial<interface ParentConfig<T>The configuration object for a given parent.ParentConfig<string>> = {};
const config1: Partial<ParentConfig<string>>config1.accepts?: ((targetParentData: ParentRecord<string>, initialParentData: ParentRecord<string>, currentParentData: ParentRecord<string>, state: BaseDragState<...>) => boolean) | undefinedA function that returns whether a given parent accepts a given node.accepts = (_parent: ParentRecord<string>_parent, lastParent: ParentRecord<string>lastParent) => {
if (lastParent: ParentRecord<string>lastParent.ParentRecord<string>.el: HTMLElementel === const target2: React.RefObject<HTMLElement>target2.React.RefObject<HTMLElement>.current: HTMLElement | nullThe current value of the ref.current) {
return false;
}
return const items2: string[]items2.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length < 3;
};
const const config2: Partial<ParentConfig<string>>config2: type Partial<T> = { [P in keyof T]?: T[P] | undefined; }Make all properties in T optionalPartial<interface ParentConfig<T>The configuration object for a given parent.ParentConfig<string>> = {};
const config2: Partial<ParentConfig<string>>config2.accepts?: ((targetParentData: ParentRecord<string>, initialParentData: ParentRecord<string>, currentParentData: ParentRecord<string>, state: BaseDragState<...>) => boolean) | undefinedA function that returns whether a given parent accepts a given node.accepts = (_parent: ParentRecord<string>_parent, lastParent: ParentRecord<string>lastParent) => {
if (lastParent: ParentRecord<string>lastParent.ParentRecord<string>.el: HTMLElementel === const target1: React.RefObject<HTMLElement>target1.React.RefObject<HTMLElement>.current: HTMLElement | nullThe current value of the ref.current) {
return false;
}
return const items3: string[]items3.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length < 5;
};
const [const target1: React.RefObject<HTMLElement>target1, const items2: string[]items2] = useDragAndDrop<HTMLElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop(["knight.bmp", "dragon.bmp"], const config1: Partial<ParentConfig<string>>config1);
const [const target2: React.RefObject<HTMLElement>target2, const items3: string[]items3] = useDragAndDrop<HTMLElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop(["brick.bmp", "moss.bmp"], const config2: Partial<ParentConfig<string>>config2);
return (
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const source: React.RefObject<HTMLUListElement>source}>
{const items1: string[]items1.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((item: stringitem) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.Attributes.key?: React.Key | null | undefinedkey={item: stringitem}>{item: stringitem}</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref="target1">
{const items2: string[]items2.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((item: stringitem) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.Attributes.key?: React.Key | null | undefinedkey={item: stringitem}>{item: stringitem}</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref="target2">
{const items3: string[]items3.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((item: stringitem) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.Attributes.key?: React.Key | null | undefinedkey={item: stringitem}>{item: stringitem}</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
);
}- dungeon_master.exe
- map_1.dat
- map_2.dat
- character1.txt
- character2.txt
- shell32.dll
- README.txt
- knight.bmp
- dragon.bmp
- brick.bmp
- moss.bmp
Drag Handles
Drag handles are a common way to allow users to drag items without accidentally dragging them when they don't intend to. To implement drag handles, set the dragHandle property to a selector that will be used to find the handle.
- React
- Vue
- Solid
- Native
import React from "react";
import { function useDragAndDrop<E extends HTMLElement, T = unknown>(list: T[], options?: Partial<ParentConfig<T>>): [React.RefObject<E>, T[], React.Dispatch<React.SetStateAction<T[]>>, (config: Partial<ParentConfig<...>>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop } from "@formkit/drag-and-drop/react";
export function function myComponent(): React.JSX.ElementmyComponent() {
const const todoItems: string[]todoItems = ["Schedule perm", "Rewind VHS tapes", "Make change for the arcade", "Get disposable camera developed", "Learn C++", "Return Nintendo Power Glove"];
const const doneItems: string[]doneItems = ["Pickup new mix-tape from Beth", "Implement drag handles"];
const [const todoList: React.RefObject<HTMLUListElement>todoList, const todos: string[]todos] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
const todoItems: string[]todoItems,
{
group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group: "todoList",
dragHandle?: string | undefinedA selector for the drag handle. Will search at any depth within the
draggable element.dragHandle: ".kanban-handle"
}
);
const [const doneList: React.RefObject<HTMLUListElement>doneList, const dones: string[]dones] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
const doneItems: string[]doneItems,
{
group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group: "todoList",
dragHandle?: string | undefinedA selector for the drag handle. Will search at any depth within the
draggable element.dragHandle: ".kanban-handle"
}
);
return (
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="kanban-board">
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const todoList: React.RefObject<HTMLUListElement>todoList} React.HTMLAttributes<T>.className?: string | undefinedclassName="kanban-column">
{const todos: string[]todos.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((todo: stringtodo) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<T>.className?: string | undefinedclassName="kanban-item" React.Attributes.key?: React.Key | null | undefinedkey={todo: stringtodo}>
<JSX.IntrinsicElements.span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>span React.HTMLAttributes<T>.className?: string | undefinedclassName="kanban-handle"></JSX.IntrinsicElements.span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>span>
{todo: stringtodo}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const doneList: React.RefObject<HTMLUListElement>doneList} React.HTMLAttributes<T>.className?: string | undefinedclassName="kanban-column">
{const dones: string[]dones.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((done: stringdone) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<T>.className?: string | undefinedclassName="kanban-item" React.Attributes.key?: React.Key | null | undefinedkey={done: stringdone}>
<JSX.IntrinsicElements.span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>span React.HTMLAttributes<T>.className?: string | undefinedclassName="kanban-handle"></JSX.IntrinsicElements.span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>span>
{done: stringdone}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
);
}
import React from "react";
import { function useDragAndDrop<E extends HTMLElement, T = unknown>(list: T[], options?: Partial<ParentConfig<T>>): [React.RefObject<E>, T[], React.Dispatch<React.SetStateAction<T[]>>, (config: Partial<ParentConfig<...>>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop } from "@formkit/drag-and-drop/react";
export function function myComponent(): React.JSX.ElementmyComponent() {
const const todoItems: string[]todoItems = ["Schedule perm", "Rewind VHS tapes", "Make change for the arcade", "Get disposable camera developed", "Learn C++", "Return Nintendo Power Glove"];
const const doneItems: string[]doneItems = ["Pickup new mix-tape from Beth", "Implement drag handles"];
const [const todoList: React.RefObject<HTMLUListElement>todoList, const todos: string[]todos] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
const todoItems: string[]todoItems,
{
group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group: "todoList",
dragHandle?: string | undefinedA selector for the drag handle. Will search at any depth within the
draggable element.dragHandle: ".kanban-handle"
}
);
const [const doneList: React.RefObject<HTMLUListElement>doneList, const dones: string[]dones] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
const doneItems: string[]doneItems,
{
group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group: "todoList",
dragHandle?: string | undefinedA selector for the drag handle. Will search at any depth within the
draggable element.dragHandle: ".kanban-handle"
}
);
return (
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="kanban-board">
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const todoList: React.RefObject<HTMLUListElement>todoList} React.HTMLAttributes<T>.className?: string | undefinedclassName="kanban-column">
{const todos: string[]todos.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((todo: stringtodo) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<T>.className?: string | undefinedclassName="kanban-item" React.Attributes.key?: React.Key | null | undefinedkey={todo: stringtodo}>
<JSX.IntrinsicElements.span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>span React.HTMLAttributes<T>.className?: string | undefinedclassName="kanban-handle"></JSX.IntrinsicElements.span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>span>
{todo: stringtodo}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const doneList: React.RefObject<HTMLUListElement>doneList} React.HTMLAttributes<T>.className?: string | undefinedclassName="kanban-column">
{const dones: string[]dones.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((done: stringdone) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<T>.className?: string | undefinedclassName="kanban-item" React.Attributes.key?: React.Key | null | undefinedkey={done: stringdone}>
<JSX.IntrinsicElements.span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>span React.HTMLAttributes<T>.className?: string | undefinedclassName="kanban-handle"></JSX.IntrinsicElements.span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>span>
{done: stringdone}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
);
}
ToDos
- Schedule perm
- Rewind VHS tapes
- Make change for the arcade
- Get disposable camera developed
- Learn C++
- Return Nintendo Power Glove
["Schedule perm","Rewind VHS tapes","Make change for the arcade","Get disposable camera developed","Learn C++","Return Nintendo Power Glove"]
Complete
- Pickup new mix-tape from Beth
- Implement drag handles
["Pickup new mix-tape from Beth","Implement drag handles"]
Updating Config
The parent's configuration can be updated idempotently during runtime by using the updateConfig method. In the example below, clicking the button will toggle whether drag and drop is enabled for the parent.
- React
- Vue
- Solid
- Native
import React from "react";
import { function useState<S>(initialState: S | (() => S)): [S, React.Dispatch<React.SetStateAction<S>>] (+1 overload)Returns a stateful value, and a function to update it.useState } from "react";
import { function useDragAndDrop<E extends HTMLElement, T = unknown>(list: T[], options?: Partial<ParentConfig<T>>): [React.RefObject<E>, T[], React.Dispatch<React.SetStateAction<T[]>>, (config: Partial<ParentConfig<...>>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop } from "@formkit/drag-and-drop/react";
export function function myComponent(): React.JSX.ElementmyComponent() {
const [const parent: React.RefObject<HTMLUListElement>parent, const values: string[]values, const _setValues: React.Dispatch<React.SetStateAction<string[]>>_setValues, const updateConfig: (config: Partial<ParentConfig<string>>) => voidupdateConfig] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<
HTMLUListElement,
string
>([
"Depeche Mode",
"Duran Duran",
"Pet Shop Boys",
"Kraftwerk",
"Tears for Fears",
"Spandau Ballet",
]);
const [const disabled: booleandisabled, const setDisabled: React.Dispatch<React.SetStateAction<boolean>>setDisabled] = useState<boolean>(initialState: boolean | (() => boolean)): [boolean, React.Dispatch<React.SetStateAction<boolean>>] (+1 overload)Returns a stateful value, and a function to update it.useState(false);
const const toggleDisabled: () => voidtoggleDisabled = () => {
const setDisabled: (value: React.SetStateAction<boolean>) => voidsetDisabled(!const disabled: booleandisabled);
const updateConfig: (config: Partial<ParentConfig<string>>) => voidupdateConfig({ disabled?: boolean | undefinedA flag to disable dragability of all nodes in the parent.disabled: !const disabled: booleandisabled });
};
return (
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const parent: React.RefObject<HTMLUListElement>parent}>
{const values: string[]values.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((tape: stringtape) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<HTMLLIElement>.className?: string | undefinedclassName="cassette" data-label: stringdata-label={tape: stringtape} React.Attributes.key?: React.Key | null | undefinedkey={tape: stringtape}>
{tape: stringtape}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
<JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefinedonClick={const toggleDisabled: () => voidtoggleDisabled}>
{const disabled: booleandisabled ? "Enable" : "Disable"} drag and drop
</JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
);
}
import React from "react";
import { function useState<S>(initialState: S | (() => S)): [S, React.Dispatch<React.SetStateAction<S>>] (+1 overload)Returns a stateful value, and a function to update it.useState } from "react";
import { function useDragAndDrop<E extends HTMLElement, T = unknown>(list: T[], options?: Partial<ParentConfig<T>>): [React.RefObject<E>, T[], React.Dispatch<React.SetStateAction<T[]>>, (config: Partial<ParentConfig<...>>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop } from "@formkit/drag-and-drop/react";
export function function myComponent(): React.JSX.ElementmyComponent() {
const [const parent: React.RefObject<HTMLUListElement>parent, const values: string[]values, const _setValues: React.Dispatch<React.SetStateAction<string[]>>_setValues, const updateConfig: (config: Partial<ParentConfig<string>>) => voidupdateConfig] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<
HTMLUListElement,
string
>([
"Depeche Mode",
"Duran Duran",
"Pet Shop Boys",
"Kraftwerk",
"Tears for Fears",
"Spandau Ballet",
]);
const [const disabled: booleandisabled, const setDisabled: React.Dispatch<React.SetStateAction<boolean>>setDisabled] = useState<boolean>(initialState: boolean | (() => boolean)): [boolean, React.Dispatch<React.SetStateAction<boolean>>] (+1 overload)Returns a stateful value, and a function to update it.useState(false);
const const toggleDisabled: () => voidtoggleDisabled = () => {
const setDisabled: (value: React.SetStateAction<boolean>) => voidsetDisabled(!const disabled: booleandisabled);
const updateConfig: (config: Partial<ParentConfig<string>>) => voidupdateConfig({ disabled?: boolean | undefinedA flag to disable dragability of all nodes in the parent.disabled: !const disabled: booleandisabled });
};
return (
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const parent: React.RefObject<HTMLUListElement>parent}>
{const values: string[]values.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((tape: stringtape) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<HTMLLIElement>.className?: string | undefinedclassName="cassette" data-label: stringdata-label={tape: stringtape} React.Attributes.key?: React.Key | null | undefinedkey={tape: stringtape}>
{tape: stringtape}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
<JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefinedonClick={const toggleDisabled: () => voidtoggleDisabled}>
{const disabled: booleandisabled ? "Enable" : "Disable"} drag and drop
</JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
);
}

- Depeche Mode

- Duran Duran

- Pet

- Kraftwerk

- Tears for Fears

- Spandau Ballet

Multi Drag
Multi drag allows users to select multiple items and drag them all at once. To enable multi drag, set the multiDrag property to true in the configuration.
- React
- Vue
- Solid
- Native
import React from "react";
import { function useDragAndDrop<E extends HTMLElement, T = unknown>(list: T[], options?: Partial<ParentConfig<T>>): [React.RefObject<E>, T[], React.Dispatch<React.SetStateAction<T[]>>, (config: Partial<ParentConfig<...>>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop } from "@formkit/drag-and-drop/react";
export function function myComponent(): React.JSX.ElementmyComponent() {
const const mockFileNames: string[]mockFileNames = [
"dungeon_master.exe",
"map_1.dat",
"map_2.dat",
"character1.txt",
"character2.txt",
"shell32.dll",
"README.txt",
];
const [const parent1: React.RefObject<HTMLUListElement>parent1, const files1: string[]files1] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
const mockFileNames: string[]mockFileNames,
{
group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group: "A",
multiDrag?: boolean | undefinedmultiDrag: true,
selectedClass?: string | undefinedThe class to add to a node when it is selected (clicked or pressed).selectedClass: "bg-blue-500 text-white",
}
);
const [const parent2: React.RefObject<HTMLUListElement>parent2, const files2: string[]files2] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>([], {
group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group: "A",
multiDrag?: boolean | undefinedmultiDrag: true,
selectedClass?: string | undefinedThe class to add to a node when it is selected (clicked or pressed).selectedClass: "bg-blue-500 text-white",
});
return (
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="file-manager">
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const parent1: React.RefObject<HTMLUListElement>parent1} React.HTMLAttributes<T>.className?: string | undefinedclassName="file-list">
{const files1: string[]files1.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((file: stringfile) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.Attributes.key?: React.Key | null | undefinedkey={file: stringfile} React.HTMLAttributes<T>.className?: string | undefinedclassName="file">
{file: stringfile}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const parent2: React.RefObject<HTMLUListElement>parent2} React.HTMLAttributes<T>.className?: string | undefinedclassName="file-list">
{const files2: string[]files2.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((file: stringfile) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.Attributes.key?: React.Key | null | undefinedkey={file: stringfile} React.HTMLAttributes<T>.className?: string | undefinedclassName="file">
{file: stringfile}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
);
}
import React from "react";
import { function useDragAndDrop<E extends HTMLElement, T = unknown>(list: T[], options?: Partial<ParentConfig<T>>): [React.RefObject<E>, T[], React.Dispatch<React.SetStateAction<T[]>>, (config: Partial<ParentConfig<...>>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop } from "@formkit/drag-and-drop/react";
export function function myComponent(): React.JSX.ElementmyComponent() {
const const mockFileNames: string[]mockFileNames = [
"dungeon_master.exe",
"map_1.dat",
"map_2.dat",
"character1.txt",
"character2.txt",
"shell32.dll",
"README.txt",
];
const [const parent1: React.RefObject<HTMLUListElement>parent1, const files1: string[]files1] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
const mockFileNames: string[]mockFileNames,
{
group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group: "A",
multiDrag?: boolean | undefinedmultiDrag: true,
selectedClass?: string | undefinedThe class to add to a node when it is selected (clicked or pressed).selectedClass: "bg-blue-500 text-white",
}
);
const [const parent2: React.RefObject<HTMLUListElement>parent2, const files2: string[]files2] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>([], {
group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group: "A",
multiDrag?: boolean | undefinedmultiDrag: true,
selectedClass?: string | undefinedThe class to add to a node when it is selected (clicked or pressed).selectedClass: "bg-blue-500 text-white",
});
return (
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="file-manager">
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const parent1: React.RefObject<HTMLUListElement>parent1} React.HTMLAttributes<T>.className?: string | undefinedclassName="file-list">
{const files1: string[]files1.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((file: stringfile) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.Attributes.key?: React.Key | null | undefinedkey={file: stringfile} React.HTMLAttributes<T>.className?: string | undefinedclassName="file">
{file: stringfile}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const parent2: React.RefObject<HTMLUListElement>parent2} React.HTMLAttributes<T>.className?: string | undefinedclassName="file-list">
{const files2: string[]files2.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((file: stringfile) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.Attributes.key?: React.Key | null | undefinedkey={file: stringfile} React.HTMLAttributes<T>.className?: string | undefinedclassName="file">
{file: stringfile}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
);
}
- dungeon_master.exe
- map_1.dat
- map_2.dat
- character1.txt
- character2.txt
- shell32.dll
- README.txt
Plugins
Plugins are a powerful way to extend the functionality of the library. They can be used to add new features, modify existing ones, or even create entirely new experiences.
Drop or Swap
The examples seen so far have shown the values of a given list being updated as the end-user drags over the list and its children. To enable behavior where the values of the list are only updated when the user drops the item, use the dropOrSwap plugin.
- React
- Vue
- Solid
- Native
import React from "react";
import { function dropOrSwap<T>(dropSwapConfig?: DropSwapConfig<T>): (parent: HTMLElement) => {
setup(): void;
} | undefined
dropOrSwap } from "@formkit/drag-and-drop";
import { function useDragAndDrop<E extends HTMLElement, T = unknown>(list: T[], options?: Partial<ParentConfig<T>>): [React.RefObject<E>, T[], React.Dispatch<React.SetStateAction<T[]>>, (config: Partial<ParentConfig<...>>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop } from "@formkit/drag-and-drop/react";
export function function myComponent(): React.JSX.ElementmyComponent() {
const const todoItems: string[]todoItems = [
"Schedule perm",
"Rewind VHS tapes",
"Make change for the arcade",
"Get disposable camera developed",
"Learn C++",
"Return Nintendo Power Glove",
];
const [const todoSwap: booleantodoSwap, const setTodoSwap: React.Dispatch<React.SetStateAction<boolean>>setTodoSwap] = React.function React.useState<boolean>(initialState: boolean | (() => boolean)): [boolean, React.Dispatch<React.SetStateAction<boolean>>] (+1 overload)Returns a stateful value, and a function to update it.useState(false);
const [const doneSwap: booleandoneSwap, const setDoneSwap: React.Dispatch<React.SetStateAction<boolean>>setDoneSwap] = React.function React.useState<boolean>(initialState: boolean | (() => boolean)): [boolean, React.Dispatch<React.SetStateAction<boolean>>] (+1 overload)Returns a stateful value, and a function to update it.useState(false);
const const doneItems: string[]doneItems = ["Pickup new mix-tape from Beth"];
const [const todoList: React.RefObject<HTMLUListElement>todoList, const todos: string[]todos] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
const todoItems: string[]todoItems,
{
group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group: "todoList",
plugins?: DNDPlugin[] | undefinedAn array of functions to use for a given parent.plugins: [
dropOrSwap<unknown>(dropSwapConfig?: DropSwapConfig<unknown> | undefined): (parent: HTMLElement) => {
setup(): void;
} | undefined
dropOrSwap({
DropSwapConfig<unknown>.shouldSwap?: ((data: ShouldSwapData<unknown>) => boolean) | undefinedshouldSwap: () => {
return const todoSwap: booleantodoSwap;
},
}),
],
}
);
const [const doneList: React.RefObject<HTMLUListElement>doneList, const dones: string[]dones] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
const doneItems: string[]doneItems,
{
group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group: "todoList",
plugins?: DNDPlugin[] | undefinedAn array of functions to use for a given parent.plugins: [
dropOrSwap<unknown>(dropSwapConfig?: DropSwapConfig<unknown> | undefined): (parent: HTMLElement) => {
setup(): void;
} | undefined
dropOrSwap({
DropSwapConfig<unknown>.shouldSwap?: ((data: ShouldSwapData<unknown>) => boolean) | undefinedshouldSwap: () => {
return const doneSwap: booleandoneSwap;
},
}),
],
}
);
function function (local function) toggleTodoSwap(): voidtoggleTodoSwap() {
const setTodoSwap: (value: React.SetStateAction<boolean>) => voidsetTodoSwap(!const todoSwap: booleantodoSwap);
}
function function (local function) toggleDoneSwap(): voidtoggleDoneSwap() {
const setDoneSwap: (value: React.SetStateAction<boolean>) => voidsetDoneSwap(!const doneSwap: booleandoneSwap);
}
return (
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="kanban-board">
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const todoList: React.RefObject<HTMLUListElement>todoList}>
{const todos: string[]todos.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((todo: stringtodo) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<T>.className?: string | undefinedclassName="kanban-item" React.Attributes.key?: React.Key | null | undefinedkey={todo: stringtodo}>
{todo: stringtodo}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
<JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefinedonClick={function (local function) toggleTodoSwap(): voidtoggleTodoSwap}>
{" "}
Toggle {const todoSwap: booleantodoSwap ? "Drop" : "Swap"}
</JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button>
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const doneList: React.RefObject<HTMLUListElement>doneList}>
{const dones: string[]dones.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((done: stringdone) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<T>.className?: string | undefinedclassName="kanban-item" React.Attributes.key?: React.Key | null | undefinedkey={done: stringdone}>
{done: stringdone}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
<JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefinedonClick={function (local function) toggleDoneSwap(): voidtoggleDoneSwap}>
Toggle {const doneSwap: booleandoneSwap ? "Drop" : "Swap"}
</JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
);
}
import React from "react";
import { function dropOrSwap<T>(dropSwapConfig?: DropSwapConfig<T>): (parent: HTMLElement) => {
setup(): void;
} | undefined
dropOrSwap } from "@formkit/drag-and-drop";
import { function useDragAndDrop<E extends HTMLElement, T = unknown>(list: T[], options?: Partial<ParentConfig<T>>): [React.RefObject<E>, T[], React.Dispatch<React.SetStateAction<T[]>>, (config: Partial<ParentConfig<...>>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop } from "@formkit/drag-and-drop/react";
export function function myComponent(): React.JSX.ElementmyComponent() {
const const todoItems: string[]todoItems = [
"Schedule perm",
"Rewind VHS tapes",
"Make change for the arcade",
"Get disposable camera developed",
"Learn C++",
"Return Nintendo Power Glove",
];
const [const todoSwap: booleantodoSwap, const setTodoSwap: React.Dispatch<React.SetStateAction<boolean>>setTodoSwap] = React.function React.useState<boolean>(initialState: boolean | (() => boolean)): [boolean, React.Dispatch<React.SetStateAction<boolean>>] (+1 overload)Returns a stateful value, and a function to update it.useState(false);
const [const doneSwap: booleandoneSwap, const setDoneSwap: React.Dispatch<React.SetStateAction<boolean>>setDoneSwap] = React.function React.useState<boolean>(initialState: boolean | (() => boolean)): [boolean, React.Dispatch<React.SetStateAction<boolean>>] (+1 overload)Returns a stateful value, and a function to update it.useState(false);
const const doneItems: string[]doneItems = ["Pickup new mix-tape from Beth"];
const [const todoList: React.RefObject<HTMLUListElement>todoList, const todos: string[]todos] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
const todoItems: string[]todoItems,
{
group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group: "todoList",
plugins?: DNDPlugin[] | undefinedAn array of functions to use for a given parent.plugins: [
dropOrSwap<unknown>(dropSwapConfig?: DropSwapConfig<unknown> | undefined): (parent: HTMLElement) => {
setup(): void;
} | undefined
dropOrSwap({
DropSwapConfig<unknown>.shouldSwap?: ((data: ShouldSwapData<unknown>) => boolean) | undefinedshouldSwap: () => {
return const todoSwap: booleantodoSwap;
},
}),
],
}
);
const [const doneList: React.RefObject<HTMLUListElement>doneList, const dones: string[]dones] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
const doneItems: string[]doneItems,
{
group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group: "todoList",
plugins?: DNDPlugin[] | undefinedAn array of functions to use for a given parent.plugins: [
dropOrSwap<unknown>(dropSwapConfig?: DropSwapConfig<unknown> | undefined): (parent: HTMLElement) => {
setup(): void;
} | undefined
dropOrSwap({
DropSwapConfig<unknown>.shouldSwap?: ((data: ShouldSwapData<unknown>) => boolean) | undefinedshouldSwap: () => {
return const doneSwap: booleandoneSwap;
},
}),
],
}
);
function function (local function) toggleTodoSwap(): voidtoggleTodoSwap() {
const setTodoSwap: (value: React.SetStateAction<boolean>) => voidsetTodoSwap(!const todoSwap: booleantodoSwap);
}
function function (local function) toggleDoneSwap(): voidtoggleDoneSwap() {
const setDoneSwap: (value: React.SetStateAction<boolean>) => voidsetDoneSwap(!const doneSwap: booleandoneSwap);
}
return (
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="kanban-board">
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const todoList: React.RefObject<HTMLUListElement>todoList}>
{const todos: string[]todos.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((todo: stringtodo) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<T>.className?: string | undefinedclassName="kanban-item" React.Attributes.key?: React.Key | null | undefinedkey={todo: stringtodo}>
{todo: stringtodo}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
<JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefinedonClick={function (local function) toggleTodoSwap(): voidtoggleTodoSwap}>
{" "}
Toggle {const todoSwap: booleantodoSwap ? "Drop" : "Swap"}
</JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button>
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const doneList: React.RefObject<HTMLUListElement>doneList}>
{const dones: string[]dones.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((done: stringdone) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<T>.className?: string | undefinedclassName="kanban-item" React.Attributes.key?: React.Key | null | undefinedkey={done: stringdone}>
{done: stringdone}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
<JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefinedonClick={function (local function) toggleDoneSwap(): voidtoggleDoneSwap}>
Toggle {const doneSwap: booleandoneSwap ? "Drop" : "Swap"}
</JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>button>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
);
}
ToDos
- Schedule perm
- Rewind VHS tapes
- Make change for the arcade
- Get disposable camera developed
- Learn C++
- Return Nintendo Power Glove
["Schedule perm","Rewind VHS tapes","Make change for the arcade","Get disposable camera developed","Learn C++","Return Nintendo Power Glove"]
Complete
- Pickup new mix-tape from Beth
["Pickup new mix-tape from Beth"]
Insert (Experimental)
Similar to the dropOrSwap plugin, the insert plugin does not update values until the user drops the item. In addition, the insert plugin will render an insert point between the items to indicate where the item will be placed.
- React
- Vue
- Solid
- Native
import React from "react";
import { function insert<T>(insertConfig: InsertConfig<T>): (parent: HTMLElement) => {
teardown(): void;
setup(): void;
} | undefined
insert } from "@formkit/drag-and-drop";
import { function useDragAndDrop<E extends HTMLElement, T = unknown>(list: T[], options?: Partial<ParentConfig<T>>): [React.RefObject<E>, T[], React.Dispatch<React.SetStateAction<T[]>>, (config: Partial<ParentConfig<...>>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop } from "@formkit/drag-and-drop/react";
const const insertPointClasses: string[]insertPointClasses = [
"absolute",
"bg-blue-500",
"z-[1000]",
"rounded-full",
"duration-[5ms]",
"before:block",
'before:content-["Insert"]',
"before:whitespace-nowrap",
"before:block",
"before:bg-blue-500",
"before:py-1",
"before:px-2",
"before:rounded-full",
"before:text-xs",
"before:absolute",
"before:top-1/2",
"before:left-1/2",
"before:-translate-y-1/2",
"before:-translate-x-1/2",
"before:text-white",
"before:text-xs",
];
export function function myComponent(): React.JSX.ElementmyComponent() {
const const todoItems: string[]todoItems = [
"Schedule perm",
"Rewind VHS tapes",
"Make change for the arcade",
"Get disposable camera developed",
"Learn C++",
"Return Nintendo Power Glove",
];
const [const todoSwap: booleantodoSwap, const setTodoSwap: React.Dispatch<React.SetStateAction<boolean>>setTodoSwap] = React.function React.useState<boolean>(initialState: boolean | (() => boolean)): [boolean, React.Dispatch<React.SetStateAction<boolean>>] (+1 overload)Returns a stateful value, and a function to update it.useState(false);
const [const doneSwap: booleandoneSwap, const setDoneSwap: React.Dispatch<React.SetStateAction<boolean>>setDoneSwap] = React.function React.useState<boolean>(initialState: boolean | (() => boolean)): [boolean, React.Dispatch<React.SetStateAction<boolean>>] (+1 overload)Returns a stateful value, and a function to update it.useState(false);
const const doneItems: string[]doneItems = ["Pickup new mix-tape from Beth"];
const [const todoList: React.RefObject<HTMLUListElement>todoList, const todos: string[]todos] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
const todoItems: string[]todoItems,
{
group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group: "todoList",
plugins?: DNDPlugin[] | undefinedAn array of functions to use for a given parent.plugins: [
insert<unknown>(insertConfig: InsertConfig<unknown>): (parent: HTMLElement) => {
teardown(): void;
setup(): void;
} | undefined
insert({
InsertConfig<unknown>.insertPoint: (parent: ParentRecord<unknown>) => HTMLElementinsertPoint: (parent: ParentRecord<unknown>parent) => {
const const div: HTMLDivElementdiv = var document: Document[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/document)document.Document.createElement<"div">(tagName: "div", options?: ElementCreationOptions): HTMLDivElement (+2 overloads)Creates an instance of the element for the specified tag.createElement("div");
for (const const cls: stringcls of const insertPointClasses: string[]insertPointClasses) const div: HTMLDivElementdiv.Element.classList: DOMTokenListAllows for manipulation of element's class content attribute as a set of whitespace-separated tokens through a DOMTokenList object.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/classList)classList.DOMTokenList.add(...tokens: string[]): voidAdds all arguments passed, except those already present.
Throws a "SyntaxError" DOMException if one of the arguments is the empty string.
Throws an "InvalidCharacterError" DOMException if one of the arguments contains any ASCII whitespace.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMTokenList/add)add(const cls: stringcls);
return const div: HTMLDivElementdiv;
},
}),
],
}
);
const [const doneList: React.RefObject<HTMLUListElement>doneList, const dones: string[]dones] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
const doneItems: string[]doneItems,
{
group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group: "todoList",
plugins?: DNDPlugin[] | undefinedAn array of functions to use for a given parent.plugins: [
insert<unknown>(insertConfig: InsertConfig<unknown>): (parent: HTMLElement) => {
teardown(): void;
setup(): void;
} | undefined
insert({
InsertConfig<unknown>.insertPoint: (parent: ParentRecord<unknown>) => HTMLElementinsertPoint: (parent: ParentRecord<unknown>parent) => {
const const div: HTMLDivElementdiv = var document: Document[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/document)document.Document.createElement<"div">(tagName: "div", options?: ElementCreationOptions): HTMLDivElement (+2 overloads)Creates an instance of the element for the specified tag.createElement("div");
for (const const cls: stringcls of const insertPointClasses: string[]insertPointClasses) const div: HTMLDivElementdiv.Element.classList: DOMTokenListAllows for manipulation of element's class content attribute as a set of whitespace-separated tokens through a DOMTokenList object.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/classList)classList.DOMTokenList.add(...tokens: string[]): voidAdds all arguments passed, except those already present.
Throws a "SyntaxError" DOMException if one of the arguments is the empty string.
Throws an "InvalidCharacterError" DOMException if one of the arguments contains any ASCII whitespace.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMTokenList/add)add(const cls: stringcls);
return const div: HTMLDivElementdiv;
},
}),
],
}
);
return (
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="kanban-board">
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const todoList: React.RefObject<HTMLUListElement>todoList}>
{const todos: string[]todos.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((todo: stringtodo) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<T>.className?: string | undefinedclassName="kanban-item" React.Attributes.key?: React.Key | null | undefinedkey={todo: stringtodo}>
{todo: stringtodo}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const doneList: React.RefObject<HTMLUListElement>doneList}>
{const dones: string[]dones.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((done: stringdone) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<T>.className?: string | undefinedclassName="kanban-item" React.Attributes.key?: React.Key | null | undefinedkey={done: stringdone}>
{done: stringdone}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
);
}
import React from "react";
import { function insert<T>(insertConfig: InsertConfig<T>): (parent: HTMLElement) => {
teardown(): void;
setup(): void;
} | undefined
insert } from "@formkit/drag-and-drop";
import { function useDragAndDrop<E extends HTMLElement, T = unknown>(list: T[], options?: Partial<ParentConfig<T>>): [React.RefObject<E>, T[], React.Dispatch<React.SetStateAction<T[]>>, (config: Partial<ParentConfig<...>>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop } from "@formkit/drag-and-drop/react";
const const insertPointClasses: string[]insertPointClasses = [
"absolute",
"bg-blue-500",
"z-[1000]",
"rounded-full",
"duration-[5ms]",
"before:block",
'before:content-["Insert"]',
"before:whitespace-nowrap",
"before:block",
"before:bg-blue-500",
"before:py-1",
"before:px-2",
"before:rounded-full",
"before:text-xs",
"before:absolute",
"before:top-1/2",
"before:left-1/2",
"before:-translate-y-1/2",
"before:-translate-x-1/2",
"before:text-white",
"before:text-xs",
];
export function function myComponent(): React.JSX.ElementmyComponent() {
const const todoItems: string[]todoItems = [
"Schedule perm",
"Rewind VHS tapes",
"Make change for the arcade",
"Get disposable camera developed",
"Learn C++",
"Return Nintendo Power Glove",
];
const [const todoSwap: booleantodoSwap, const setTodoSwap: React.Dispatch<React.SetStateAction<boolean>>setTodoSwap] = React.function React.useState<boolean>(initialState: boolean | (() => boolean)): [boolean, React.Dispatch<React.SetStateAction<boolean>>] (+1 overload)Returns a stateful value, and a function to update it.useState(false);
const [const doneSwap: booleandoneSwap, const setDoneSwap: React.Dispatch<React.SetStateAction<boolean>>setDoneSwap] = React.function React.useState<boolean>(initialState: boolean | (() => boolean)): [boolean, React.Dispatch<React.SetStateAction<boolean>>] (+1 overload)Returns a stateful value, and a function to update it.useState(false);
const const doneItems: string[]doneItems = ["Pickup new mix-tape from Beth"];
const [const todoList: React.RefObject<HTMLUListElement>todoList, const todos: string[]todos] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
const todoItems: string[]todoItems,
{
group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group: "todoList",
plugins?: DNDPlugin[] | undefinedAn array of functions to use for a given parent.plugins: [
insert<unknown>(insertConfig: InsertConfig<unknown>): (parent: HTMLElement) => {
teardown(): void;
setup(): void;
} | undefined
insert({
InsertConfig<unknown>.insertPoint: (parent: ParentRecord<unknown>) => HTMLElementinsertPoint: (parent: ParentRecord<unknown>parent) => {
const const div: HTMLDivElementdiv = var document: Document[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/document)document.Document.createElement<"div">(tagName: "div", options?: ElementCreationOptions): HTMLDivElement (+2 overloads)Creates an instance of the element for the specified tag.createElement("div");
for (const const cls: stringcls of const insertPointClasses: string[]insertPointClasses) const div: HTMLDivElementdiv.Element.classList: DOMTokenListAllows for manipulation of element's class content attribute as a set of whitespace-separated tokens through a DOMTokenList object.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/classList)classList.DOMTokenList.add(...tokens: string[]): voidAdds all arguments passed, except those already present.
Throws a "SyntaxError" DOMException if one of the arguments is the empty string.
Throws an "InvalidCharacterError" DOMException if one of the arguments contains any ASCII whitespace.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMTokenList/add)add(const cls: stringcls);
return const div: HTMLDivElementdiv;
},
}),
],
}
);
const [const doneList: React.RefObject<HTMLUListElement>doneList, const dones: string[]dones] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
const doneItems: string[]doneItems,
{
group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group: "todoList",
plugins?: DNDPlugin[] | undefinedAn array of functions to use for a given parent.plugins: [
insert<unknown>(insertConfig: InsertConfig<unknown>): (parent: HTMLElement) => {
teardown(): void;
setup(): void;
} | undefined
insert({
InsertConfig<unknown>.insertPoint: (parent: ParentRecord<unknown>) => HTMLElementinsertPoint: (parent: ParentRecord<unknown>parent) => {
const const div: HTMLDivElementdiv = var document: Document[MDN Reference](https://developer.mozilla.org/docs/Web/API/Window/document)document.Document.createElement<"div">(tagName: "div", options?: ElementCreationOptions): HTMLDivElement (+2 overloads)Creates an instance of the element for the specified tag.createElement("div");
for (const const cls: stringcls of const insertPointClasses: string[]insertPointClasses) const div: HTMLDivElementdiv.Element.classList: DOMTokenListAllows for manipulation of element's class content attribute as a set of whitespace-separated tokens through a DOMTokenList object.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/classList)classList.DOMTokenList.add(...tokens: string[]): voidAdds all arguments passed, except those already present.
Throws a "SyntaxError" DOMException if one of the arguments is the empty string.
Throws an "InvalidCharacterError" DOMException if one of the arguments contains any ASCII whitespace.
[MDN Reference](https://developer.mozilla.org/docs/Web/API/DOMTokenList/add)add(const cls: stringcls);
return const div: HTMLDivElementdiv;
},
}),
],
}
);
return (
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="kanban-board">
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const todoList: React.RefObject<HTMLUListElement>todoList}>
{const todos: string[]todos.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((todo: stringtodo) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<T>.className?: string | undefinedclassName="kanban-item" React.Attributes.key?: React.Key | null | undefinedkey={todo: stringtodo}>
{todo: stringtodo}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const doneList: React.RefObject<HTMLUListElement>doneList}>
{const dones: string[]dones.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((done: stringdone) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<T>.className?: string | undefinedclassName="kanban-item" React.Attributes.key?: React.Key | null | undefinedkey={done: stringdone}>
{done: stringdone}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
);
}
ToDos
- Schedule perm
- Rewind VHS tapes
- Make change for the arcade
- Get disposable camera developed
- Learn C++
- Return Nintendo Power Glove
["Schedule perm","Rewind VHS tapes","Make change for the arcade","Get disposable camera developed","Learn C++","Return Nintendo Power Glove"]
Complete
- Pickup new mix-tape from Beth
["Pickup new mix-tape from Beth"]
Animations (Experimenal)
To add animations to your drag and drop experience, you can use the animations plugin.
- React
- Vue
- Solid
- Native
import React from "react";
import { function animations(animationsConfig?: Partial<AnimationsConfig>): (parent: HTMLElement) => {
setup(): void;
setupNodeRemap<T>(data: SetupNodeData<T>): void;
} | undefined
animations } from "@formkit/drag-and-drop";
import { function useDragAndDrop<E extends HTMLElement, T = unknown>(list: T[], options?: Partial<ParentConfig<T>>): [React.RefObject<E>, T[], React.Dispatch<React.SetStateAction<T[]>>, (config: Partial<ParentConfig<...>>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop } from "@formkit/drag-and-drop/react";
export function function myComponent(): React.JSX.ElementmyComponent() {
const [const parent: React.RefObject<HTMLUListElement>parent, const tapes: string[]tapes] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
[
"Depeche Mode",
"Duran Duran",
"Pet Shop Boys",
"Kraftwerk",
"Tears for Fears",
"Spandau Ballet",
],
{ plugins?: DNDPlugin[] | undefinedAn array of functions to use for a given parent.plugins: [function animations(animationsConfig?: Partial<AnimationsConfig>): (parent: HTMLElement) => {
setup(): void;
setupNodeRemap<T>(data: SetupNodeData<T>): void;
} | undefined
animations()] }
);
return (
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const parent: React.RefObject<HTMLUListElement>parent}>
{const tapes: string[]tapes.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((tape: stringtape) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<HTMLLIElement>.className?: string | undefinedclassName="cassette" data-label: stringdata-label={tape: stringtape} React.Attributes.key?: React.Key | null | undefinedkey={tape: stringtape}>
{tape: stringtape}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
);
}
import React from "react";
import { function animations(animationsConfig?: Partial<AnimationsConfig>): (parent: HTMLElement) => {
setup(): void;
setupNodeRemap<T>(data: SetupNodeData<T>): void;
} | undefined
animations } from "@formkit/drag-and-drop";
import { function useDragAndDrop<E extends HTMLElement, T = unknown>(list: T[], options?: Partial<ParentConfig<T>>): [React.RefObject<E>, T[], React.Dispatch<React.SetStateAction<T[]>>, (config: Partial<ParentConfig<...>>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop } from "@formkit/drag-and-drop/react";
export function function myComponent(): React.JSX.ElementmyComponent() {
const [const parent: React.RefObject<HTMLUListElement>parent, const tapes: string[]tapes] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
[
"Depeche Mode",
"Duran Duran",
"Pet Shop Boys",
"Kraftwerk",
"Tears for Fears",
"Spandau Ballet",
],
{ plugins?: DNDPlugin[] | undefinedAn array of functions to use for a given parent.plugins: [function animations(animationsConfig?: Partial<AnimationsConfig>): (parent: HTMLElement) => {
setup(): void;
setupNodeRemap<T>(data: SetupNodeData<T>): void;
} | undefined
animations()] }
);
return (
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const parent: React.RefObject<HTMLUListElement>parent}>
{const tapes: string[]tapes.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((tape: stringtape) => (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<HTMLLIElement>.className?: string | undefinedclassName="cassette" data-label: stringdata-label={tape: stringtape} React.Attributes.key?: React.Key | null | undefinedkey={tape: stringtape}>
{tape: stringtape}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
))}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
);
}

- Depeche Mode

- Duran Duran

- Pet Shop Boys

- Kraftwerk

- Tears for Fears

- Spandau Ballet

Events
When using a drag and drop library, often it is useful to be able to listen to particular events, such as when a drag starts or ends. Event listeners can be set at either a global level or at the parent level.
Global
Two global events are available, `dragStarted` and `dragEnded`. In order to listen to global events, import the state object from @formkit/drag-and-drop and register your event listener using the on method.
- React
- Vue
- Solid
- Native
import React from "react";
import { function useState<S>(initialState: S | (() => S)): [S, React.Dispatch<React.SetStateAction<S>>] (+1 overload)Returns a stateful value, and a function to update it.useState } from "react";
import { let state: BaseDragState<unknown>The state of the drag and drop.state } from "@formkit/drag-and-drop";
import { function useDragAndDrop<E extends HTMLElement, T = unknown>(list: T[], options?: Partial<ParentConfig<T>>): [React.RefObject<E>, T[], React.Dispatch<React.SetStateAction<T[]>>, (config: Partial<ParentConfig<...>>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop } from "@formkit/drag-and-drop/react";
export function function App(): React.JSX.ElementApp() {
const [const dragStatus: stringdragStatus, const setDragStatus: React.Dispatch<React.SetStateAction<string>>setDragStatus] = useState<string>(initialState: string | (() => string)): [string, React.Dispatch<React.SetStateAction<string>>] (+1 overload)Returns a stateful value, and a function to update it.useState("Not dragging");
const [const parent: React.RefObject<HTMLUListElement>parent, const items: string[]items] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>([
"🍦 vanilla",
"🍫 chocolate",
"🍓 strawberry",
]);
let state: BaseDragState<unknown>The state of the drag and drop.state.on: (event: string, callback: (data: unknown) => void) => voidon("dragStarted", () => const setDragStatus: (value: React.SetStateAction<string>) => voidsetDragStatus("Dragging"));
let state: BaseDragState<unknown>The state of the drag and drop.state.on: (event: string, callback: (data: unknown) => void) => voidon("dragEnded", () => const setDragStatus: (value: React.SetStateAction<string>) => voidsetDragStatus("Not dragging"));
return (
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
<JSX.IntrinsicElements.strong: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>strong>Rank your favorite flavors</JSX.IntrinsicElements.strong: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>strong>
<JSX.IntrinsicElements.br: React.DetailedHTMLProps<React.HTMLAttributes<HTMLBRElement>, HTMLBRElement>br />
<JSX.IntrinsicElements.span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>span>{const dragStatus: stringdragStatus}</JSX.IntrinsicElements.span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>span>
<JSX.IntrinsicElements.br: React.DetailedHTMLProps<React.HTMLAttributes<HTMLBRElement>, HTMLBRElement>br />
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const parent: React.RefObject<HTMLUListElement>parent}>
{const items: string[]items.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((item: stringitem) => {
return <JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.Attributes.key?: React.Key | null | undefinedkey={item: stringitem}>{item: stringitem}</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>;
})}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
);
}
import React from "react";
import { function useState<S>(initialState: S | (() => S)): [S, React.Dispatch<React.SetStateAction<S>>] (+1 overload)Returns a stateful value, and a function to update it.useState } from "react";
import { let state: BaseDragState<unknown>The state of the drag and drop.state } from "@formkit/drag-and-drop";
import { function useDragAndDrop<E extends HTMLElement, T = unknown>(list: T[], options?: Partial<ParentConfig<T>>): [React.RefObject<E>, T[], React.Dispatch<React.SetStateAction<T[]>>, (config: Partial<ParentConfig<...>>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop } from "@formkit/drag-and-drop/react";
export function function App(): React.JSX.ElementApp() {
const [const dragStatus: stringdragStatus, const setDragStatus: React.Dispatch<React.SetStateAction<string>>setDragStatus] = useState<string>(initialState: string | (() => string)): [string, React.Dispatch<React.SetStateAction<string>>] (+1 overload)Returns a stateful value, and a function to update it.useState("Not dragging");
const [const parent: React.RefObject<HTMLUListElement>parent, const items: string[]items] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>([
"🍦 vanilla",
"🍫 chocolate",
"🍓 strawberry",
]);
let state: BaseDragState<unknown>The state of the drag and drop.state.on: (event: string, callback: (data: unknown) => void) => voidon("dragStarted", () => const setDragStatus: (value: React.SetStateAction<string>) => voidsetDragStatus("Dragging"));
let state: BaseDragState<unknown>The state of the drag and drop.state.on: (event: string, callback: (data: unknown) => void) => voidon("dragEnded", () => const setDragStatus: (value: React.SetStateAction<string>) => voidsetDragStatus("Not dragging"));
return (
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
<JSX.IntrinsicElements.strong: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>strong>Rank your favorite flavors</JSX.IntrinsicElements.strong: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>strong>
<JSX.IntrinsicElements.br: React.DetailedHTMLProps<React.HTMLAttributes<HTMLBRElement>, HTMLBRElement>br />
<JSX.IntrinsicElements.span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>span>{const dragStatus: stringdragStatus}</JSX.IntrinsicElements.span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>span>
<JSX.IntrinsicElements.br: React.DetailedHTMLProps<React.HTMLAttributes<HTMLBRElement>, HTMLBRElement>br />
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const parent: React.RefObject<HTMLUListElement>parent}>
{const items: string[]items.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((item: stringitem) => {
return <JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.Attributes.key?: React.Key | null | undefinedkey={item: stringitem}>{item: stringitem}</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>;
})}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
);
}
Parent
Parents themselves have their own set of events that can be listened to. These events include: `onDragstart`, `onDragend`, `onSort`, `onTransfer`. To listen to parent events, assign a callback function to the event in the parent's configuration object.
- React
- Vue
- Solid
- Native
import React from "react";
import { function useState<S>(initialState: S | (() => S)): [S, React.Dispatch<React.SetStateAction<S>>] (+1 overload)Returns a stateful value, and a function to update it.useState } from "react";
import { function useDragAndDrop<E extends HTMLElement, T = unknown>(list: T[], options?: Partial<ParentConfig<T>>): [React.RefObject<E>, T[], React.Dispatch<React.SetStateAction<T[]>>, (config: Partial<ParentConfig<...>>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop } from "@formkit/drag-and-drop/react";
export function function App(): React.JSX.ElementApp() {
const [const dragStatus: stringdragStatus, const setDragStatus: React.Dispatch<React.SetStateAction<string>>setDragStatus] = useState<string>(initialState: string | (() => string)): [string, React.Dispatch<React.SetStateAction<string>>] (+1 overload)Returns a stateful value, and a function to update it.useState("Not dragging");
const [const valuesChanged: stringvaluesChanged, const setValuesChanged: React.Dispatch<React.SetStateAction<string>>setValuesChanged] = useState<string>(initialState: string | (() => string)): [string, React.Dispatch<React.SetStateAction<string>>] (+1 overload)Returns a stateful value, and a function to update it.useState("Not sorting");
const [const parent: React.RefObject<HTMLUListElement>parent, const items: string[]items] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
["🍦 vanilla", "🍫 chocolate", "🍓 strawberry"],
{
onDragstart?: DragstartEvent | undefinedFired when a drag is started, whether native drag or syntheticonDragstart: () => {
const setDragStatus: (value: React.SetStateAction<string>) => voidsetDragStatus("Dragging");
},
onDragend?: DragendEvent | undefinedFired when a drag is ended, whether native drag or syntheticonDragend: () => {
const setDragStatus: (value: React.SetStateAction<string>) => voidsetDragStatus("Not dragging");
const setValuesChanged: (value: React.SetStateAction<string>) => voidsetValuesChanged("Not sorting");
},
onSort?: SortEvent | undefinedCallback function for when a sort operation is performed.onSort: (event: SortEventData<T>event) => {
const setValuesChanged: (value: React.SetStateAction<string>) => voidsetValuesChanged(`${event: SortEventData<T>event.SortEventData<T>.previousValues: T[]previousValues} -> ${event: SortEventData<T>event.SortEventData<T>.values: T[]values}`);
},
}
);
return (
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
<JSX.IntrinsicElements.strong: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>strong>Rank your favorite flavors</JSX.IntrinsicElements.strong: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>strong>
<JSX.IntrinsicElements.br: React.DetailedHTMLProps<React.HTMLAttributes<HTMLBRElement>, HTMLBRElement>br />
<JSX.IntrinsicElements.span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>span>{const dragStatus: stringdragStatus}</JSX.IntrinsicElements.span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>span>
<JSX.IntrinsicElements.span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>span>{const valuesChanged: stringvaluesChanged}</JSX.IntrinsicElements.span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>span>
<JSX.IntrinsicElements.br: React.DetailedHTMLProps<React.HTMLAttributes<HTMLBRElement>, HTMLBRElement>br />
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const parent: React.RefObject<HTMLUListElement>parent}>
{const items: string[]items.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((item: stringitem) => {
return <JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.Attributes.key?: React.Key | null | undefinedkey={item: stringitem}>{item: stringitem}</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>;
})}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
);
}
import React from "react";
import { function useState<S>(initialState: S | (() => S)): [S, React.Dispatch<React.SetStateAction<S>>] (+1 overload)Returns a stateful value, and a function to update it.useState } from "react";
import { function useDragAndDrop<E extends HTMLElement, T = unknown>(list: T[], options?: Partial<ParentConfig<T>>): [React.RefObject<E>, T[], React.Dispatch<React.SetStateAction<T[]>>, (config: Partial<ParentConfig<...>>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop } from "@formkit/drag-and-drop/react";
export function function App(): React.JSX.ElementApp() {
const [const dragStatus: stringdragStatus, const setDragStatus: React.Dispatch<React.SetStateAction<string>>setDragStatus] = useState<string>(initialState: string | (() => string)): [string, React.Dispatch<React.SetStateAction<string>>] (+1 overload)Returns a stateful value, and a function to update it.useState("Not dragging");
const [const valuesChanged: stringvaluesChanged, const setValuesChanged: React.Dispatch<React.SetStateAction<string>>setValuesChanged] = useState<string>(initialState: string | (() => string)): [string, React.Dispatch<React.SetStateAction<string>>] (+1 overload)Returns a stateful value, and a function to update it.useState("Not sorting");
const [const parent: React.RefObject<HTMLUListElement>parent, const items: string[]items] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<HTMLUListElement, string>(
["🍦 vanilla", "🍫 chocolate", "🍓 strawberry"],
{
onDragstart?: DragstartEvent | undefinedFired when a drag is started, whether native drag or syntheticonDragstart: () => {
const setDragStatus: (value: React.SetStateAction<string>) => voidsetDragStatus("Dragging");
},
onDragend?: DragendEvent | undefinedFired when a drag is ended, whether native drag or syntheticonDragend: () => {
const setDragStatus: (value: React.SetStateAction<string>) => voidsetDragStatus("Not dragging");
const setValuesChanged: (value: React.SetStateAction<string>) => voidsetValuesChanged("Not sorting");
},
onSort?: SortEvent | undefinedCallback function for when a sort operation is performed.onSort: (event: SortEventData<T>event) => {
const setValuesChanged: (value: React.SetStateAction<string>) => voidsetValuesChanged(`${event: SortEventData<T>event.SortEventData<T>.previousValues: T[]previousValues} -> ${event: SortEventData<T>event.SortEventData<T>.values: T[]values}`);
},
}
);
return (
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
<JSX.IntrinsicElements.strong: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>strong>Rank your favorite flavors</JSX.IntrinsicElements.strong: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>strong>
<JSX.IntrinsicElements.br: React.DetailedHTMLProps<React.HTMLAttributes<HTMLBRElement>, HTMLBRElement>br />
<JSX.IntrinsicElements.span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>span>{const dragStatus: stringdragStatus}</JSX.IntrinsicElements.span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>span>
<JSX.IntrinsicElements.span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>span>{const valuesChanged: stringvaluesChanged}</JSX.IntrinsicElements.span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>span>
<JSX.IntrinsicElements.br: React.DetailedHTMLProps<React.HTMLAttributes<HTMLBRElement>, HTMLBRElement>br />
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const parent: React.RefObject<HTMLUListElement>parent}>
{const items: string[]items.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((item: stringitem) => {
return <JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.Attributes.key?: React.Key | null | undefinedkey={item: stringitem}>{item: stringitem}</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>;
})}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
);
}
Configuration
Each parent can be passed a configuration object. This object can be used to set options like group, drag handles, and or more dynamic options such as determining which direct descendants of a list are draggable. Invocation of useDragAndDrop or dragAndDrop can be used idempotently to update the configuration for a given list.
import type {
interface ParentRecord<T>The parent record, contains the el and the data in the `parents` weakmap.ParentRecord,
type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState,
interface NodeDragEventData<T>The data passed to the node event listener when the event is a drag event.NodeDragEventData,
interface NodePointerEventData<T>The data passed to the node event listener when the event is a pointer event (not a native drag event).NodePointerEventData,
interface NodeEventData<T>The data passed to the node event listener.NodeEventData,
type DNDPlugin = (parent: HTMLElement) => DNDPluginData | undefinedDNDPlugin,
interface PointeroverNodeEvent<T>The payload of the custom event dispatched when a node is "touched" over a
node.PointeroverNodeEvent,
type SetupNode = <T>(data: SetupNodeData<T>) => voidSetupNode,
interface ParentEventData<T>The data passed to the parent event listener.ParentEventData,
type TearDownNode = <T>(data: TearDownNodeData<T>) => voidTearDownNode,
interface PointeroverParentEvent<T>The payload of the custom event dispatched when a node is "touched" over a
parent.PointeroverParentEvent,
type SortEvent = <T>(data: SortEventData<T>) => voidSortEvent,
type TransferEvent = <T>(data: TransferEventData<T>) => voidTransferEvent,
type DragstartEvent = <T>(data: DragstartEventData<T>) => voidDragstartEvent,
type DragendEvent = <T>(data: DragendEventData<T>) => voidDragendEvent,
interface ParentDragEventData<T>ParentDragEventData,
type BaseDragState<T> = {
activeState?: {
node: NodeRecord<T>;
parent: ParentRecord<T>;
};
affectedNodes: Array<NodeRecord<T>>;
currentTargetValue: T | undefined;
emit: (event: string, data: unknown) => void;
... 23 more ...;
frameIdY?: number;
}
BaseDragState,
type NativeDragEffects = "link" | "none" | "copy" | "move"NativeDragEffects,
interface NodeRecord<T>The node record, contains the el and the data in the `nodes` weakmap.NodeRecord,
interface DropSwapConfig<T>DropSwapConfig,
interface InsertConfig<T>InsertConfig,
interface ParentData<T>The data assigned to a given parent in the `parents` weakmap.ParentData,
interface ParentKeydownEventData<T>ParentKeydownEventData,
type SynthDragState<T> = SynthDragStateProps & DragStateProps<T> & BaseDragState<T>The state of the current drag. State is only created when a drag start
event is triggered.SynthDragState,
} from "@formkit/drag-and-drop";
/**
* The configuration object for a given parent.
*/
export interface interface ParentConfig<T>The configuration object for a given parent.ParentConfig<function (type parameter) T in ParentConfig<T>T> {
/**
* A function that returns whether a given parent accepts a given node.
*/
ParentConfig<T>.accepts?: ((targetParentData: ParentRecord<T>, initialParentData: ParentRecord<T>, currentParentData: ParentRecord<T>, state: BaseDragState<T>) => boolean) | undefinedA function that returns whether a given parent accepts a given node.accepts?: (
targetParentData: ParentRecord<T>targetParentData: interface ParentRecord<T>The parent record, contains the el and the data in the `parents` weakmap.ParentRecord<function (type parameter) T in ParentConfig<T>T>,
initialParentData: ParentRecord<T>initialParentData: interface ParentRecord<T>The parent record, contains the el and the data in the `parents` weakmap.ParentRecord<function (type parameter) T in ParentConfig<T>T>,
currentParentData: ParentRecord<T>currentParentData: interface ParentRecord<T>The parent record, contains the el and the data in the `parents` weakmap.ParentRecord<function (type parameter) T in ParentConfig<T>T>,
state: BaseDragState<T>state: type BaseDragState<T> = {
activeState?: {
node: NodeRecord<T>;
parent: ParentRecord<T>;
};
affectedNodes: Array<NodeRecord<T>>;
currentTargetValue: T | undefined;
emit: (event: string, data: unknown) => void;
... 23 more ...;
frameIdY?: number;
}
BaseDragState<function (type parameter) T in ParentConfig<T>T>
) => boolean;
/**
* The data transfer effect to use for the drag operation.
*/
ParentConfig<T>.dragEffectAllowed: NativeDragEffectsThe data transfer effect to use for the drag operation.dragEffectAllowed: type NativeDragEffects = "link" | "none" | "copy" | "move"NativeDragEffects;
/**
* The data transfer effect to use for the drag operation.
*/
ParentConfig<T>.dragDropEffect: NativeDragEffectsThe data transfer effect to use for the drag operation.dragDropEffect: type NativeDragEffects = "link" | "none" | "copy" | "move"NativeDragEffects;
/**
* A function that returns the image to use for the drag operation. This is
* invoked for native operations. The clonedNode is what will be set as the drag
* image, but this can be updated.
*/
ParentConfig<T>.dragImage?: ((data: NodeDragEventData<T>, draggedNodes: Array<NodeRecord<T>>) => HTMLElement) | undefinedA function that returns the image to use for the drag operation. This is
invoked for native operations. The clonedNode is what will be set as the drag
image, but this can be updated.dragImage?: (
data: NodeDragEventData<T>data: interface NodeDragEventData<T>The data passed to the node event listener when the event is a drag event.NodeDragEventData<function (type parameter) T in ParentConfig<T>T>,
draggedNodes: NodeRecord<T>[]draggedNodes: interface Array<T>Array<interface NodeRecord<T>The node record, contains the el and the data in the `nodes` weakmap.NodeRecord<function (type parameter) T in ParentConfig<T>T>>
) => HTMLElement;
/**
* A flag to disable dragability of all nodes in the parent.
*/
ParentConfig<T>.disabled?: boolean | undefinedA flag to disable dragability of all nodes in the parent.disabled?: boolean;
/**
* A selector for the drag handle. Will search at any depth within the
* draggable element.
*/
ParentConfig<T>.dragHandle?: string | undefinedA selector for the drag handle. Will search at any depth within the
draggable element.dragHandle?: string;
/**
* External drag handle
*/
ParentConfig<T>.externalDragHandle?: {
el: HTMLElement;
callback: () => HTMLElement;
} | undefined
External drag handleexternalDragHandle?: {
el: HTMLElementel: HTMLElement;
callback: () => HTMLElementcallback: () => HTMLElement;
};
/**
* A function that returns whether a given node is draggable.
*/
ParentConfig<T>.draggable?: ((child: HTMLElement) => boolean) | undefinedA function that returns whether a given node is draggable.draggable?: (child: HTMLElementchild: HTMLElement) => boolean;
/**
* A function that returns whether a given value is draggable
*/
ParentConfig<T>.draggableValue?: ((values: T) => boolean) | undefinedA function that returns whether a given value is draggabledraggableValue?: (values: Tvalues: function (type parameter) T in ParentConfig<T>T) => boolean;
ParentConfig<T>.draggedNodes: (pointerDown: {
parent: ParentRecord<T>;
node: NodeRecord<T>;
}) => Array<NodeRecord<T>>
draggedNodes: (pointerDown: {
parent: ParentRecord<T>;
node: NodeRecord<T>;
}
pointerDown: {
parent: ParentRecord<T>parent: interface ParentRecord<T>The parent record, contains the el and the data in the `parents` weakmap.ParentRecord<function (type parameter) T in ParentConfig<T>T>;
node: NodeRecord<T>node: interface NodeRecord<T>The node record, contains the el and the data in the `nodes` weakmap.NodeRecord<function (type parameter) T in ParentConfig<T>T>;
}) => interface Array<T>Array<interface NodeRecord<T>The node record, contains the el and the data in the `nodes` weakmap.NodeRecord<function (type parameter) T in ParentConfig<T>T>>;
/**
* The class to add to a node when it is being dragged.
*/
ParentConfig<T>.draggingClass?: string | undefinedThe class to add to a node when it is being dragged.draggingClass?: string;
/**
* Accepts array of "dragged nodes" and applies dragstart classes to them.
*/
ParentConfig<T>.dragstartClasses: (node: NodeRecord<T>, nodes: Array<NodeRecord<T>>, config: ParentConfig<T>, isSynthDrag?: boolean) => voidAccepts array of "dragged nodes" and applies dragstart classes to them.dragstartClasses: (
node: NodeRecord<T>node: interface NodeRecord<T>The node record, contains the el and the data in the `nodes` weakmap.NodeRecord<function (type parameter) T in ParentConfig<T>T>,
nodes: NodeRecord<T>[]nodes: interface Array<T>Array<interface NodeRecord<T>The node record, contains the el and the data in the `nodes` weakmap.NodeRecord<function (type parameter) T in ParentConfig<T>T>>,
config: ParentConfig<T>config: interface ParentConfig<T>The configuration object for a given parent.ParentConfig<function (type parameter) T in ParentConfig<T>T>,
isSynthDrag: boolean | undefinedisSynthDrag?: boolean
) => void;
// When drag starts, this will be applied to the dragged node(s) (not their
// representations being dragged) on dragstart. This will remain on the nodes
// until the drag ends.
ParentConfig<T>.dragPlaceholderClass?: string | undefineddragPlaceholderClass?: string;
/**
* The configuration object for the drop and swap plugin.
*/
ParentConfig<T>.dropSwapConfig?: DropSwapConfig<T> | undefinedThe configuration object for the drop and swap plugin.dropSwapConfig?: interface DropSwapConfig<T>DropSwapConfig<function (type parameter) T in ParentConfig<T>T>;
/**
* The class to add to a node when the node is dragged over it.
*/
ParentConfig<T>.dropZoneClass?: string | undefinedThe class to add to a node when the node is dragged over it.dropZoneClass?: string;
/**
* The class to add to a parent when it is dragged over.
*/
ParentConfig<T>.dropZoneParentClass?: string | undefinedThe class to add to a parent when it is dragged over.dropZoneParentClass?: string;
/**
* A flag to indicate whether the parent itself is a dropZone.
*/
ParentConfig<T>.dropZone?: boolean | undefinedA flag to indicate whether the parent itself is a dropZone.dropZone?: boolean;
/**
* The group that the parent belongs to. This is used for allowing multiple
* parents to transfer nodes between each other.
*/
ParentConfig<T>.group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group?: string;
ParentConfig<T>.handleParentFocus: (data: ParentEventData<T>, state: BaseDragState<T>) => voidhandleParentFocus: (
data: ParentEventData<T>data: interface ParentEventData<T>The data passed to the parent event listener.ParentEventData<function (type parameter) T in ParentConfig<T>T>,
state: BaseDragState<T>state: type BaseDragState<T> = {
activeState?: {
node: NodeRecord<T>;
parent: ParentRecord<T>;
};
affectedNodes: Array<NodeRecord<T>>;
currentTargetValue: T | undefined;
emit: (event: string, data: unknown) => void;
... 23 more ...;
frameIdY?: number;
}
BaseDragState<function (type parameter) T in ParentConfig<T>T>
) => void;
ParentConfig<T>.handleNodeKeydown: (data: NodeEventData<T>, state: DragState<T>) => voidhandleNodeKeydown: (data: NodeEventData<T>data: interface NodeEventData<T>The data passed to the node event listener.NodeEventData<function (type parameter) T in ParentConfig<T>T>, state: DragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T>) => void;
/**
* Function that is called when dragend or touchend event occurs.
*/
ParentConfig<T>.handleDragend: (data: NodeDragEventData<T>, state: DragState<T>) => voidFunction that is called when dragend or touchend event occurs.handleDragend: (data: NodeDragEventData<T>data: interface NodeDragEventData<T>The data passed to the node event listener when the event is a drag event.NodeDragEventData<function (type parameter) T in ParentConfig<T>T>, state: DragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T>) => void;
/**
* Function that is called when dragstart event occurs.
*/
ParentConfig<T>.handleDragstart: (data: NodeDragEventData<T>, state: DragState<T>) => voidFunction that is called when dragstart event occurs.handleDragstart: (data: NodeDragEventData<T>data: interface NodeDragEventData<T>The data passed to the node event listener when the event is a drag event.NodeDragEventData<function (type parameter) T in ParentConfig<T>T>, state: DragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T>) => void;
ParentConfig<T>.handleEnd: (state: DragState<T> | SynthDragState<T>) => voidhandleEnd: (state: DragState<T> | SynthDragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T> | type SynthDragState<T> = SynthDragStateProps & DragStateProps<T> & BaseDragState<T>The state of the current drag. State is only created when a drag start
event is triggered.SynthDragState<function (type parameter) T in ParentConfig<T>T>) => void;
ParentConfig<T>.handleNodeDrop: (data: NodeDragEventData<T>, state: DragState<T>) => voidhandleNodeDrop: (data: NodeDragEventData<T>data: interface NodeDragEventData<T>The data passed to the node event listener when the event is a drag event.NodeDragEventData<function (type parameter) T in ParentConfig<T>T>, state: DragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T>) => void;
ParentConfig<T>.handleNodePointerup: (data: NodePointerEventData<T>, state: DragState<T>) => voidhandleNodePointerup: (
data: NodePointerEventData<T>data: interface NodePointerEventData<T>The data passed to the node event listener when the event is a pointer event (not a native drag event).NodePointerEventData<function (type parameter) T in ParentConfig<T>T>,
state: DragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T>
) => void;
ParentConfig<T>.handleParentScroll: (data: ParentEventData<T>, state: DragState<T> | BaseDragState<T> | SynthDragState<T>) => voidhandleParentScroll: (
data: ParentEventData<T>data: interface ParentEventData<T>The data passed to the parent event listener.ParentEventData<function (type parameter) T in ParentConfig<T>T>,
state: BaseDragState<T> | DragState<T> | SynthDragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T> | type BaseDragState<T> = {
activeState?: {
node: NodeRecord<T>;
parent: ParentRecord<T>;
};
affectedNodes: Array<NodeRecord<T>>;
currentTargetValue: T | undefined;
emit: (event: string, data: unknown) => void;
... 23 more ...;
frameIdY?: number;
}
BaseDragState<function (type parameter) T in ParentConfig<T>T> | type SynthDragState<T> = SynthDragStateProps & DragStateProps<T> & BaseDragState<T>The state of the current drag. State is only created when a drag start
event is triggered.SynthDragState<function (type parameter) T in ParentConfig<T>T>
) => void;
/**
* Function that is called when a dragenter event is triggered on the node.
*/
ParentConfig<T>.handleNodeDragenter: (data: NodeDragEventData<T>, state: DragState<T>) => voidFunction that is called when a dragenter event is triggered on the node.handleNodeDragenter: (
data: NodeDragEventData<T>data: interface NodeDragEventData<T>The data passed to the node event listener when the event is a drag event.NodeDragEventData<function (type parameter) T in ParentConfig<T>T>,
state: DragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T>
) => void;
ParentConfig<T>.handleNodeBlur: (data: NodeEventData<T>, state: DragState<T>) => voidhandleNodeBlur: (data: NodeEventData<T>data: interface NodeEventData<T>The data passed to the node event listener.NodeEventData<function (type parameter) T in ParentConfig<T>T>, state: DragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T>) => void;
ParentConfig<T>.handleNodeFocus: (data: NodeEventData<T>, state: DragState<T>) => voidhandleNodeFocus: (data: NodeEventData<T>data: interface NodeEventData<T>The data passed to the node event listener.NodeEventData<function (type parameter) T in ParentConfig<T>T>, state: DragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T>) => void;
/**
* Dragleave event on node
*/
ParentConfig<T>.handleNodeDragleave: (data: NodeDragEventData<T>, state: DragState<T>) => voidDragleave event on nodehandleNodeDragleave: (
data: NodeDragEventData<T>data: interface NodeDragEventData<T>The data passed to the node event listener when the event is a drag event.NodeDragEventData<function (type parameter) T in ParentConfig<T>T>,
state: DragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T>
) => void;
/**
* Function that is called when a dragover event is triggered on the parent.
*/
ParentConfig<T>.handleParentDragover: (data: ParentDragEventData<T>, state: DragState<T>) => voidFunction that is called when a dragover event is triggered on the parent.handleParentDragover: (
data: ParentDragEventData<T>data: interface ParentDragEventData<T>ParentDragEventData<function (type parameter) T in ParentConfig<T>T>,
state: DragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T>
) => void;
/**
* Drop event on parent
*/
ParentConfig<T>.handleParentDrop: (data: ParentDragEventData<T>, state: DragState<T>) => voidDrop event on parenthandleParentDrop: (data: ParentDragEventData<T>data: interface ParentDragEventData<T>ParentDragEventData<function (type parameter) T in ParentConfig<T>T>, state: DragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T>) => void;
/**
* Function that is called when a dragover event is triggered on a node.
*/
ParentConfig<T>.handleNodeDragover: (data: NodeDragEventData<T>, state: DragState<T>) => voidFunction that is called when a dragover event is triggered on a node.handleNodeDragover: (data: NodeDragEventData<T>data: interface NodeDragEventData<T>The data passed to the node event listener when the event is a drag event.NodeDragEventData<function (type parameter) T in ParentConfig<T>T>, state: DragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T>) => void;
ParentConfig<T>.handlePointercancel: (data: NodeDragEventData<T> | NodePointerEventData<T>, state: DragState<T> | SynthDragState<T> | BaseDragState<T>) => voidhandlePointercancel: (
data: NodeDragEventData<T> | NodePointerEventData<T>data: interface NodeDragEventData<T>The data passed to the node event listener when the event is a drag event.NodeDragEventData<function (type parameter) T in ParentConfig<T>T> | interface NodePointerEventData<T>The data passed to the node event listener when the event is a pointer event (not a native drag event).NodePointerEventData<function (type parameter) T in ParentConfig<T>T>,
state: BaseDragState<T> | DragState<T> | SynthDragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T> | type SynthDragState<T> = SynthDragStateProps & DragStateProps<T> & BaseDragState<T>The state of the current drag. State is only created when a drag start
event is triggered.SynthDragState<function (type parameter) T in ParentConfig<T>T> | type BaseDragState<T> = {
activeState?: {
node: NodeRecord<T>;
parent: ParentRecord<T>;
};
affectedNodes: Array<NodeRecord<T>>;
currentTargetValue: T | undefined;
emit: (event: string, data: unknown) => void;
... 23 more ...;
frameIdY?: number;
}
BaseDragState<function (type parameter) T in ParentConfig<T>T>
) => void;
/*
* Function that is called when a pointerdown is triggered on node.
*/
ParentConfig<T>.handleNodePointerdown: (data: NodePointerEventData<T>, state: DragState<T>) => voidhandleNodePointerdown: (
data: NodePointerEventData<T>data: interface NodePointerEventData<T>The data passed to the node event listener when the event is a pointer event (not a native drag event).NodePointerEventData<function (type parameter) T in ParentConfig<T>T>,
state: DragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T>
) => void;
/**
* Function that is called when a node that is being moved by touchmove event
* is over a given node (similar to dragover).
*/
ParentConfig<T>.handleNodePointerover: (data: PointeroverNodeEvent<T>, state: SynthDragState<T>) => voidFunction that is called when a node that is being moved by touchmove event
is over a given node (similar to dragover).handleNodePointerover: (
data: PointeroverNodeEvent<T>data: interface PointeroverNodeEvent<T>The payload of the custom event dispatched when a node is "touched" over a
node.PointeroverNodeEvent<function (type parameter) T in ParentConfig<T>T>,
state: SynthDragState<T>state: type SynthDragState<T> = SynthDragStateProps & DragStateProps<T> & BaseDragState<T>The state of the current drag. State is only created when a drag start
event is triggered.SynthDragState<function (type parameter) T in ParentConfig<T>T>
) => void;
/**
* Function that is called when a node that is being moved by touchmove event
* is over the parent (similar to dragover).
*/
ParentConfig<T>.handleParentPointerover: (e: PointeroverParentEvent<T>, state: SynthDragState<T>) => voidFunction that is called when a node that is being moved by touchmove event
is over the parent (similar to dragover).handleParentPointerover: (
e: PointeroverParentEvent<T>e: interface PointeroverParentEvent<T>The payload of the custom event dispatched when a node is "touched" over a
parent.PointeroverParentEvent<function (type parameter) T in ParentConfig<T>T>,
state: SynthDragState<T>state: type SynthDragState<T> = SynthDragStateProps & DragStateProps<T> & BaseDragState<T>The state of the current drag. State is only created when a drag start
event is triggered.SynthDragState<function (type parameter) T in ParentConfig<T>T>
) => void;
/**
* Config option for insert plugin.
*/
ParentConfig<T>.insertConfig?: InsertConfig<T> | undefinedConfig option for insert plugin.insertConfig?: interface InsertConfig<T>InsertConfig<function (type parameter) T in ParentConfig<T>T>;
/**
* A flag to indicate whether long touch is enabled.
*/
ParentConfig<T>.longPress?: boolean | undefinedA flag to indicate whether long touch is enabled.longPress?: boolean;
/**
* The class to add to a node when a long touch action is performed.
*/
ParentConfig<T>.longPressClass?: string | undefinedThe class to add to a node when a long touch action is performed.longPressClass?: string;
/**
* The time in milliseconds to wait before a long touch is performed.
*/
ParentConfig<T>.longPressDuration?: number | undefinedThe time in milliseconds to wait before a long touch is performed.longPressDuration?: number;
/**
* The name of the parent (used for accepts function for increased specificity).
*/
ParentConfig<T>.name?: string | undefinedThe name of the parent (used for accepts function for increased specificity).name?: string;
ParentConfig<T>.multiDrag?: boolean | undefinedmultiDrag?: boolean;
/**
* If set to false, the library will not use the native drag and drop API.
*/
ParentConfig<T>.nativeDrag?: boolean | undefinedIf set to false, the library will not use the native drag and drop API.nativeDrag?: boolean;
/**
* Function that is called when a sort operation is to be performed.
*/
ParentConfig<T>.performSort: ({ parent, draggedNodes, targetNodes, }: {
parent: ParentRecord<T>;
draggedNodes: Array<NodeRecord<T>>;
targetNodes: Array<NodeRecord<T>>;
}) => void
Function that is called when a sort operation is to be performed.performSort: ({
parent: ParentRecord<T>parent,
draggedNodes: NodeRecord<T>[]draggedNodes,
targetNodes: NodeRecord<T>[]targetNodes,
}: {
parent: ParentRecord<T>parent: interface ParentRecord<T>The parent record, contains the el and the data in the `parents` weakmap.ParentRecord<function (type parameter) T in ParentConfig<T>T>;
draggedNodes: NodeRecord<T>[]draggedNodes: interface Array<T>Array<interface NodeRecord<T>The node record, contains the el and the data in the `nodes` weakmap.NodeRecord<function (type parameter) T in ParentConfig<T>T>>;
targetNodes: NodeRecord<T>[]targetNodes: interface Array<T>Array<interface NodeRecord<T>The node record, contains the el and the data in the `nodes` weakmap.NodeRecord<function (type parameter) T in ParentConfig<T>T>>;
}) => void;
/**
* Function that is called when a transfer operation is to be performed.
*/
ParentConfig<T>.performTransfer: ({ currentParent, targetParent, initialParent, draggedNodes, initialIndex, state, targetNodes, }: {
currentParent: ParentRecord<T>;
targetParent: ParentRecord<T>;
initialParent: ParentRecord<T>;
draggedNodes: Array<NodeRecord<T>>;
initialIndex: number;
state: BaseDragState<T> | DragState<T> | SynthDragState<T>;
targetNodes: Array<NodeRecord<T>>;
}) => void
Function that is called when a transfer operation is to be performed.performTransfer: ({
currentParent: ParentRecord<T>currentParent,
targetParent: ParentRecord<T>targetParent,
initialParent: ParentRecord<T>initialParent,
draggedNodes: NodeRecord<T>[]draggedNodes,
initialIndex: numberinitialIndex,
state: BaseDragState<T> | DragState<T> | SynthDragState<T>state,
targetNodes: NodeRecord<T>[]targetNodes,
}: {
currentParent: ParentRecord<T>currentParent: interface ParentRecord<T>The parent record, contains the el and the data in the `parents` weakmap.ParentRecord<function (type parameter) T in ParentConfig<T>T>;
targetParent: ParentRecord<T>targetParent: interface ParentRecord<T>The parent record, contains the el and the data in the `parents` weakmap.ParentRecord<function (type parameter) T in ParentConfig<T>T>;
initialParent: ParentRecord<T>initialParent: interface ParentRecord<T>The parent record, contains the el and the data in the `parents` weakmap.ParentRecord<function (type parameter) T in ParentConfig<T>T>;
draggedNodes: NodeRecord<T>[]draggedNodes: interface Array<T>Array<interface NodeRecord<T>The node record, contains the el and the data in the `nodes` weakmap.NodeRecord<function (type parameter) T in ParentConfig<T>T>>;
initialIndex: numberinitialIndex: number;
state: BaseDragState<T> | DragState<T> | SynthDragState<T>state: type BaseDragState<T> = {
activeState?: {
node: NodeRecord<T>;
parent: ParentRecord<T>;
};
affectedNodes: Array<NodeRecord<T>>;
currentTargetValue: T | undefined;
emit: (event: string, data: unknown) => void;
... 23 more ...;
frameIdY?: number;
}
BaseDragState<function (type parameter) T in ParentConfig<T>T> | type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T> | type SynthDragState<T> = SynthDragStateProps & DragStateProps<T> & BaseDragState<T>The state of the current drag. State is only created when a drag start
event is triggered.SynthDragState<function (type parameter) T in ParentConfig<T>T>;
targetNodes: NodeRecord<T>[]targetNodes: interface Array<T>Array<interface NodeRecord<T>The node record, contains the el and the data in the `nodes` weakmap.NodeRecord<function (type parameter) T in ParentConfig<T>T>>;
}) => void;
/**
* An array of functions to use for a given parent.
*/
ParentConfig<T>.plugins?: DNDPlugin[] | undefinedAn array of functions to use for a given parent.plugins?: interface Array<T>Array<type DNDPlugin = (parent: HTMLElement) => DNDPluginData | undefinedDNDPlugin>;
/**
* Takes a given node and reapplies the drag classes.
*/
ParentConfig<T>.reapplyDragClasses: (node: Node, parentData: ParentData<T>) => voidTakes a given node and reapplies the drag classes.reapplyDragClasses: (node: Nodenode: Node, parentData: ParentData<T>parentData: interface ParentData<T>The data assigned to a given parent in the `parents` weakmap.ParentData<function (type parameter) T in ParentConfig<T>T>) => void;
/**
* Invoked when the remapping of a given parent's nodes is finished.
*/
ParentConfig<T>.remapFinished: (data: ParentData<T>) => voidInvoked when the remapping of a given parent's nodes is finished.remapFinished: (data: ParentData<T>data: interface ParentData<T>The data assigned to a given parent in the `parents` weakmap.ParentData<function (type parameter) T in ParentConfig<T>T>) => void;
/**
* The root element to use for the parent.
*/
ParentConfig<T>.root: Document | ShadowRootThe root element to use for the parent.root: Document | ShadowRoot;
/**
* The class to add to a node when it is selected (clicked or pressed).
*/
ParentConfig<T>.selectedClass?: string | undefinedThe class to add to a node when it is selected (clicked or pressed).selectedClass?: string;
/**
* Function that is called when a node is set up.
*/
ParentConfig<T>.setupNode: SetupNodeFunction that is called when a node is set up.setupNode: type SetupNode = <T>(data: SetupNodeData<T>) => voidSetupNode;
/**
* Called when the value of the parent is changed and the nodes are remapped.
*/
ParentConfig<T>.setupNodeRemap: SetupNodeCalled when the value of the parent is changed and the nodes are remapped.setupNodeRemap: type SetupNode = <T>(data: SetupNodeData<T>) => voidSetupNode;
/**
* Flag for whether or not to allow sorting within a given parent.
*/
ParentConfig<T>.sortable?: boolean | undefinedFlag for whether or not to allow sorting within a given parent.sortable?: boolean;
/**
* The class to add to a parent when it is dragged over.
*/
ParentConfig<T>.synthDropZoneParentClass?: string | undefinedThe class to add to a parent when it is dragged over.synthDropZoneParentClass?: string;
/**
* A function that returns the image to use for the drag operation. This is
* invoked for synth drag operations operations. The clonedNode is what will
* be set as the drag image, but this can be updated.
*/
ParentConfig<T>.synthDragImage?: ((node: NodeRecord<T>, parent: ParentRecord<T>, e: PointerEvent, draggedNodes: Array<NodeRecord<T>>) => {
dragImage: HTMLElement;
offsetX?: number;
offsetY?: number;
}) | undefined
A function that returns the image to use for the drag operation. This is
invoked for synth drag operations operations. The clonedNode is what will
be set as the drag image, but this can be updated.synthDragImage?: (
node: NodeRecord<T>node: interface NodeRecord<T>The node record, contains the el and the data in the `nodes` weakmap.NodeRecord<function (type parameter) T in ParentConfig<T>T>,
parent: ParentRecord<T>parent: interface ParentRecord<T>The parent record, contains the el and the data in the `parents` weakmap.ParentRecord<function (type parameter) T in ParentConfig<T>T>,
e: PointerEvente: PointerEvent,
draggedNodes: NodeRecord<T>[]draggedNodes: interface Array<T>Array<interface NodeRecord<T>The node record, contains the el and the data in the `nodes` weakmap.NodeRecord<function (type parameter) T in ParentConfig<T>T>>
) => {
dragImage: HTMLElementdragImage: HTMLElement;
offsetX?: number | undefinedoffsetX?: number;
offsetY?: number | undefinedoffsetY?: number;
};
/**
* Function that is called when a node is torn down.
*/
ParentConfig<T>.tearDownNode: TearDownNodeFunction that is called when a node is torn down.tearDownNode: type TearDownNode = <T>(data: TearDownNodeData<T>) => voidTearDownNode;
/**
* Called when the value of the parent is changed and the nodes are remapped.
*/
ParentConfig<T>.tearDownNodeRemap: TearDownNodeCalled when the value of the parent is changed and the nodes are remapped.tearDownNodeRemap: type TearDownNode = <T>(data: TearDownNodeData<T>) => voidTearDownNode;
/**
* Property to identify which group of tree descendants the current parent belongs to.
*/
/**
* The threshold for a drag to be considered a valid sort
* operation.
*/
ParentConfig<T>.threshold: {
horizontal: number;
vertical: number;
}
The threshold for a drag to be considered a valid sort
operation.threshold: {
horizontal: numberhorizontal: number;
vertical: numbervertical: number;
};
/**
* The class to add to a node when it is being synthetically dragged.
*/
ParentConfig<T>.synthDraggingClass?: string | undefinedThe class to add to a node when it is being synthetically dragged.synthDraggingClass?: string;
/**
* On synth drag start, this is applied to the dragged node(s) (not their
* representations being dragged).
*/
ParentConfig<T>.synthDragPlaceholderClass?: string | undefinedOn synth drag start, this is applied to the dragged node(s) (not their
representations being dragged).synthDragPlaceholderClass?: string;
/**
* When hovering over a node, this class is applied to the node.
*/
ParentConfig<T>.synthDropZoneClass?: string | undefinedWhen hovering over a node, this class is applied to the node.synthDropZoneClass?: string;
/**
* Callback function for when a sort operation is performed.
*/
ParentConfig<T>.onSort?: SortEvent | undefinedCallback function for when a sort operation is performed.onSort?: type SortEvent = <T>(data: SortEventData<T>) => voidSortEvent;
/**
* Callback function for when a transfer operation is performed.
*/
ParentConfig<T>.onTransfer?: TransferEvent | undefinedCallback function for when a transfer operation is performed.onTransfer?: type TransferEvent = <T>(data: TransferEventData<T>) => voidTransferEvent;
/**
* Fired when a drag is started, whether native drag or synthetic
*/
ParentConfig<T>.onDragstart?: DragstartEvent | undefinedFired when a drag is started, whether native drag or syntheticonDragstart?: type DragstartEvent = <T>(data: DragstartEventData<T>) => voidDragstartEvent;
/**
* Fired when a drag is ended, whether native drag or synthetic
*/
ParentConfig<T>.onDragend?: DragendEvent | undefinedFired when a drag is ended, whether native drag or syntheticonDragend?: type DragendEvent = <T>(data: DragendEventData<T>) => voidDragendEvent;
}
import type {
interface ParentRecord<T>The parent record, contains the el and the data in the `parents` weakmap.ParentRecord,
type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState,
interface NodeDragEventData<T>The data passed to the node event listener when the event is a drag event.NodeDragEventData,
interface NodePointerEventData<T>The data passed to the node event listener when the event is a pointer event (not a native drag event).NodePointerEventData,
interface NodeEventData<T>The data passed to the node event listener.NodeEventData,
type DNDPlugin = (parent: HTMLElement) => DNDPluginData | undefinedDNDPlugin,
interface PointeroverNodeEvent<T>The payload of the custom event dispatched when a node is "touched" over a
node.PointeroverNodeEvent,
type SetupNode = <T>(data: SetupNodeData<T>) => voidSetupNode,
interface ParentEventData<T>The data passed to the parent event listener.ParentEventData,
type TearDownNode = <T>(data: TearDownNodeData<T>) => voidTearDownNode,
interface PointeroverParentEvent<T>The payload of the custom event dispatched when a node is "touched" over a
parent.PointeroverParentEvent,
type SortEvent = <T>(data: SortEventData<T>) => voidSortEvent,
type TransferEvent = <T>(data: TransferEventData<T>) => voidTransferEvent,
type DragstartEvent = <T>(data: DragstartEventData<T>) => voidDragstartEvent,
type DragendEvent = <T>(data: DragendEventData<T>) => voidDragendEvent,
interface ParentDragEventData<T>ParentDragEventData,
type BaseDragState<T> = {
activeState?: {
node: NodeRecord<T>;
parent: ParentRecord<T>;
};
affectedNodes: Array<NodeRecord<T>>;
currentTargetValue: T | undefined;
emit: (event: string, data: unknown) => void;
... 23 more ...;
frameIdY?: number;
}
BaseDragState,
type NativeDragEffects = "link" | "none" | "copy" | "move"NativeDragEffects,
interface NodeRecord<T>The node record, contains the el and the data in the `nodes` weakmap.NodeRecord,
interface DropSwapConfig<T>DropSwapConfig,
interface InsertConfig<T>InsertConfig,
interface ParentData<T>The data assigned to a given parent in the `parents` weakmap.ParentData,
interface ParentKeydownEventData<T>ParentKeydownEventData,
type SynthDragState<T> = SynthDragStateProps & DragStateProps<T> & BaseDragState<T>The state of the current drag. State is only created when a drag start
event is triggered.SynthDragState,
} from "@formkit/drag-and-drop";
/**
* The configuration object for a given parent.
*/
export interface interface ParentConfig<T>The configuration object for a given parent.ParentConfig<function (type parameter) T in ParentConfig<T>T> {
/**
* A function that returns whether a given parent accepts a given node.
*/
ParentConfig<T>.accepts?: ((targetParentData: ParentRecord<T>, initialParentData: ParentRecord<T>, currentParentData: ParentRecord<T>, state: BaseDragState<T>) => boolean) | undefinedA function that returns whether a given parent accepts a given node.accepts?: (
targetParentData: ParentRecord<T>targetParentData: interface ParentRecord<T>The parent record, contains the el and the data in the `parents` weakmap.ParentRecord<function (type parameter) T in ParentConfig<T>T>,
initialParentData: ParentRecord<T>initialParentData: interface ParentRecord<T>The parent record, contains the el and the data in the `parents` weakmap.ParentRecord<function (type parameter) T in ParentConfig<T>T>,
currentParentData: ParentRecord<T>currentParentData: interface ParentRecord<T>The parent record, contains the el and the data in the `parents` weakmap.ParentRecord<function (type parameter) T in ParentConfig<T>T>,
state: BaseDragState<T>state: type BaseDragState<T> = {
activeState?: {
node: NodeRecord<T>;
parent: ParentRecord<T>;
};
affectedNodes: Array<NodeRecord<T>>;
currentTargetValue: T | undefined;
emit: (event: string, data: unknown) => void;
... 23 more ...;
frameIdY?: number;
}
BaseDragState<function (type parameter) T in ParentConfig<T>T>
) => boolean;
/**
* The data transfer effect to use for the drag operation.
*/
ParentConfig<T>.dragEffectAllowed: NativeDragEffectsThe data transfer effect to use for the drag operation.dragEffectAllowed: type NativeDragEffects = "link" | "none" | "copy" | "move"NativeDragEffects;
/**
* The data transfer effect to use for the drag operation.
*/
ParentConfig<T>.dragDropEffect: NativeDragEffectsThe data transfer effect to use for the drag operation.dragDropEffect: type NativeDragEffects = "link" | "none" | "copy" | "move"NativeDragEffects;
/**
* A function that returns the image to use for the drag operation. This is
* invoked for native operations. The clonedNode is what will be set as the drag
* image, but this can be updated.
*/
ParentConfig<T>.dragImage?: ((data: NodeDragEventData<T>, draggedNodes: Array<NodeRecord<T>>) => HTMLElement) | undefinedA function that returns the image to use for the drag operation. This is
invoked for native operations. The clonedNode is what will be set as the drag
image, but this can be updated.dragImage?: (
data: NodeDragEventData<T>data: interface NodeDragEventData<T>The data passed to the node event listener when the event is a drag event.NodeDragEventData<function (type parameter) T in ParentConfig<T>T>,
draggedNodes: NodeRecord<T>[]draggedNodes: interface Array<T>Array<interface NodeRecord<T>The node record, contains the el and the data in the `nodes` weakmap.NodeRecord<function (type parameter) T in ParentConfig<T>T>>
) => HTMLElement;
/**
* A flag to disable dragability of all nodes in the parent.
*/
ParentConfig<T>.disabled?: boolean | undefinedA flag to disable dragability of all nodes in the parent.disabled?: boolean;
/**
* A selector for the drag handle. Will search at any depth within the
* draggable element.
*/
ParentConfig<T>.dragHandle?: string | undefinedA selector for the drag handle. Will search at any depth within the
draggable element.dragHandle?: string;
/**
* External drag handle
*/
ParentConfig<T>.externalDragHandle?: {
el: HTMLElement;
callback: () => HTMLElement;
} | undefined
External drag handleexternalDragHandle?: {
el: HTMLElementel: HTMLElement;
callback: () => HTMLElementcallback: () => HTMLElement;
};
/**
* A function that returns whether a given node is draggable.
*/
ParentConfig<T>.draggable?: ((child: HTMLElement) => boolean) | undefinedA function that returns whether a given node is draggable.draggable?: (child: HTMLElementchild: HTMLElement) => boolean;
/**
* A function that returns whether a given value is draggable
*/
ParentConfig<T>.draggableValue?: ((values: T) => boolean) | undefinedA function that returns whether a given value is draggabledraggableValue?: (values: Tvalues: function (type parameter) T in ParentConfig<T>T) => boolean;
ParentConfig<T>.draggedNodes: (pointerDown: {
parent: ParentRecord<T>;
node: NodeRecord<T>;
}) => Array<NodeRecord<T>>
draggedNodes: (pointerDown: {
parent: ParentRecord<T>;
node: NodeRecord<T>;
}
pointerDown: {
parent: ParentRecord<T>parent: interface ParentRecord<T>The parent record, contains the el and the data in the `parents` weakmap.ParentRecord<function (type parameter) T in ParentConfig<T>T>;
node: NodeRecord<T>node: interface NodeRecord<T>The node record, contains the el and the data in the `nodes` weakmap.NodeRecord<function (type parameter) T in ParentConfig<T>T>;
}) => interface Array<T>Array<interface NodeRecord<T>The node record, contains the el and the data in the `nodes` weakmap.NodeRecord<function (type parameter) T in ParentConfig<T>T>>;
/**
* The class to add to a node when it is being dragged.
*/
ParentConfig<T>.draggingClass?: string | undefinedThe class to add to a node when it is being dragged.draggingClass?: string;
/**
* Accepts array of "dragged nodes" and applies dragstart classes to them.
*/
ParentConfig<T>.dragstartClasses: (node: NodeRecord<T>, nodes: Array<NodeRecord<T>>, config: ParentConfig<T>, isSynthDrag?: boolean) => voidAccepts array of "dragged nodes" and applies dragstart classes to them.dragstartClasses: (
node: NodeRecord<T>node: interface NodeRecord<T>The node record, contains the el and the data in the `nodes` weakmap.NodeRecord<function (type parameter) T in ParentConfig<T>T>,
nodes: NodeRecord<T>[]nodes: interface Array<T>Array<interface NodeRecord<T>The node record, contains the el and the data in the `nodes` weakmap.NodeRecord<function (type parameter) T in ParentConfig<T>T>>,
config: ParentConfig<T>config: interface ParentConfig<T>The configuration object for a given parent.ParentConfig<function (type parameter) T in ParentConfig<T>T>,
isSynthDrag: boolean | undefinedisSynthDrag?: boolean
) => void;
// When drag starts, this will be applied to the dragged node(s) (not their
// representations being dragged) on dragstart. This will remain on the nodes
// until the drag ends.
ParentConfig<T>.dragPlaceholderClass?: string | undefineddragPlaceholderClass?: string;
/**
* The configuration object for the drop and swap plugin.
*/
ParentConfig<T>.dropSwapConfig?: DropSwapConfig<T> | undefinedThe configuration object for the drop and swap plugin.dropSwapConfig?: interface DropSwapConfig<T>DropSwapConfig<function (type parameter) T in ParentConfig<T>T>;
/**
* The class to add to a node when the node is dragged over it.
*/
ParentConfig<T>.dropZoneClass?: string | undefinedThe class to add to a node when the node is dragged over it.dropZoneClass?: string;
/**
* The class to add to a parent when it is dragged over.
*/
ParentConfig<T>.dropZoneParentClass?: string | undefinedThe class to add to a parent when it is dragged over.dropZoneParentClass?: string;
/**
* A flag to indicate whether the parent itself is a dropZone.
*/
ParentConfig<T>.dropZone?: boolean | undefinedA flag to indicate whether the parent itself is a dropZone.dropZone?: boolean;
/**
* The group that the parent belongs to. This is used for allowing multiple
* parents to transfer nodes between each other.
*/
ParentConfig<T>.group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group?: string;
ParentConfig<T>.handleParentFocus: (data: ParentEventData<T>, state: BaseDragState<T>) => voidhandleParentFocus: (
data: ParentEventData<T>data: interface ParentEventData<T>The data passed to the parent event listener.ParentEventData<function (type parameter) T in ParentConfig<T>T>,
state: BaseDragState<T>state: type BaseDragState<T> = {
activeState?: {
node: NodeRecord<T>;
parent: ParentRecord<T>;
};
affectedNodes: Array<NodeRecord<T>>;
currentTargetValue: T | undefined;
emit: (event: string, data: unknown) => void;
... 23 more ...;
frameIdY?: number;
}
BaseDragState<function (type parameter) T in ParentConfig<T>T>
) => void;
ParentConfig<T>.handleNodeKeydown: (data: NodeEventData<T>, state: DragState<T>) => voidhandleNodeKeydown: (data: NodeEventData<T>data: interface NodeEventData<T>The data passed to the node event listener.NodeEventData<function (type parameter) T in ParentConfig<T>T>, state: DragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T>) => void;
/**
* Function that is called when dragend or touchend event occurs.
*/
ParentConfig<T>.handleDragend: (data: NodeDragEventData<T>, state: DragState<T>) => voidFunction that is called when dragend or touchend event occurs.handleDragend: (data: NodeDragEventData<T>data: interface NodeDragEventData<T>The data passed to the node event listener when the event is a drag event.NodeDragEventData<function (type parameter) T in ParentConfig<T>T>, state: DragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T>) => void;
/**
* Function that is called when dragstart event occurs.
*/
ParentConfig<T>.handleDragstart: (data: NodeDragEventData<T>, state: DragState<T>) => voidFunction that is called when dragstart event occurs.handleDragstart: (data: NodeDragEventData<T>data: interface NodeDragEventData<T>The data passed to the node event listener when the event is a drag event.NodeDragEventData<function (type parameter) T in ParentConfig<T>T>, state: DragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T>) => void;
ParentConfig<T>.handleEnd: (state: DragState<T> | SynthDragState<T>) => voidhandleEnd: (state: DragState<T> | SynthDragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T> | type SynthDragState<T> = SynthDragStateProps & DragStateProps<T> & BaseDragState<T>The state of the current drag. State is only created when a drag start
event is triggered.SynthDragState<function (type parameter) T in ParentConfig<T>T>) => void;
ParentConfig<T>.handleNodeDrop: (data: NodeDragEventData<T>, state: DragState<T>) => voidhandleNodeDrop: (data: NodeDragEventData<T>data: interface NodeDragEventData<T>The data passed to the node event listener when the event is a drag event.NodeDragEventData<function (type parameter) T in ParentConfig<T>T>, state: DragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T>) => void;
ParentConfig<T>.handleNodePointerup: (data: NodePointerEventData<T>, state: DragState<T>) => voidhandleNodePointerup: (
data: NodePointerEventData<T>data: interface NodePointerEventData<T>The data passed to the node event listener when the event is a pointer event (not a native drag event).NodePointerEventData<function (type parameter) T in ParentConfig<T>T>,
state: DragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T>
) => void;
ParentConfig<T>.handleParentScroll: (data: ParentEventData<T>, state: DragState<T> | BaseDragState<T> | SynthDragState<T>) => voidhandleParentScroll: (
data: ParentEventData<T>data: interface ParentEventData<T>The data passed to the parent event listener.ParentEventData<function (type parameter) T in ParentConfig<T>T>,
state: BaseDragState<T> | DragState<T> | SynthDragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T> | type BaseDragState<T> = {
activeState?: {
node: NodeRecord<T>;
parent: ParentRecord<T>;
};
affectedNodes: Array<NodeRecord<T>>;
currentTargetValue: T | undefined;
emit: (event: string, data: unknown) => void;
... 23 more ...;
frameIdY?: number;
}
BaseDragState<function (type parameter) T in ParentConfig<T>T> | type SynthDragState<T> = SynthDragStateProps & DragStateProps<T> & BaseDragState<T>The state of the current drag. State is only created when a drag start
event is triggered.SynthDragState<function (type parameter) T in ParentConfig<T>T>
) => void;
/**
* Function that is called when a dragenter event is triggered on the node.
*/
ParentConfig<T>.handleNodeDragenter: (data: NodeDragEventData<T>, state: DragState<T>) => voidFunction that is called when a dragenter event is triggered on the node.handleNodeDragenter: (
data: NodeDragEventData<T>data: interface NodeDragEventData<T>The data passed to the node event listener when the event is a drag event.NodeDragEventData<function (type parameter) T in ParentConfig<T>T>,
state: DragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T>
) => void;
ParentConfig<T>.handleNodeBlur: (data: NodeEventData<T>, state: DragState<T>) => voidhandleNodeBlur: (data: NodeEventData<T>data: interface NodeEventData<T>The data passed to the node event listener.NodeEventData<function (type parameter) T in ParentConfig<T>T>, state: DragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T>) => void;
ParentConfig<T>.handleNodeFocus: (data: NodeEventData<T>, state: DragState<T>) => voidhandleNodeFocus: (data: NodeEventData<T>data: interface NodeEventData<T>The data passed to the node event listener.NodeEventData<function (type parameter) T in ParentConfig<T>T>, state: DragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T>) => void;
/**
* Dragleave event on node
*/
ParentConfig<T>.handleNodeDragleave: (data: NodeDragEventData<T>, state: DragState<T>) => voidDragleave event on nodehandleNodeDragleave: (
data: NodeDragEventData<T>data: interface NodeDragEventData<T>The data passed to the node event listener when the event is a drag event.NodeDragEventData<function (type parameter) T in ParentConfig<T>T>,
state: DragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T>
) => void;
/**
* Function that is called when a dragover event is triggered on the parent.
*/
ParentConfig<T>.handleParentDragover: (data: ParentDragEventData<T>, state: DragState<T>) => voidFunction that is called when a dragover event is triggered on the parent.handleParentDragover: (
data: ParentDragEventData<T>data: interface ParentDragEventData<T>ParentDragEventData<function (type parameter) T in ParentConfig<T>T>,
state: DragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T>
) => void;
/**
* Drop event on parent
*/
ParentConfig<T>.handleParentDrop: (data: ParentDragEventData<T>, state: DragState<T>) => voidDrop event on parenthandleParentDrop: (data: ParentDragEventData<T>data: interface ParentDragEventData<T>ParentDragEventData<function (type parameter) T in ParentConfig<T>T>, state: DragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T>) => void;
/**
* Function that is called when a dragover event is triggered on a node.
*/
ParentConfig<T>.handleNodeDragover: (data: NodeDragEventData<T>, state: DragState<T>) => voidFunction that is called when a dragover event is triggered on a node.handleNodeDragover: (data: NodeDragEventData<T>data: interface NodeDragEventData<T>The data passed to the node event listener when the event is a drag event.NodeDragEventData<function (type parameter) T in ParentConfig<T>T>, state: DragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T>) => void;
ParentConfig<T>.handlePointercancel: (data: NodeDragEventData<T> | NodePointerEventData<T>, state: DragState<T> | SynthDragState<T> | BaseDragState<T>) => voidhandlePointercancel: (
data: NodeDragEventData<T> | NodePointerEventData<T>data: interface NodeDragEventData<T>The data passed to the node event listener when the event is a drag event.NodeDragEventData<function (type parameter) T in ParentConfig<T>T> | interface NodePointerEventData<T>The data passed to the node event listener when the event is a pointer event (not a native drag event).NodePointerEventData<function (type parameter) T in ParentConfig<T>T>,
state: BaseDragState<T> | DragState<T> | SynthDragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T> | type SynthDragState<T> = SynthDragStateProps & DragStateProps<T> & BaseDragState<T>The state of the current drag. State is only created when a drag start
event is triggered.SynthDragState<function (type parameter) T in ParentConfig<T>T> | type BaseDragState<T> = {
activeState?: {
node: NodeRecord<T>;
parent: ParentRecord<T>;
};
affectedNodes: Array<NodeRecord<T>>;
currentTargetValue: T | undefined;
emit: (event: string, data: unknown) => void;
... 23 more ...;
frameIdY?: number;
}
BaseDragState<function (type parameter) T in ParentConfig<T>T>
) => void;
/*
* Function that is called when a pointerdown is triggered on node.
*/
ParentConfig<T>.handleNodePointerdown: (data: NodePointerEventData<T>, state: DragState<T>) => voidhandleNodePointerdown: (
data: NodePointerEventData<T>data: interface NodePointerEventData<T>The data passed to the node event listener when the event is a pointer event (not a native drag event).NodePointerEventData<function (type parameter) T in ParentConfig<T>T>,
state: DragState<T>state: type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T>
) => void;
/**
* Function that is called when a node that is being moved by touchmove event
* is over a given node (similar to dragover).
*/
ParentConfig<T>.handleNodePointerover: (data: PointeroverNodeEvent<T>, state: SynthDragState<T>) => voidFunction that is called when a node that is being moved by touchmove event
is over a given node (similar to dragover).handleNodePointerover: (
data: PointeroverNodeEvent<T>data: interface PointeroverNodeEvent<T>The payload of the custom event dispatched when a node is "touched" over a
node.PointeroverNodeEvent<function (type parameter) T in ParentConfig<T>T>,
state: SynthDragState<T>state: type SynthDragState<T> = SynthDragStateProps & DragStateProps<T> & BaseDragState<T>The state of the current drag. State is only created when a drag start
event is triggered.SynthDragState<function (type parameter) T in ParentConfig<T>T>
) => void;
/**
* Function that is called when a node that is being moved by touchmove event
* is over the parent (similar to dragover).
*/
ParentConfig<T>.handleParentPointerover: (e: PointeroverParentEvent<T>, state: SynthDragState<T>) => voidFunction that is called when a node that is being moved by touchmove event
is over the parent (similar to dragover).handleParentPointerover: (
e: PointeroverParentEvent<T>e: interface PointeroverParentEvent<T>The payload of the custom event dispatched when a node is "touched" over a
parent.PointeroverParentEvent<function (type parameter) T in ParentConfig<T>T>,
state: SynthDragState<T>state: type SynthDragState<T> = SynthDragStateProps & DragStateProps<T> & BaseDragState<T>The state of the current drag. State is only created when a drag start
event is triggered.SynthDragState<function (type parameter) T in ParentConfig<T>T>
) => void;
/**
* Config option for insert plugin.
*/
ParentConfig<T>.insertConfig?: InsertConfig<T> | undefinedConfig option for insert plugin.insertConfig?: interface InsertConfig<T>InsertConfig<function (type parameter) T in ParentConfig<T>T>;
/**
* A flag to indicate whether long touch is enabled.
*/
ParentConfig<T>.longPress?: boolean | undefinedA flag to indicate whether long touch is enabled.longPress?: boolean;
/**
* The class to add to a node when a long touch action is performed.
*/
ParentConfig<T>.longPressClass?: string | undefinedThe class to add to a node when a long touch action is performed.longPressClass?: string;
/**
* The time in milliseconds to wait before a long touch is performed.
*/
ParentConfig<T>.longPressDuration?: number | undefinedThe time in milliseconds to wait before a long touch is performed.longPressDuration?: number;
/**
* The name of the parent (used for accepts function for increased specificity).
*/
ParentConfig<T>.name?: string | undefinedThe name of the parent (used for accepts function for increased specificity).name?: string;
ParentConfig<T>.multiDrag?: boolean | undefinedmultiDrag?: boolean;
/**
* If set to false, the library will not use the native drag and drop API.
*/
ParentConfig<T>.nativeDrag?: boolean | undefinedIf set to false, the library will not use the native drag and drop API.nativeDrag?: boolean;
/**
* Function that is called when a sort operation is to be performed.
*/
ParentConfig<T>.performSort: ({ parent, draggedNodes, targetNodes, }: {
parent: ParentRecord<T>;
draggedNodes: Array<NodeRecord<T>>;
targetNodes: Array<NodeRecord<T>>;
}) => void
Function that is called when a sort operation is to be performed.performSort: ({
parent: ParentRecord<T>parent,
draggedNodes: NodeRecord<T>[]draggedNodes,
targetNodes: NodeRecord<T>[]targetNodes,
}: {
parent: ParentRecord<T>parent: interface ParentRecord<T>The parent record, contains the el and the data in the `parents` weakmap.ParentRecord<function (type parameter) T in ParentConfig<T>T>;
draggedNodes: NodeRecord<T>[]draggedNodes: interface Array<T>Array<interface NodeRecord<T>The node record, contains the el and the data in the `nodes` weakmap.NodeRecord<function (type parameter) T in ParentConfig<T>T>>;
targetNodes: NodeRecord<T>[]targetNodes: interface Array<T>Array<interface NodeRecord<T>The node record, contains the el and the data in the `nodes` weakmap.NodeRecord<function (type parameter) T in ParentConfig<T>T>>;
}) => void;
/**
* Function that is called when a transfer operation is to be performed.
*/
ParentConfig<T>.performTransfer: ({ currentParent, targetParent, initialParent, draggedNodes, initialIndex, state, targetNodes, }: {
currentParent: ParentRecord<T>;
targetParent: ParentRecord<T>;
initialParent: ParentRecord<T>;
draggedNodes: Array<NodeRecord<T>>;
initialIndex: number;
state: BaseDragState<T> | DragState<T> | SynthDragState<T>;
targetNodes: Array<NodeRecord<T>>;
}) => void
Function that is called when a transfer operation is to be performed.performTransfer: ({
currentParent: ParentRecord<T>currentParent,
targetParent: ParentRecord<T>targetParent,
initialParent: ParentRecord<T>initialParent,
draggedNodes: NodeRecord<T>[]draggedNodes,
initialIndex: numberinitialIndex,
state: BaseDragState<T> | DragState<T> | SynthDragState<T>state,
targetNodes: NodeRecord<T>[]targetNodes,
}: {
currentParent: ParentRecord<T>currentParent: interface ParentRecord<T>The parent record, contains the el and the data in the `parents` weakmap.ParentRecord<function (type parameter) T in ParentConfig<T>T>;
targetParent: ParentRecord<T>targetParent: interface ParentRecord<T>The parent record, contains the el and the data in the `parents` weakmap.ParentRecord<function (type parameter) T in ParentConfig<T>T>;
initialParent: ParentRecord<T>initialParent: interface ParentRecord<T>The parent record, contains the el and the data in the `parents` weakmap.ParentRecord<function (type parameter) T in ParentConfig<T>T>;
draggedNodes: NodeRecord<T>[]draggedNodes: interface Array<T>Array<interface NodeRecord<T>The node record, contains the el and the data in the `nodes` weakmap.NodeRecord<function (type parameter) T in ParentConfig<T>T>>;
initialIndex: numberinitialIndex: number;
state: BaseDragState<T> | DragState<T> | SynthDragState<T>state: type BaseDragState<T> = {
activeState?: {
node: NodeRecord<T>;
parent: ParentRecord<T>;
};
affectedNodes: Array<NodeRecord<T>>;
currentTargetValue: T | undefined;
emit: (event: string, data: unknown) => void;
... 23 more ...;
frameIdY?: number;
}
BaseDragState<function (type parameter) T in ParentConfig<T>T> | type DragState<T> = DragStateProps<T> & BaseDragState<T>DragState<function (type parameter) T in ParentConfig<T>T> | type SynthDragState<T> = SynthDragStateProps & DragStateProps<T> & BaseDragState<T>The state of the current drag. State is only created when a drag start
event is triggered.SynthDragState<function (type parameter) T in ParentConfig<T>T>;
targetNodes: NodeRecord<T>[]targetNodes: interface Array<T>Array<interface NodeRecord<T>The node record, contains the el and the data in the `nodes` weakmap.NodeRecord<function (type parameter) T in ParentConfig<T>T>>;
}) => void;
/**
* An array of functions to use for a given parent.
*/
ParentConfig<T>.plugins?: DNDPlugin[] | undefinedAn array of functions to use for a given parent.plugins?: interface Array<T>Array<type DNDPlugin = (parent: HTMLElement) => DNDPluginData | undefinedDNDPlugin>;
/**
* Takes a given node and reapplies the drag classes.
*/
ParentConfig<T>.reapplyDragClasses: (node: Node, parentData: ParentData<T>) => voidTakes a given node and reapplies the drag classes.reapplyDragClasses: (node: Nodenode: Node, parentData: ParentData<T>parentData: interface ParentData<T>The data assigned to a given parent in the `parents` weakmap.ParentData<function (type parameter) T in ParentConfig<T>T>) => void;
/**
* Invoked when the remapping of a given parent's nodes is finished.
*/
ParentConfig<T>.remapFinished: (data: ParentData<T>) => voidInvoked when the remapping of a given parent's nodes is finished.remapFinished: (data: ParentData<T>data: interface ParentData<T>The data assigned to a given parent in the `parents` weakmap.ParentData<function (type parameter) T in ParentConfig<T>T>) => void;
/**
* The root element to use for the parent.
*/
ParentConfig<T>.root: Document | ShadowRootThe root element to use for the parent.root: Document | ShadowRoot;
/**
* The class to add to a node when it is selected (clicked or pressed).
*/
ParentConfig<T>.selectedClass?: string | undefinedThe class to add to a node when it is selected (clicked or pressed).selectedClass?: string;
/**
* Function that is called when a node is set up.
*/
ParentConfig<T>.setupNode: SetupNodeFunction that is called when a node is set up.setupNode: type SetupNode = <T>(data: SetupNodeData<T>) => voidSetupNode;
/**
* Called when the value of the parent is changed and the nodes are remapped.
*/
ParentConfig<T>.setupNodeRemap: SetupNodeCalled when the value of the parent is changed and the nodes are remapped.setupNodeRemap: type SetupNode = <T>(data: SetupNodeData<T>) => voidSetupNode;
/**
* Flag for whether or not to allow sorting within a given parent.
*/
ParentConfig<T>.sortable?: boolean | undefinedFlag for whether or not to allow sorting within a given parent.sortable?: boolean;
/**
* The class to add to a parent when it is dragged over.
*/
ParentConfig<T>.synthDropZoneParentClass?: string | undefinedThe class to add to a parent when it is dragged over.synthDropZoneParentClass?: string;
/**
* A function that returns the image to use for the drag operation. This is
* invoked for synth drag operations operations. The clonedNode is what will
* be set as the drag image, but this can be updated.
*/
ParentConfig<T>.synthDragImage?: ((node: NodeRecord<T>, parent: ParentRecord<T>, e: PointerEvent, draggedNodes: Array<NodeRecord<T>>) => {
dragImage: HTMLElement;
offsetX?: number;
offsetY?: number;
}) | undefined
A function that returns the image to use for the drag operation. This is
invoked for synth drag operations operations. The clonedNode is what will
be set as the drag image, but this can be updated.synthDragImage?: (
node: NodeRecord<T>node: interface NodeRecord<T>The node record, contains the el and the data in the `nodes` weakmap.NodeRecord<function (type parameter) T in ParentConfig<T>T>,
parent: ParentRecord<T>parent: interface ParentRecord<T>The parent record, contains the el and the data in the `parents` weakmap.ParentRecord<function (type parameter) T in ParentConfig<T>T>,
e: PointerEvente: PointerEvent,
draggedNodes: NodeRecord<T>[]draggedNodes: interface Array<T>Array<interface NodeRecord<T>The node record, contains the el and the data in the `nodes` weakmap.NodeRecord<function (type parameter) T in ParentConfig<T>T>>
) => {
dragImage: HTMLElementdragImage: HTMLElement;
offsetX?: number | undefinedoffsetX?: number;
offsetY?: number | undefinedoffsetY?: number;
};
/**
* Function that is called when a node is torn down.
*/
ParentConfig<T>.tearDownNode: TearDownNodeFunction that is called when a node is torn down.tearDownNode: type TearDownNode = <T>(data: TearDownNodeData<T>) => voidTearDownNode;
/**
* Called when the value of the parent is changed and the nodes are remapped.
*/
ParentConfig<T>.tearDownNodeRemap: TearDownNodeCalled when the value of the parent is changed and the nodes are remapped.tearDownNodeRemap: type TearDownNode = <T>(data: TearDownNodeData<T>) => voidTearDownNode;
/**
* Property to identify which group of tree descendants the current parent belongs to.
*/
/**
* The threshold for a drag to be considered a valid sort
* operation.
*/
ParentConfig<T>.threshold: {
horizontal: number;
vertical: number;
}
The threshold for a drag to be considered a valid sort
operation.threshold: {
horizontal: numberhorizontal: number;
vertical: numbervertical: number;
};
/**
* The class to add to a node when it is being synthetically dragged.
*/
ParentConfig<T>.synthDraggingClass?: string | undefinedThe class to add to a node when it is being synthetically dragged.synthDraggingClass?: string;
/**
* On synth drag start, this is applied to the dragged node(s) (not their
* representations being dragged).
*/
ParentConfig<T>.synthDragPlaceholderClass?: string | undefinedOn synth drag start, this is applied to the dragged node(s) (not their
representations being dragged).synthDragPlaceholderClass?: string;
/**
* When hovering over a node, this class is applied to the node.
*/
ParentConfig<T>.synthDropZoneClass?: string | undefinedWhen hovering over a node, this class is applied to the node.synthDropZoneClass?: string;
/**
* Callback function for when a sort operation is performed.
*/
ParentConfig<T>.onSort?: SortEvent | undefinedCallback function for when a sort operation is performed.onSort?: type SortEvent = <T>(data: SortEventData<T>) => voidSortEvent;
/**
* Callback function for when a transfer operation is performed.
*/
ParentConfig<T>.onTransfer?: TransferEvent | undefinedCallback function for when a transfer operation is performed.onTransfer?: type TransferEvent = <T>(data: TransferEventData<T>) => voidTransferEvent;
/**
* Fired when a drag is started, whether native drag or synthetic
*/
ParentConfig<T>.onDragstart?: DragstartEvent | undefinedFired when a drag is started, whether native drag or syntheticonDragstart?: type DragstartEvent = <T>(data: DragstartEventData<T>) => voidDragstartEvent;
/**
* Fired when a drag is ended, whether native drag or synthetic
*/
ParentConfig<T>.onDragend?: DragendEvent | undefinedFired when a drag is ended, whether native drag or syntheticonDragend?: type DragendEvent = <T>(data: DragendEventData<T>) => voidDragendEvent;
}
Accessibility
Below is an example of how to implement drag and drop lists with accessibility in mind.
- React
- Vue
- Solid
- Native
import React, { function useState<S>(initialState: S | (() => S)): [S, React.Dispatch<React.SetStateAction<S>>] (+1 overload)Returns a stateful value, and a function to update it.useState, function useRef<T>(initialValue: T): React.MutableRefObject<T> (+2 overloads)`useRef` returns a mutable ref object whose `.current` property is initialized to the passed argument
(`initialValue`). The returned object will persist for the full lifetime of the component.
Note that `useRef()` is useful for more than the `ref` attribute. It’s handy for keeping any mutable
value around similar to how you’d use instance fields in classes.useRef, function useCallback<T extends Function>(callback: T, deps: React.DependencyList): T`useCallback` will return a memoized version of the callback that only changes if one of the `inputs`
has changed.useCallback, function useMemo<T>(factory: () => T, deps: React.DependencyList): T`useMemo` will only recompute the memoized value when one of the `deps` has changed.useMemo } from "react";
import { function useDragAndDrop<E extends HTMLElement, T = unknown>(list: T[], options?: Partial<ParentConfig<T>>): [React.RefObject<E>, T[], React.Dispatch<React.SetStateAction<T[]>>, (config: Partial<ParentConfig<...>>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop } from "@formkit/drag-and-drop/react";
// Initial data for the lists
const const initialItems1: string[]initialItems1 = ["Apples", "Bananas", "Oranges", "Grapes"];
const const initialItems2: string[]initialItems2 = ["Milk", "Cheese", "Yogurt"];
interface SelectedItem {
SelectedItem.listIndex: 0 | 1listIndex: 0 | 1;
SelectedItem.itemIndex: numberitemIndex: number;
SelectedItem.value: stringvalue: string;
}
export default function function AccessibilityDemoReact(): React.JSX.ElementAccessibilityDemoReact() {
// Refs for ARIA Live region
const [const liveMessage: stringliveMessage, const setLiveMessage: React.Dispatch<React.SetStateAction<string>>setLiveMessage] = useState<string>(initialState: string | (() => string)): [string, React.Dispatch<React.SetStateAction<string>>] (+1 overload)Returns a stateful value, and a function to update it.useState("");
const const liveRegionRef: React.RefObject<HTMLDivElement>liveRegionRef = useRef<HTMLDivElement>(initialValue: HTMLDivElement | null): React.RefObject<HTMLDivElement> (+2 overloads)`useRef` returns a mutable ref object whose `.current` property is initialized to the passed argument
(`initialValue`). The returned object will persist for the full lifetime of the component.
Note that `useRef()` is useful for more than the `ref` attribute. It’s handy for keeping any mutable
value around similar to how you’d use instance fields in classes.
Usage note: if you need the result of useRef to be directly mutable, include `| null` in the type
of the generic argument.useRef<HTMLDivElement>(null);
// Function to update the live region
const const announce: (message: string) => voidannounce = useCallback<(message: string) => void>(callback: (message: string) => void, deps: React.DependencyList): (message: string) => void`useCallback` will return a memoized version of the callback that only changes if one of the `inputs`
has changed.useCallback((message: stringmessage: string) => {
const setLiveMessage: (value: React.SetStateAction<string>) => voidsetLiveMessage(message: stringmessage);
// Optional: Clear the message after a delay
// const timer = setTimeout(() => setLiveMessage(''), 5000);
// return () => clearTimeout(timer);
}, []);
// Setup drag and drop for List 1
const [const list1Ref: React.RefObject<HTMLUListElement>list1Ref, const items1: string[]items1, const setItems1: React.Dispatch<React.SetStateAction<string[]>>setItems1] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<
HTMLUListElement,
string
>(const initialItems1: string[]initialItems1, {
group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group: "accessibleList",
// Let types be inferred
onDragstart?: DragstartEvent | undefinedFired when a drag is started, whether native drag or syntheticonDragstart: (state: DragstartEventData<T>state) =>
const announce: (message: string) => voidannounce(`Drag started for ${state: DragstartEventData<T>state.DragstartEventData<T>.draggedNode: NodeRecord<T>draggedNode.NodeRecord<T>.data: NodeData<T>data.NodeData<T>.value: TThe value of the node.value}.`),
onSort?: SortEvent | undefinedCallback function for when a sort operation is performed.onSort: (event: SortEventData<T>event) =>
const announce: (message: string) => voidannounce(
`Sorted ${event: SortEventData<T>event.SortEventData<T>.draggedNodes: NodeRecord<T>[]draggedNodes[0].NodeRecord<T>.data: NodeData<T>data.NodeData<T>.value: TThe value of the node.value} in List 1 to position ${
event: SortEventData<T>event.SortEventData<T>.position: numberposition + 1
}.`
),
onTransfer?: TransferEvent | undefinedCallback function for when a transfer operation is performed.onTransfer: (event: TransferEventData<T>event) => {
const announce: (message: string) => voidannounce(
`Transferred ${event: TransferEventData<T>event.TransferEventData<T>.draggedNodes: NodeRecord<T>[]draggedNodes[0].NodeRecord<T>.data: NodeData<T>data.NodeData<T>.value: TThe value of the node.value} from List ${
event: TransferEventData<T>event.TransferEventData<T>.sourceParent: ParentRecord<T>sourceParent.ParentRecord<T>.el: HTMLElementel === const list1Ref: React.RefObject<HTMLUListElement>list1Ref.React.RefObject<HTMLUListElement>.current: HTMLUListElement | nullThe current value of the ref.current ? 1 : 2
} to List 1 at position ${event: TransferEventData<T>event.TransferEventData<T>.targetIndex: numbertargetIndex + 1}.`
);
},
onDragend?: DragendEvent | undefinedFired when a drag is ended, whether native drag or syntheticonDragend: (state: DragendEventData<T>state) =>
const announce: (message: string) => voidannounce(`Drag ended for ${state: DragendEventData<T>state.DragendEventData<T>.draggedNode: NodeRecord<T>draggedNode.NodeRecord<T>.data: NodeData<T>data.NodeData<T>.value: TThe value of the node.value}.`),
});
// Setup drag and drop for List 2
const [const list2Ref: React.RefObject<HTMLUListElement>list2Ref, const items2: string[]items2, const setItems2: React.Dispatch<React.SetStateAction<string[]>>setItems2] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<
HTMLUListElement,
string
>(const initialItems2: string[]initialItems2, {
group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group: "accessibleList",
onDragstart?: DragstartEvent | undefinedFired when a drag is started, whether native drag or syntheticonDragstart: (state: DragstartEventData<T>state) =>
const announce: (message: string) => voidannounce(`Drag started for ${state: DragstartEventData<T>state.DragstartEventData<T>.draggedNode: NodeRecord<T>draggedNode.NodeRecord<T>.data: NodeData<T>data.NodeData<T>.value: TThe value of the node.value}.`),
onSort?: SortEvent | undefinedCallback function for when a sort operation is performed.onSort: (event: SortEventData<T>event) =>
const announce: (message: string) => voidannounce(
`Sorted ${event: SortEventData<T>event.SortEventData<T>.draggedNodes: NodeRecord<T>[]draggedNodes[0].NodeRecord<T>.data: NodeData<T>data.NodeData<T>.value: TThe value of the node.value} in List 2 to position ${
event: SortEventData<T>event.SortEventData<T>.position: numberposition + 1
}.`
),
onTransfer?: TransferEvent | undefinedCallback function for when a transfer operation is performed.onTransfer: (event: TransferEventData<T>event) => {
// Compare against element refs
const announce: (message: string) => voidannounce(
`Transferred ${event: TransferEventData<T>event.TransferEventData<T>.draggedNodes: NodeRecord<T>[]draggedNodes[0].NodeRecord<T>.data: NodeData<T>data.NodeData<T>.value: TThe value of the node.value} from List ${
event: TransferEventData<T>event.TransferEventData<T>.sourceParent: ParentRecord<T>sourceParent.ParentRecord<T>.el: HTMLElementel === const list1Ref: React.RefObject<HTMLUListElement>list1Ref.React.RefObject<HTMLUListElement>.current: HTMLUListElement | nullThe current value of the ref.current ? 1 : 2
} to List 2 at position ${event: TransferEventData<T>event.TransferEventData<T>.targetIndex: numbertargetIndex + 1}.`
);
},
onDragend?: DragendEvent | undefinedFired when a drag is ended, whether native drag or syntheticonDragend: (state: DragendEventData<T>state) =>
const announce: (message: string) => voidannounce(`Drag ended for ${state: DragendEventData<T>state.DragendEventData<T>.draggedNode: NodeRecord<T>draggedNode.NodeRecord<T>.data: NodeData<T>data.NodeData<T>.value: TThe value of the node.value}.`),
});
// State for keyboard navigation
const [const focusedListIndex: 0 | 1 | nullfocusedListIndex, const setFocusedListIndex: React.Dispatch<React.SetStateAction<0 | 1 | null>>setFocusedListIndex] = useState<0 | 1 | null>(initialState: 0 | 1 | (() => 0 | 1 | null) | null): [0 | 1 | null, React.Dispatch<React.SetStateAction<0 | 1 | null>>] (+1 overload)Returns a stateful value, and a function to update it.useState<0 | 1 | null>(null);
const [const focusedItemIndex: number | nullfocusedItemIndex, const setFocusedItemIndex: React.Dispatch<React.SetStateAction<number | null>>setFocusedItemIndex] = useState<number | null>(initialState: number | (() => number | null) | null): [number | null, React.Dispatch<React.SetStateAction<number | null>>] (+1 overload)Returns a stateful value, and a function to update it.useState<number | null>(null);
const [const selectedItem: SelectedItem | nullselectedItem, const setSelectedItem: React.Dispatch<React.SetStateAction<SelectedItem | null>>setSelectedItem] = useState<SelectedItem | null>(initialState: SelectedItem | (() => SelectedItem | null) | null): [SelectedItem | null, React.Dispatch<...>] (+1 overload)Returns a stateful value, and a function to update it.useState<SelectedItem | null>(null);
// Calculate focusedItemId directly
const const focusedItemId: string | undefinedfocusedItemId = useMemo<string | undefined>(factory: () => string | undefined, deps: React.DependencyList): string | undefined`useMemo` will only recompute the memoized value when one of the `deps` has changed.useMemo(() => {
if (
const focusedListIndex: 0 | 1 | nullfocusedListIndex === 0 &&
const focusedItemIndex: number | nullfocusedItemIndex !== null &&
const focusedItemIndex: numberfocusedItemIndex < const items1: string[]items1.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length
) {
return `list1-item-${const items1: string[]items1[const focusedItemIndex: numberfocusedItemIndex]}`;
}
if (
const focusedListIndex: 0 | 1 | nullfocusedListIndex === 1 &&
const focusedItemIndex: number | nullfocusedItemIndex !== null &&
const focusedItemIndex: numberfocusedItemIndex < const items2: string[]items2.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length
) {
return `list2-item-${const items2: string[]items2[const focusedItemIndex: numberfocusedItemIndex]}`;
}
return var undefinedundefined;
}, [const focusedListIndex: 0 | 1 | nullfocusedListIndex, const focusedItemIndex: number | nullfocusedItemIndex, const items1: string[]items1, const items2: string[]items2]);
const const handleListFocus: (listIndex: 0 | 1) => voidhandleListFocus = useCallback<(listIndex: 0 | 1) => void>(callback: (listIndex: 0 | 1) => void, deps: React.DependencyList): (listIndex: 0 | 1) => void`useCallback` will return a memoized version of the callback that only changes if one of the `inputs`
has changed.useCallback(
(listIndex: 0 | 1listIndex: 0 | 1) => {
const setFocusedListIndex: (value: React.SetStateAction<0 | 1 | null>) => voidsetFocusedListIndex(listIndex: 0 | 1listIndex);
const const currentItems: string[]currentItems = listIndex: 0 | 1listIndex === 0 ? const items1: string[]items1 : const items2: string[]items2;
if (const focusedItemIndex: number | nullfocusedItemIndex === null && const currentItems: string[]currentItems.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length > 0) {
const setFocusedItemIndex: (value: React.SetStateAction<number | null>) => voidsetFocusedItemIndex(0);
}
const announce: (message: string) => voidannounce(
`List ${
listIndex: 0 | 1listIndex + 1
} focused. Use up and down arrows to navigate items.`
);
},
[const items1: string[]items1, const items2: string[]items2, const announce: (message: string) => voidannounce, const focusedItemIndex: number | nullfocusedItemIndex]
);
const const handleListBlur: () => voidhandleListBlur = useCallback<() => void>(callback: () => void, deps: React.DependencyList): () => void`useCallback` will return a memoized version of the callback that only changes if one of the `inputs`
has changed.useCallback(() => {
// Minimal blur handler - prevents immediate focus loss when clicking between elements
// More complex logic might be needed for specific focus management scenarios
}, []);
const const handleListKeydown: (event: React.KeyboardEvent<HTMLUListElement>, listIndex: 0 | 1) => voidhandleListKeydown = useCallback<(event: React.KeyboardEvent<HTMLUListElement>, listIndex: 0 | 1) => void>(callback: (event: React.KeyboardEvent<HTMLUListElement>, listIndex: 0 | 1) => void, deps: React.DependencyList): (event: React.KeyboardEvent<HTMLUListElement>, listIndex: 0 | 1) => void`useCallback` will return a memoized version of the callback that only changes if one of the `inputs`
has changed.useCallback(
(event: React.KeyboardEvent<HTMLUListElement>event: React.interface React.KeyboardEvent<T = Element>KeyboardEvent<HTMLUListElement>, listIndex: 0 | 1listIndex: 0 | 1) => {
if (const focusedListIndex: 0 | 1 | nullfocusedListIndex !== listIndex: 0 | 1listIndex) return;
const const currentItems: string[]currentItems = listIndex: 0 | 1listIndex === 0 ? const items1: string[]items1 : const items2: string[]items2;
const const setItems: React.Dispatch<React.SetStateAction<string[]>>setItems = listIndex: 0 | 1listIndex === 0 ? const setItems1: React.Dispatch<React.SetStateAction<string[]>>setItems1 : const setItems2: React.Dispatch<React.SetStateAction<string[]>>setItems2;
const const currentItemIndex: number | nullcurrentItemIndex = const focusedItemIndex: number | nullfocusedItemIndex;
switch (event: React.KeyboardEvent<HTMLUListElement>event.React.KeyboardEvent<HTMLUListElement>.key: stringSee the [DOM Level 3 Events spec](https://www.w3.org/TR/uievents-key/#named-key-attribute-values). for possible valueskey) {
case "ArrowDown":
event: React.KeyboardEvent<HTMLUListElement>event.React.BaseSyntheticEvent<KeyboardEvent, EventTarget & HTMLUListElement, EventTarget>.preventDefault(): voidpreventDefault();
if (const currentItems: string[]currentItems.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length > 0) {
const const nextIndex: numbernextIndex =
const currentItemIndex: number | nullcurrentItemIndex === null
? 0
: (const currentItemIndex: numbercurrentItemIndex + 1) % const currentItems: string[]currentItems.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length;
const setFocusedItemIndex: (value: React.SetStateAction<number | null>) => voidsetFocusedItemIndex(const nextIndex: numbernextIndex);
const announce: (message: string) => voidannounce(const currentItems: string[]currentItems[const nextIndex: numbernextIndex]);
}
break;
case "ArrowUp":
event: React.KeyboardEvent<HTMLUListElement>event.React.BaseSyntheticEvent<KeyboardEvent, EventTarget & HTMLUListElement, EventTarget>.preventDefault(): voidpreventDefault();
if (const currentItems: string[]currentItems.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length > 0) {
const const prevIndex: numberprevIndex =
const currentItemIndex: number | nullcurrentItemIndex === null
? const currentItems: string[]currentItems.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length - 1
: (const currentItemIndex: numbercurrentItemIndex - 1 + const currentItems: string[]currentItems.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length) %
const currentItems: string[]currentItems.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length;
const setFocusedItemIndex: (value: React.SetStateAction<number | null>) => voidsetFocusedItemIndex(const prevIndex: numberprevIndex);
const announce: (message: string) => voidannounce(const currentItems: string[]currentItems[const prevIndex: numberprevIndex]);
}
break;
case " ": // Spacebar
event: React.KeyboardEvent<HTMLUListElement>event.React.BaseSyntheticEvent<KeyboardEvent, EventTarget & HTMLUListElement, EventTarget>.preventDefault(): voidpreventDefault();
if (
const currentItemIndex: number | nullcurrentItemIndex !== null &&
const currentItemIndex: numbercurrentItemIndex >= 0 &&
const currentItemIndex: numbercurrentItemIndex < const currentItems: string[]currentItems.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length
) {
const const currentItemValue: stringcurrentItemValue = const currentItems: string[]currentItems[const currentItemIndex: numbercurrentItemIndex];
if (
const selectedItem: SelectedItem | nullselectedItem?.SelectedItem.listIndex: 0 | 1 | undefinedlistIndex === listIndex: 0 | 1listIndex &&
const selectedItem: SelectedItemselectedItem?.SelectedItem.itemIndex: numberitemIndex === const currentItemIndex: numbercurrentItemIndex
) {
const setSelectedItem: (value: React.SetStateAction<SelectedItem | null>) => voidsetSelectedItem(null);
const announce: (message: string) => voidannounce(`${const currentItemValue: stringcurrentItemValue} deselected.`);
} else {
const setSelectedItem: (value: React.SetStateAction<SelectedItem | null>) => voidsetSelectedItem({
SelectedItem.listIndex: 0 | 1listIndex,
SelectedItem.itemIndex: numberitemIndex: const currentItemIndex: numbercurrentItemIndex,
SelectedItem.value: stringvalue: const currentItemValue: stringcurrentItemValue,
});
const announce: (message: string) => voidannounce(
`${const currentItemValue: stringcurrentItemValue} selected. Use arrow keys to choose drop position, then press Enter.`
);
}
}
break;
case "Enter":
event: React.KeyboardEvent<HTMLUListElement>event.React.BaseSyntheticEvent<KeyboardEvent, EventTarget & HTMLUListElement, EventTarget>.preventDefault(): voidpreventDefault();
if (
const selectedItem: SelectedItem | nullselectedItem &&
const currentItemIndex: number | nullcurrentItemIndex !== null &&
const currentItemIndex: numbercurrentItemIndex >= 0 &&
const currentItemIndex: numbercurrentItemIndex < const currentItems: string[]currentItems.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length
) {
const {
SelectedItem.listIndex: 0 | 1listIndex: const sourceListIndex: 0 | 1sourceListIndex,
SelectedItem.itemIndex: numberitemIndex: const sourceItemIndex: numbersourceItemIndex,
SelectedItem.value: stringvalue: const itemValue: stringitemValue,
} = const selectedItem: SelectedItemselectedItem;
const const targetListIndex: 0 | 1targetListIndex = listIndex: 0 | 1listIndex;
const const targetItemIndex: numbertargetItemIndex = const currentItemIndex: numbercurrentItemIndex;
if (
const sourceListIndex: 0 | 1sourceListIndex === const targetListIndex: 0 | 1targetListIndex &&
const sourceItemIndex: numbersourceItemIndex === const targetItemIndex: numbertargetItemIndex
) {
const announce: (message: string) => voidannounce(`Keyboard: Cannot drop ${const itemValue: stringitemValue} onto itself.`);
const setSelectedItem: (value: React.SetStateAction<SelectedItem | null>) => voidsetSelectedItem(null);
break;
}
const const sourceItemsArray: string[]sourceItemsArray = const sourceListIndex: 0 | 1sourceListIndex === 0 ? const items1: string[]items1 : const items2: string[]items2;
const const setSourceItems: React.Dispatch<React.SetStateAction<string[]>>setSourceItems =
const sourceListIndex: 0 | 1sourceListIndex === 0 ? const setItems1: React.Dispatch<React.SetStateAction<string[]>>setItems1 : const setItems2: React.Dispatch<React.SetStateAction<string[]>>setItems2;
const const targetItemsArray: string[]targetItemsArray = const targetListIndex: 0 | 1targetListIndex === 0 ? const items1: string[]items1 : const items2: string[]items2;
const const setTargetItems: React.Dispatch<React.SetStateAction<string[]>>setTargetItems =
const targetListIndex: 0 | 1targetListIndex === 0 ? const setItems1: React.Dispatch<React.SetStateAction<string[]>>setItems1 : const setItems2: React.Dispatch<React.SetStateAction<string[]>>setItems2;
const const newSourceItems: string[]newSourceItems = [...const sourceItemsArray: string[]sourceItemsArray];
const newSourceItems: string[]newSourceItems.Array<string>.splice(start: number, deleteCount?: number): string[] (+1 overload)Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements.splice(const sourceItemIndex: numbersourceItemIndex, 1);
const const newTargetItems: string[]newTargetItems =
const sourceListIndex: 0 | 1sourceListIndex === const targetListIndex: 0 | 1targetListIndex
? const newSourceItems: string[]newSourceItems
: [...const targetItemsArray: string[]targetItemsArray];
let let effectiveTargetIndex: numbereffectiveTargetIndex = const targetItemIndex: numbertargetItemIndex;
if (const sourceListIndex: 0 | 1sourceListIndex === const targetListIndex: 0 | 1targetListIndex) {
if (const sourceItemIndex: numbersourceItemIndex < const targetItemIndex: numbertargetItemIndex) {
let effectiveTargetIndex: numbereffectiveTargetIndex = const targetItemIndex: numbertargetItemIndex - 1;
} else {
let effectiveTargetIndex: numbereffectiveTargetIndex = const targetItemIndex: numbertargetItemIndex;
}
} else {
let effectiveTargetIndex: numbereffectiveTargetIndex = const targetItemIndex: numbertargetItemIndex;
}
let effectiveTargetIndex: numbereffectiveTargetIndex = var Math: MathAn intrinsic object that provides basic mathematics functionality and constants.Math.Math.max(...values: number[]): numberReturns the larger of a set of supplied numeric expressions.max(
0,
var Math: MathAn intrinsic object that provides basic mathematics functionality and constants.Math.Math.min(...values: number[]): numberReturns the smaller of a set of supplied numeric expressions.min(let effectiveTargetIndex: numbereffectiveTargetIndex, const newTargetItems: string[]newTargetItems.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length)
);
const newTargetItems: string[]newTargetItems.Array<string>.splice(start: number, deleteCount: number, ...items: string[]): string[] (+1 overload)Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements.splice(let effectiveTargetIndex: numbereffectiveTargetIndex, 0, const itemValue: stringitemValue);
// Update state using the hook's setters
const setSourceItems: (value: React.SetStateAction<string[]>) => voidsetSourceItems(const newSourceItems: string[]newSourceItems);
if (const sourceListIndex: 0 | 1sourceListIndex !== const targetListIndex: 0 | 1targetListIndex) {
const setTargetItems: (value: React.SetStateAction<string[]>) => voidsetTargetItems(const newTargetItems: string[]newTargetItems);
}
// If same list, setSourceItems has updated the array
const announce: (message: string) => voidannounce(
`Keyboard: Moved ${const itemValue: stringitemValue} to position ${
let effectiveTargetIndex: numbereffectiveTargetIndex + 1
} in List ${const targetListIndex: 0 | 1targetListIndex + 1}.`
);
const setSelectedItem: (value: React.SetStateAction<SelectedItem | null>) => voidsetSelectedItem(null);
const setFocusedItemIndex: (value: React.SetStateAction<number | null>) => voidsetFocusedItemIndex(let effectiveTargetIndex: numbereffectiveTargetIndex);
const setFocusedListIndex: (value: React.SetStateAction<0 | 1 | null>) => voidsetFocusedListIndex(const targetListIndex: 0 | 1targetListIndex);
}
break;
case "Escape":
event: React.KeyboardEvent<HTMLUListElement>event.React.BaseSyntheticEvent<KeyboardEvent, EventTarget & HTMLUListElement, EventTarget>.preventDefault(): voidpreventDefault();
if (const selectedItem: SelectedItem | nullselectedItem) {
const announce: (message: string) => voidannounce(`Selection cancelled for ${const selectedItem: SelectedItemselectedItem.SelectedItem.value: stringvalue}.`);
const setSelectedItem: (value: React.SetStateAction<SelectedItem | null>) => voidsetSelectedItem(null);
} else {
const announce: (message: string) => voidannounce("Escape pressed. No item selected.");
(event: React.KeyboardEvent<HTMLUListElement>event.React.BaseSyntheticEvent<KeyboardEvent, EventTarget & HTMLUListElement, EventTarget>.target: EventTargettarget as HTMLElement).HTMLOrSVGElement.blur(): void[MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLElement/blur)blur();
const setFocusedListIndex: (value: React.SetStateAction<0 | 1 | null>) => voidsetFocusedListIndex(null);
const setFocusedItemIndex: (value: React.SetStateAction<number | null>) => voidsetFocusedItemIndex(null);
}
break;
default:
return;
}
},
[
const focusedListIndex: 0 | 1 | nullfocusedListIndex,
const focusedItemIndex: number | nullfocusedItemIndex,
const selectedItem: SelectedItem | nullselectedItem,
const items1: string[]items1,
const items2: string[]items2,
const setItems1: React.Dispatch<React.SetStateAction<string[]>>setItems1,
const setItems2: React.Dispatch<React.SetStateAction<string[]>>setItems2,
const announce: (message: string) => voidannounce,
]
);
return (
// Using a standard div wrapper instead of DemoContainer
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="container p-4 border-4 border-blue-300">
<JSX.IntrinsicElements.h1: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>h1 React.HTMLAttributes<T>.className?: string | undefinedclassName="text-xl font-bold mb-4">React Accessibility Demo</JSX.IntrinsicElements.h1: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>h1>
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="accessibility-demo">
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.RefAttributes<HTMLDivElement>.ref?: React.LegacyRef<HTMLDivElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const liveRegionRef: React.RefObject<HTMLDivElement>liveRegionRef} React.AriaAttributes["aria-live"]?: "polite" | "off" | "assertive" | undefinedIndicates that an element will be updated, and describes the types of updates the user agents, assistive technologies, and user can expect from the live region.aria-live="polite" React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="sr-only">
{const liveMessage: stringliveMessage}
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="lists-container">
{/* List 1 */}
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="list-wrapper">
<JSX.IntrinsicElements.h2: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>h2 React.HTMLAttributes<HTMLHeadingElement>.id?: string | undefinedid="list1-item-heading">List 1</JSX.IntrinsicElements.h2: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>h2>
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul
React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const list1Ref: React.RefObject<HTMLUListElement>list1Ref} // Assign ref from hook
React.HTMLAttributes<T>.className?: string | undefinedclassName="list"
React.HTMLAttributes<HTMLUListElement>.tabIndex?: number | undefinedtabIndex={0}
React.HTMLAttributes<HTMLUListElement>.role?: React.AriaRole | undefinedrole="listbox"
React.AriaAttributes["aria-labelledby"]?: string | undefinedIdentifies the element (or elements) that labels the current element.aria-labelledby="list1-item-heading"
React.AriaAttributes["aria-activedescendant"]?: string | undefinedIdentifies the currently active element when DOM focus is on a composite widget, textbox, group, or application.aria-activedescendant={
const focusedListIndex: 0 | 1 | nullfocusedListIndex === 0 ? const focusedItemId: string | undefinedfocusedItemId : var undefinedundefined
}
React.DOMAttributes<HTMLUListElement>.onFocus?: React.FocusEventHandler<HTMLUListElement> | undefinedonFocus={() => const handleListFocus: (listIndex: 0 | 1) => voidhandleListFocus(0)}
React.DOMAttributes<HTMLUListElement>.onBlur?: React.FocusEventHandler<HTMLUListElement> | undefinedonBlur={const handleListBlur: () => voidhandleListBlur}
React.DOMAttributes<HTMLUListElement>.onKeyDown?: React.KeyboardEventHandler<HTMLUListElement> | undefinedonKeyDown={(e: React.KeyboardEvent<HTMLUListElement>e) => const handleListKeydown: (event: React.KeyboardEvent<HTMLUListElement>, listIndex: 0 | 1) => voidhandleListKeydown(e: React.KeyboardEvent<HTMLUListElement>e, 0)}
>
{const items1: string[]items1.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length > 0 ? (
const items1: string[]items1.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((item: stringitem, index: numberindex) => {
const const isFocused: booleanisFocused =
const focusedListIndex: 0 | 1 | nullfocusedListIndex === 0 && const focusedItemIndex: number | nullfocusedItemIndex === index: numberindex;
const const isSelected: booleanisSelected =
const selectedItem: SelectedItem | nullselectedItem?.SelectedItem.listIndex: 0 | 1 | undefinedlistIndex === 0 &&
const selectedItem: SelectedItemselectedItem?.SelectedItem.itemIndex: numberitemIndex === index: numberindex;
const const itemId: stringitemId = `list1-item-${item: stringitem}`;
return (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li
React.Attributes.key?: React.Key | null | undefinedkey={const itemId: stringitemId}
React.HTMLAttributes<T>.id?: string | undefinedid={const itemId: stringitemId}
React.HTMLAttributes<T>.className?: string | undefinedclassName={`item ${const isFocused: booleanisFocused ? "item-focused" : ""} ${
const isSelected: booleanisSelected ? "item-selected" : ""
}`}
React.HTMLAttributes<T>.role?: React.AriaRole | undefinedrole="option"
React.AriaAttributes["aria-selected"]?: Booleanish | undefinedIndicates the current "selected" state of various widgets.aria-selected={const isFocused: booleanisFocused}
React.HTMLAttributes<T>.tabIndex?: number | undefinedtabIndex={-1}
>
{item: stringitem}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
);
})
) : (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<T>.className?: string | undefinedclassName="empty-list-message">Empty</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
)}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
{/* List 2 */}
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="list-wrapper">
<JSX.IntrinsicElements.h2: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>h2 React.HTMLAttributes<HTMLHeadingElement>.id?: string | undefinedid="list2-item-heading">List 2</JSX.IntrinsicElements.h2: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>h2>
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul
React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const list2Ref: React.RefObject<HTMLUListElement>list2Ref} // Assign ref from hook
React.HTMLAttributes<T>.className?: string | undefinedclassName="list"
React.HTMLAttributes<HTMLUListElement>.tabIndex?: number | undefinedtabIndex={0}
React.HTMLAttributes<HTMLUListElement>.role?: React.AriaRole | undefinedrole="listbox"
React.AriaAttributes["aria-labelledby"]?: string | undefinedIdentifies the element (or elements) that labels the current element.aria-labelledby="list2-item-heading"
React.AriaAttributes["aria-activedescendant"]?: string | undefinedIdentifies the currently active element when DOM focus is on a composite widget, textbox, group, or application.aria-activedescendant={
const focusedListIndex: 0 | 1 | nullfocusedListIndex === 1 ? const focusedItemId: string | undefinedfocusedItemId : var undefinedundefined
}
React.DOMAttributes<HTMLUListElement>.onFocus?: React.FocusEventHandler<HTMLUListElement> | undefinedonFocus={() => const handleListFocus: (listIndex: 0 | 1) => voidhandleListFocus(1)}
React.DOMAttributes<HTMLUListElement>.onBlur?: React.FocusEventHandler<HTMLUListElement> | undefinedonBlur={const handleListBlur: () => voidhandleListBlur}
React.DOMAttributes<HTMLUListElement>.onKeyDown?: React.KeyboardEventHandler<HTMLUListElement> | undefinedonKeyDown={(e: React.KeyboardEvent<HTMLUListElement>e) => const handleListKeydown: (event: React.KeyboardEvent<HTMLUListElement>, listIndex: 0 | 1) => voidhandleListKeydown(e: React.KeyboardEvent<HTMLUListElement>e, 1)}
>
{const items2: string[]items2.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length > 0 ? (
const items2: string[]items2.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((item: stringitem, index: numberindex) => {
const const isFocused: booleanisFocused =
const focusedListIndex: 0 | 1 | nullfocusedListIndex === 1 && const focusedItemIndex: number | nullfocusedItemIndex === index: numberindex;
const const isSelected: booleanisSelected =
const selectedItem: SelectedItem | nullselectedItem?.SelectedItem.listIndex: 0 | 1 | undefinedlistIndex === 1 &&
const selectedItem: SelectedItemselectedItem?.SelectedItem.itemIndex: numberitemIndex === index: numberindex;
const const itemId: stringitemId = `list2-item-${item: stringitem}`;
return (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li
React.Attributes.key?: React.Key | null | undefinedkey={const itemId: stringitemId}
React.HTMLAttributes<T>.id?: string | undefinedid={const itemId: stringitemId}
React.HTMLAttributes<T>.className?: string | undefinedclassName={`item ${const isFocused: booleanisFocused ? "item-focused" : ""} ${
const isSelected: booleanisSelected ? "item-selected" : ""
}`}
React.HTMLAttributes<T>.role?: React.AriaRole | undefinedrole="option"
React.AriaAttributes["aria-selected"]?: Booleanish | undefinedIndicates the current "selected" state of various widgets.aria-selected={const isFocused: booleanisFocused}
React.HTMLAttributes<T>.tabIndex?: number | undefinedtabIndex={-1}
>
{item: stringitem}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
);
})
) : (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<T>.className?: string | undefinedclassName="empty-list-message">Empty</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
)}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="instructions">
<JSX.IntrinsicElements.h3: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>h3>Keyboard Instructions</JSX.IntrinsicElements.h3: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>h3>
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
Use <JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd>Tab</JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd> or <JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd>Shift+Tab</JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd> to focus a list.
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
Use <JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd>↑</JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd> / <JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd>↓</JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd> arrows to navigate items within
the focused list.
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
Press <JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd>Spacebar</JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd> to select/deselect the highlighted item
for moving.
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
With an item selected, use <JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd>↑</JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd> / <JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd>↓</JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd> (within the
same or another focused list) to choose the drop position.
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
Press <JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd>Enter</JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd> to drop the selected item at the
highlighted position.
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
Press <JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd>Escape</JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd> to cancel a selection or leave the list.
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
{/* Styles assumed to be global or handled via CSS Modules/Tailwind etc. */}
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
);
}
import React, { function useState<S>(initialState: S | (() => S)): [S, React.Dispatch<React.SetStateAction<S>>] (+1 overload)Returns a stateful value, and a function to update it.useState, function useRef<T>(initialValue: T): React.MutableRefObject<T> (+2 overloads)`useRef` returns a mutable ref object whose `.current` property is initialized to the passed argument
(`initialValue`). The returned object will persist for the full lifetime of the component.
Note that `useRef()` is useful for more than the `ref` attribute. It’s handy for keeping any mutable
value around similar to how you’d use instance fields in classes.useRef, function useCallback<T extends Function>(callback: T, deps: React.DependencyList): T`useCallback` will return a memoized version of the callback that only changes if one of the `inputs`
has changed.useCallback, function useMemo<T>(factory: () => T, deps: React.DependencyList): T`useMemo` will only recompute the memoized value when one of the `deps` has changed.useMemo } from "react";
import { function useDragAndDrop<E extends HTMLElement, T = unknown>(list: T[], options?: Partial<ParentConfig<T>>): [React.RefObject<E>, T[], React.Dispatch<React.SetStateAction<T[]>>, (config: Partial<ParentConfig<...>>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop } from "@formkit/drag-and-drop/react";
// Initial data for the lists
const const initialItems1: string[]initialItems1 = ["Apples", "Bananas", "Oranges", "Grapes"];
const const initialItems2: string[]initialItems2 = ["Milk", "Cheese", "Yogurt"];
interface SelectedItem {
SelectedItem.listIndex: 0 | 1listIndex: 0 | 1;
SelectedItem.itemIndex: numberitemIndex: number;
SelectedItem.value: stringvalue: string;
}
export default function function AccessibilityDemoReact(): React.JSX.ElementAccessibilityDemoReact() {
// Refs for ARIA Live region
const [const liveMessage: stringliveMessage, const setLiveMessage: React.Dispatch<React.SetStateAction<string>>setLiveMessage] = useState<string>(initialState: string | (() => string)): [string, React.Dispatch<React.SetStateAction<string>>] (+1 overload)Returns a stateful value, and a function to update it.useState("");
const const liveRegionRef: React.RefObject<HTMLDivElement>liveRegionRef = useRef<HTMLDivElement>(initialValue: HTMLDivElement | null): React.RefObject<HTMLDivElement> (+2 overloads)`useRef` returns a mutable ref object whose `.current` property is initialized to the passed argument
(`initialValue`). The returned object will persist for the full lifetime of the component.
Note that `useRef()` is useful for more than the `ref` attribute. It’s handy for keeping any mutable
value around similar to how you’d use instance fields in classes.
Usage note: if you need the result of useRef to be directly mutable, include `| null` in the type
of the generic argument.useRef<HTMLDivElement>(null);
// Function to update the live region
const const announce: (message: string) => voidannounce = useCallback<(message: string) => void>(callback: (message: string) => void, deps: React.DependencyList): (message: string) => void`useCallback` will return a memoized version of the callback that only changes if one of the `inputs`
has changed.useCallback((message: stringmessage: string) => {
const setLiveMessage: (value: React.SetStateAction<string>) => voidsetLiveMessage(message: stringmessage);
// Optional: Clear the message after a delay
// const timer = setTimeout(() => setLiveMessage(''), 5000);
// return () => clearTimeout(timer);
}, []);
// Setup drag and drop for List 1
const [const list1Ref: React.RefObject<HTMLUListElement>list1Ref, const items1: string[]items1, const setItems1: React.Dispatch<React.SetStateAction<string[]>>setItems1] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<
HTMLUListElement,
string
>(const initialItems1: string[]initialItems1, {
group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group: "accessibleList",
// Let types be inferred
onDragstart?: DragstartEvent | undefinedFired when a drag is started, whether native drag or syntheticonDragstart: (state: DragstartEventData<T>state) =>
const announce: (message: string) => voidannounce(`Drag started for ${state: DragstartEventData<T>state.DragstartEventData<T>.draggedNode: NodeRecord<T>draggedNode.NodeRecord<T>.data: NodeData<T>data.NodeData<T>.value: TThe value of the node.value}.`),
onSort?: SortEvent | undefinedCallback function for when a sort operation is performed.onSort: (event: SortEventData<T>event) =>
const announce: (message: string) => voidannounce(
`Sorted ${event: SortEventData<T>event.SortEventData<T>.draggedNodes: NodeRecord<T>[]draggedNodes[0].NodeRecord<T>.data: NodeData<T>data.NodeData<T>.value: TThe value of the node.value} in List 1 to position ${
event: SortEventData<T>event.SortEventData<T>.position: numberposition + 1
}.`
),
onTransfer?: TransferEvent | undefinedCallback function for when a transfer operation is performed.onTransfer: (event: TransferEventData<T>event) => {
const announce: (message: string) => voidannounce(
`Transferred ${event: TransferEventData<T>event.TransferEventData<T>.draggedNodes: NodeRecord<T>[]draggedNodes[0].NodeRecord<T>.data: NodeData<T>data.NodeData<T>.value: TThe value of the node.value} from List ${
event: TransferEventData<T>event.TransferEventData<T>.sourceParent: ParentRecord<T>sourceParent.ParentRecord<T>.el: HTMLElementel === const list1Ref: React.RefObject<HTMLUListElement>list1Ref.React.RefObject<HTMLUListElement>.current: HTMLUListElement | nullThe current value of the ref.current ? 1 : 2
} to List 1 at position ${event: TransferEventData<T>event.TransferEventData<T>.targetIndex: numbertargetIndex + 1}.`
);
},
onDragend?: DragendEvent | undefinedFired when a drag is ended, whether native drag or syntheticonDragend: (state: DragendEventData<T>state) =>
const announce: (message: string) => voidannounce(`Drag ended for ${state: DragendEventData<T>state.DragendEventData<T>.draggedNode: NodeRecord<T>draggedNode.NodeRecord<T>.data: NodeData<T>data.NodeData<T>.value: TThe value of the node.value}.`),
});
// Setup drag and drop for List 2
const [const list2Ref: React.RefObject<HTMLUListElement>list2Ref, const items2: string[]items2, const setItems2: React.Dispatch<React.SetStateAction<string[]>>setItems2] = useDragAndDrop<HTMLUListElement, string>(list: string[], options?: Partial<ParentConfig<string>> | undefined): [React.RefObject<HTMLUListElement>, string[], React.Dispatch<...>, (config: Partial<...>) => void]Hook for adding drag and drop/sortable support to a list of items.useDragAndDrop<
HTMLUListElement,
string
>(const initialItems2: string[]initialItems2, {
group?: string | undefinedThe group that the parent belongs to. This is used for allowing multiple
parents to transfer nodes between each other.group: "accessibleList",
onDragstart?: DragstartEvent | undefinedFired when a drag is started, whether native drag or syntheticonDragstart: (state: DragstartEventData<T>state) =>
const announce: (message: string) => voidannounce(`Drag started for ${state: DragstartEventData<T>state.DragstartEventData<T>.draggedNode: NodeRecord<T>draggedNode.NodeRecord<T>.data: NodeData<T>data.NodeData<T>.value: TThe value of the node.value}.`),
onSort?: SortEvent | undefinedCallback function for when a sort operation is performed.onSort: (event: SortEventData<T>event) =>
const announce: (message: string) => voidannounce(
`Sorted ${event: SortEventData<T>event.SortEventData<T>.draggedNodes: NodeRecord<T>[]draggedNodes[0].NodeRecord<T>.data: NodeData<T>data.NodeData<T>.value: TThe value of the node.value} in List 2 to position ${
event: SortEventData<T>event.SortEventData<T>.position: numberposition + 1
}.`
),
onTransfer?: TransferEvent | undefinedCallback function for when a transfer operation is performed.onTransfer: (event: TransferEventData<T>event) => {
// Compare against element refs
const announce: (message: string) => voidannounce(
`Transferred ${event: TransferEventData<T>event.TransferEventData<T>.draggedNodes: NodeRecord<T>[]draggedNodes[0].NodeRecord<T>.data: NodeData<T>data.NodeData<T>.value: TThe value of the node.value} from List ${
event: TransferEventData<T>event.TransferEventData<T>.sourceParent: ParentRecord<T>sourceParent.ParentRecord<T>.el: HTMLElementel === const list1Ref: React.RefObject<HTMLUListElement>list1Ref.React.RefObject<HTMLUListElement>.current: HTMLUListElement | nullThe current value of the ref.current ? 1 : 2
} to List 2 at position ${event: TransferEventData<T>event.TransferEventData<T>.targetIndex: numbertargetIndex + 1}.`
);
},
onDragend?: DragendEvent | undefinedFired when a drag is ended, whether native drag or syntheticonDragend: (state: DragendEventData<T>state) =>
const announce: (message: string) => voidannounce(`Drag ended for ${state: DragendEventData<T>state.DragendEventData<T>.draggedNode: NodeRecord<T>draggedNode.NodeRecord<T>.data: NodeData<T>data.NodeData<T>.value: TThe value of the node.value}.`),
});
// State for keyboard navigation
const [const focusedListIndex: 0 | 1 | nullfocusedListIndex, const setFocusedListIndex: React.Dispatch<React.SetStateAction<0 | 1 | null>>setFocusedListIndex] = useState<0 | 1 | null>(initialState: 0 | 1 | (() => 0 | 1 | null) | null): [0 | 1 | null, React.Dispatch<React.SetStateAction<0 | 1 | null>>] (+1 overload)Returns a stateful value, and a function to update it.useState<0 | 1 | null>(null);
const [const focusedItemIndex: number | nullfocusedItemIndex, const setFocusedItemIndex: React.Dispatch<React.SetStateAction<number | null>>setFocusedItemIndex] = useState<number | null>(initialState: number | (() => number | null) | null): [number | null, React.Dispatch<React.SetStateAction<number | null>>] (+1 overload)Returns a stateful value, and a function to update it.useState<number | null>(null);
const [const selectedItem: SelectedItem | nullselectedItem, const setSelectedItem: React.Dispatch<React.SetStateAction<SelectedItem | null>>setSelectedItem] = useState<SelectedItem | null>(initialState: SelectedItem | (() => SelectedItem | null) | null): [SelectedItem | null, React.Dispatch<...>] (+1 overload)Returns a stateful value, and a function to update it.useState<SelectedItem | null>(null);
// Calculate focusedItemId directly
const const focusedItemId: string | undefinedfocusedItemId = useMemo<string | undefined>(factory: () => string | undefined, deps: React.DependencyList): string | undefined`useMemo` will only recompute the memoized value when one of the `deps` has changed.useMemo(() => {
if (
const focusedListIndex: 0 | 1 | nullfocusedListIndex === 0 &&
const focusedItemIndex: number | nullfocusedItemIndex !== null &&
const focusedItemIndex: numberfocusedItemIndex < const items1: string[]items1.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length
) {
return `list1-item-${const items1: string[]items1[const focusedItemIndex: numberfocusedItemIndex]}`;
}
if (
const focusedListIndex: 0 | 1 | nullfocusedListIndex === 1 &&
const focusedItemIndex: number | nullfocusedItemIndex !== null &&
const focusedItemIndex: numberfocusedItemIndex < const items2: string[]items2.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length
) {
return `list2-item-${const items2: string[]items2[const focusedItemIndex: numberfocusedItemIndex]}`;
}
return var undefinedundefined;
}, [const focusedListIndex: 0 | 1 | nullfocusedListIndex, const focusedItemIndex: number | nullfocusedItemIndex, const items1: string[]items1, const items2: string[]items2]);
const const handleListFocus: (listIndex: 0 | 1) => voidhandleListFocus = useCallback<(listIndex: 0 | 1) => void>(callback: (listIndex: 0 | 1) => void, deps: React.DependencyList): (listIndex: 0 | 1) => void`useCallback` will return a memoized version of the callback that only changes if one of the `inputs`
has changed.useCallback(
(listIndex: 0 | 1listIndex: 0 | 1) => {
const setFocusedListIndex: (value: React.SetStateAction<0 | 1 | null>) => voidsetFocusedListIndex(listIndex: 0 | 1listIndex);
const const currentItems: string[]currentItems = listIndex: 0 | 1listIndex === 0 ? const items1: string[]items1 : const items2: string[]items2;
if (const focusedItemIndex: number | nullfocusedItemIndex === null && const currentItems: string[]currentItems.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length > 0) {
const setFocusedItemIndex: (value: React.SetStateAction<number | null>) => voidsetFocusedItemIndex(0);
}
const announce: (message: string) => voidannounce(
`List ${
listIndex: 0 | 1listIndex + 1
} focused. Use up and down arrows to navigate items.`
);
},
[const items1: string[]items1, const items2: string[]items2, const announce: (message: string) => voidannounce, const focusedItemIndex: number | nullfocusedItemIndex]
);
const const handleListBlur: () => voidhandleListBlur = useCallback<() => void>(callback: () => void, deps: React.DependencyList): () => void`useCallback` will return a memoized version of the callback that only changes if one of the `inputs`
has changed.useCallback(() => {
// Minimal blur handler - prevents immediate focus loss when clicking between elements
// More complex logic might be needed for specific focus management scenarios
}, []);
const const handleListKeydown: (event: React.KeyboardEvent<HTMLUListElement>, listIndex: 0 | 1) => voidhandleListKeydown = useCallback<(event: React.KeyboardEvent<HTMLUListElement>, listIndex: 0 | 1) => void>(callback: (event: React.KeyboardEvent<HTMLUListElement>, listIndex: 0 | 1) => void, deps: React.DependencyList): (event: React.KeyboardEvent<HTMLUListElement>, listIndex: 0 | 1) => void`useCallback` will return a memoized version of the callback that only changes if one of the `inputs`
has changed.useCallback(
(event: React.KeyboardEvent<HTMLUListElement>event: React.interface React.KeyboardEvent<T = Element>KeyboardEvent<HTMLUListElement>, listIndex: 0 | 1listIndex: 0 | 1) => {
if (const focusedListIndex: 0 | 1 | nullfocusedListIndex !== listIndex: 0 | 1listIndex) return;
const const currentItems: string[]currentItems = listIndex: 0 | 1listIndex === 0 ? const items1: string[]items1 : const items2: string[]items2;
const const setItems: React.Dispatch<React.SetStateAction<string[]>>setItems = listIndex: 0 | 1listIndex === 0 ? const setItems1: React.Dispatch<React.SetStateAction<string[]>>setItems1 : const setItems2: React.Dispatch<React.SetStateAction<string[]>>setItems2;
const const currentItemIndex: number | nullcurrentItemIndex = const focusedItemIndex: number | nullfocusedItemIndex;
switch (event: React.KeyboardEvent<HTMLUListElement>event.React.KeyboardEvent<HTMLUListElement>.key: stringSee the [DOM Level 3 Events spec](https://www.w3.org/TR/uievents-key/#named-key-attribute-values). for possible valueskey) {
case "ArrowDown":
event: React.KeyboardEvent<HTMLUListElement>event.React.BaseSyntheticEvent<KeyboardEvent, EventTarget & HTMLUListElement, EventTarget>.preventDefault(): voidpreventDefault();
if (const currentItems: string[]currentItems.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length > 0) {
const const nextIndex: numbernextIndex =
const currentItemIndex: number | nullcurrentItemIndex === null
? 0
: (const currentItemIndex: numbercurrentItemIndex + 1) % const currentItems: string[]currentItems.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length;
const setFocusedItemIndex: (value: React.SetStateAction<number | null>) => voidsetFocusedItemIndex(const nextIndex: numbernextIndex);
const announce: (message: string) => voidannounce(const currentItems: string[]currentItems[const nextIndex: numbernextIndex]);
}
break;
case "ArrowUp":
event: React.KeyboardEvent<HTMLUListElement>event.React.BaseSyntheticEvent<KeyboardEvent, EventTarget & HTMLUListElement, EventTarget>.preventDefault(): voidpreventDefault();
if (const currentItems: string[]currentItems.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length > 0) {
const const prevIndex: numberprevIndex =
const currentItemIndex: number | nullcurrentItemIndex === null
? const currentItems: string[]currentItems.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length - 1
: (const currentItemIndex: numbercurrentItemIndex - 1 + const currentItems: string[]currentItems.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length) %
const currentItems: string[]currentItems.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length;
const setFocusedItemIndex: (value: React.SetStateAction<number | null>) => voidsetFocusedItemIndex(const prevIndex: numberprevIndex);
const announce: (message: string) => voidannounce(const currentItems: string[]currentItems[const prevIndex: numberprevIndex]);
}
break;
case " ": // Spacebar
event: React.KeyboardEvent<HTMLUListElement>event.React.BaseSyntheticEvent<KeyboardEvent, EventTarget & HTMLUListElement, EventTarget>.preventDefault(): voidpreventDefault();
if (
const currentItemIndex: number | nullcurrentItemIndex !== null &&
const currentItemIndex: numbercurrentItemIndex >= 0 &&
const currentItemIndex: numbercurrentItemIndex < const currentItems: string[]currentItems.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length
) {
const const currentItemValue: stringcurrentItemValue = const currentItems: string[]currentItems[const currentItemIndex: numbercurrentItemIndex];
if (
const selectedItem: SelectedItem | nullselectedItem?.SelectedItem.listIndex: 0 | 1 | undefinedlistIndex === listIndex: 0 | 1listIndex &&
const selectedItem: SelectedItemselectedItem?.SelectedItem.itemIndex: numberitemIndex === const currentItemIndex: numbercurrentItemIndex
) {
const setSelectedItem: (value: React.SetStateAction<SelectedItem | null>) => voidsetSelectedItem(null);
const announce: (message: string) => voidannounce(`${const currentItemValue: stringcurrentItemValue} deselected.`);
} else {
const setSelectedItem: (value: React.SetStateAction<SelectedItem | null>) => voidsetSelectedItem({
SelectedItem.listIndex: 0 | 1listIndex,
SelectedItem.itemIndex: numberitemIndex: const currentItemIndex: numbercurrentItemIndex,
SelectedItem.value: stringvalue: const currentItemValue: stringcurrentItemValue,
});
const announce: (message: string) => voidannounce(
`${const currentItemValue: stringcurrentItemValue} selected. Use arrow keys to choose drop position, then press Enter.`
);
}
}
break;
case "Enter":
event: React.KeyboardEvent<HTMLUListElement>event.React.BaseSyntheticEvent<KeyboardEvent, EventTarget & HTMLUListElement, EventTarget>.preventDefault(): voidpreventDefault();
if (
const selectedItem: SelectedItem | nullselectedItem &&
const currentItemIndex: number | nullcurrentItemIndex !== null &&
const currentItemIndex: numbercurrentItemIndex >= 0 &&
const currentItemIndex: numbercurrentItemIndex < const currentItems: string[]currentItems.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length
) {
const {
SelectedItem.listIndex: 0 | 1listIndex: const sourceListIndex: 0 | 1sourceListIndex,
SelectedItem.itemIndex: numberitemIndex: const sourceItemIndex: numbersourceItemIndex,
SelectedItem.value: stringvalue: const itemValue: stringitemValue,
} = const selectedItem: SelectedItemselectedItem;
const const targetListIndex: 0 | 1targetListIndex = listIndex: 0 | 1listIndex;
const const targetItemIndex: numbertargetItemIndex = const currentItemIndex: numbercurrentItemIndex;
if (
const sourceListIndex: 0 | 1sourceListIndex === const targetListIndex: 0 | 1targetListIndex &&
const sourceItemIndex: numbersourceItemIndex === const targetItemIndex: numbertargetItemIndex
) {
const announce: (message: string) => voidannounce(`Keyboard: Cannot drop ${const itemValue: stringitemValue} onto itself.`);
const setSelectedItem: (value: React.SetStateAction<SelectedItem | null>) => voidsetSelectedItem(null);
break;
}
const const sourceItemsArray: string[]sourceItemsArray = const sourceListIndex: 0 | 1sourceListIndex === 0 ? const items1: string[]items1 : const items2: string[]items2;
const const setSourceItems: React.Dispatch<React.SetStateAction<string[]>>setSourceItems =
const sourceListIndex: 0 | 1sourceListIndex === 0 ? const setItems1: React.Dispatch<React.SetStateAction<string[]>>setItems1 : const setItems2: React.Dispatch<React.SetStateAction<string[]>>setItems2;
const const targetItemsArray: string[]targetItemsArray = const targetListIndex: 0 | 1targetListIndex === 0 ? const items1: string[]items1 : const items2: string[]items2;
const const setTargetItems: React.Dispatch<React.SetStateAction<string[]>>setTargetItems =
const targetListIndex: 0 | 1targetListIndex === 0 ? const setItems1: React.Dispatch<React.SetStateAction<string[]>>setItems1 : const setItems2: React.Dispatch<React.SetStateAction<string[]>>setItems2;
const const newSourceItems: string[]newSourceItems = [...const sourceItemsArray: string[]sourceItemsArray];
const newSourceItems: string[]newSourceItems.Array<string>.splice(start: number, deleteCount?: number): string[] (+1 overload)Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements.splice(const sourceItemIndex: numbersourceItemIndex, 1);
const const newTargetItems: string[]newTargetItems =
const sourceListIndex: 0 | 1sourceListIndex === const targetListIndex: 0 | 1targetListIndex
? const newSourceItems: string[]newSourceItems
: [...const targetItemsArray: string[]targetItemsArray];
let let effectiveTargetIndex: numbereffectiveTargetIndex = const targetItemIndex: numbertargetItemIndex;
if (const sourceListIndex: 0 | 1sourceListIndex === const targetListIndex: 0 | 1targetListIndex) {
if (const sourceItemIndex: numbersourceItemIndex < const targetItemIndex: numbertargetItemIndex) {
let effectiveTargetIndex: numbereffectiveTargetIndex = const targetItemIndex: numbertargetItemIndex - 1;
} else {
let effectiveTargetIndex: numbereffectiveTargetIndex = const targetItemIndex: numbertargetItemIndex;
}
} else {
let effectiveTargetIndex: numbereffectiveTargetIndex = const targetItemIndex: numbertargetItemIndex;
}
let effectiveTargetIndex: numbereffectiveTargetIndex = var Math: MathAn intrinsic object that provides basic mathematics functionality and constants.Math.Math.max(...values: number[]): numberReturns the larger of a set of supplied numeric expressions.max(
0,
var Math: MathAn intrinsic object that provides basic mathematics functionality and constants.Math.Math.min(...values: number[]): numberReturns the smaller of a set of supplied numeric expressions.min(let effectiveTargetIndex: numbereffectiveTargetIndex, const newTargetItems: string[]newTargetItems.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length)
);
const newTargetItems: string[]newTargetItems.Array<string>.splice(start: number, deleteCount: number, ...items: string[]): string[] (+1 overload)Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements.splice(let effectiveTargetIndex: numbereffectiveTargetIndex, 0, const itemValue: stringitemValue);
// Update state using the hook's setters
const setSourceItems: (value: React.SetStateAction<string[]>) => voidsetSourceItems(const newSourceItems: string[]newSourceItems);
if (const sourceListIndex: 0 | 1sourceListIndex !== const targetListIndex: 0 | 1targetListIndex) {
const setTargetItems: (value: React.SetStateAction<string[]>) => voidsetTargetItems(const newTargetItems: string[]newTargetItems);
}
// If same list, setSourceItems has updated the array
const announce: (message: string) => voidannounce(
`Keyboard: Moved ${const itemValue: stringitemValue} to position ${
let effectiveTargetIndex: numbereffectiveTargetIndex + 1
} in List ${const targetListIndex: 0 | 1targetListIndex + 1}.`
);
const setSelectedItem: (value: React.SetStateAction<SelectedItem | null>) => voidsetSelectedItem(null);
const setFocusedItemIndex: (value: React.SetStateAction<number | null>) => voidsetFocusedItemIndex(let effectiveTargetIndex: numbereffectiveTargetIndex);
const setFocusedListIndex: (value: React.SetStateAction<0 | 1 | null>) => voidsetFocusedListIndex(const targetListIndex: 0 | 1targetListIndex);
}
break;
case "Escape":
event: React.KeyboardEvent<HTMLUListElement>event.React.BaseSyntheticEvent<KeyboardEvent, EventTarget & HTMLUListElement, EventTarget>.preventDefault(): voidpreventDefault();
if (const selectedItem: SelectedItem | nullselectedItem) {
const announce: (message: string) => voidannounce(`Selection cancelled for ${const selectedItem: SelectedItemselectedItem.SelectedItem.value: stringvalue}.`);
const setSelectedItem: (value: React.SetStateAction<SelectedItem | null>) => voidsetSelectedItem(null);
} else {
const announce: (message: string) => voidannounce("Escape pressed. No item selected.");
(event: React.KeyboardEvent<HTMLUListElement>event.React.BaseSyntheticEvent<KeyboardEvent, EventTarget & HTMLUListElement, EventTarget>.target: EventTargettarget as HTMLElement).HTMLOrSVGElement.blur(): void[MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLElement/blur)blur();
const setFocusedListIndex: (value: React.SetStateAction<0 | 1 | null>) => voidsetFocusedListIndex(null);
const setFocusedItemIndex: (value: React.SetStateAction<number | null>) => voidsetFocusedItemIndex(null);
}
break;
default:
return;
}
},
[
const focusedListIndex: 0 | 1 | nullfocusedListIndex,
const focusedItemIndex: number | nullfocusedItemIndex,
const selectedItem: SelectedItem | nullselectedItem,
const items1: string[]items1,
const items2: string[]items2,
const setItems1: React.Dispatch<React.SetStateAction<string[]>>setItems1,
const setItems2: React.Dispatch<React.SetStateAction<string[]>>setItems2,
const announce: (message: string) => voidannounce,
]
);
return (
// Using a standard div wrapper instead of DemoContainer
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="container p-4 border-4 border-blue-300">
<JSX.IntrinsicElements.h1: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>h1 React.HTMLAttributes<T>.className?: string | undefinedclassName="text-xl font-bold mb-4">React Accessibility Demo</JSX.IntrinsicElements.h1: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>h1>
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="accessibility-demo">
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.RefAttributes<HTMLDivElement>.ref?: React.LegacyRef<HTMLDivElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const liveRegionRef: React.RefObject<HTMLDivElement>liveRegionRef} React.AriaAttributes["aria-live"]?: "polite" | "off" | "assertive" | undefinedIndicates that an element will be updated, and describes the types of updates the user agents, assistive technologies, and user can expect from the live region.aria-live="polite" React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="sr-only">
{const liveMessage: stringliveMessage}
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="lists-container">
{/* List 1 */}
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="list-wrapper">
<JSX.IntrinsicElements.h2: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>h2 React.HTMLAttributes<HTMLHeadingElement>.id?: string | undefinedid="list1-item-heading">List 1</JSX.IntrinsicElements.h2: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>h2>
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul
React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const list1Ref: React.RefObject<HTMLUListElement>list1Ref} // Assign ref from hook
React.HTMLAttributes<T>.className?: string | undefinedclassName="list"
React.HTMLAttributes<HTMLUListElement>.tabIndex?: number | undefinedtabIndex={0}
React.HTMLAttributes<HTMLUListElement>.role?: React.AriaRole | undefinedrole="listbox"
React.AriaAttributes["aria-labelledby"]?: string | undefinedIdentifies the element (or elements) that labels the current element.aria-labelledby="list1-item-heading"
React.AriaAttributes["aria-activedescendant"]?: string | undefinedIdentifies the currently active element when DOM focus is on a composite widget, textbox, group, or application.aria-activedescendant={
const focusedListIndex: 0 | 1 | nullfocusedListIndex === 0 ? const focusedItemId: string | undefinedfocusedItemId : var undefinedundefined
}
React.DOMAttributes<HTMLUListElement>.onFocus?: React.FocusEventHandler<HTMLUListElement> | undefinedonFocus={() => const handleListFocus: (listIndex: 0 | 1) => voidhandleListFocus(0)}
React.DOMAttributes<HTMLUListElement>.onBlur?: React.FocusEventHandler<HTMLUListElement> | undefinedonBlur={const handleListBlur: () => voidhandleListBlur}
React.DOMAttributes<HTMLUListElement>.onKeyDown?: React.KeyboardEventHandler<HTMLUListElement> | undefinedonKeyDown={(e: React.KeyboardEvent<HTMLUListElement>e) => const handleListKeydown: (event: React.KeyboardEvent<HTMLUListElement>, listIndex: 0 | 1) => voidhandleListKeydown(e: React.KeyboardEvent<HTMLUListElement>e, 0)}
>
{const items1: string[]items1.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length > 0 ? (
const items1: string[]items1.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((item: stringitem, index: numberindex) => {
const const isFocused: booleanisFocused =
const focusedListIndex: 0 | 1 | nullfocusedListIndex === 0 && const focusedItemIndex: number | nullfocusedItemIndex === index: numberindex;
const const isSelected: booleanisSelected =
const selectedItem: SelectedItem | nullselectedItem?.SelectedItem.listIndex: 0 | 1 | undefinedlistIndex === 0 &&
const selectedItem: SelectedItemselectedItem?.SelectedItem.itemIndex: numberitemIndex === index: numberindex;
const const itemId: stringitemId = `list1-item-${item: stringitem}`;
return (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li
React.Attributes.key?: React.Key | null | undefinedkey={const itemId: stringitemId}
React.HTMLAttributes<T>.id?: string | undefinedid={const itemId: stringitemId}
React.HTMLAttributes<T>.className?: string | undefinedclassName={`item ${const isFocused: booleanisFocused ? "item-focused" : ""} ${
const isSelected: booleanisSelected ? "item-selected" : ""
}`}
React.HTMLAttributes<T>.role?: React.AriaRole | undefinedrole="option"
React.AriaAttributes["aria-selected"]?: Booleanish | undefinedIndicates the current "selected" state of various widgets.aria-selected={const isFocused: booleanisFocused}
React.HTMLAttributes<T>.tabIndex?: number | undefinedtabIndex={-1}
>
{item: stringitem}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
);
})
) : (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<T>.className?: string | undefinedclassName="empty-list-message">Empty</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
)}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
{/* List 2 */}
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="list-wrapper">
<JSX.IntrinsicElements.h2: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>h2 React.HTMLAttributes<HTMLHeadingElement>.id?: string | undefinedid="list2-item-heading">List 2</JSX.IntrinsicElements.h2: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>h2>
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul
React.RefAttributes<HTMLUListElement>.ref?: React.LegacyRef<HTMLUListElement> | undefinedAllows getting a ref to the component instance.
Once the component unmounts, React will set `ref.current` to `null`
(or call the ref with `null` if you passed a callback ref).ref={const list2Ref: React.RefObject<HTMLUListElement>list2Ref} // Assign ref from hook
React.HTMLAttributes<T>.className?: string | undefinedclassName="list"
React.HTMLAttributes<HTMLUListElement>.tabIndex?: number | undefinedtabIndex={0}
React.HTMLAttributes<HTMLUListElement>.role?: React.AriaRole | undefinedrole="listbox"
React.AriaAttributes["aria-labelledby"]?: string | undefinedIdentifies the element (or elements) that labels the current element.aria-labelledby="list2-item-heading"
React.AriaAttributes["aria-activedescendant"]?: string | undefinedIdentifies the currently active element when DOM focus is on a composite widget, textbox, group, or application.aria-activedescendant={
const focusedListIndex: 0 | 1 | nullfocusedListIndex === 1 ? const focusedItemId: string | undefinedfocusedItemId : var undefinedundefined
}
React.DOMAttributes<HTMLUListElement>.onFocus?: React.FocusEventHandler<HTMLUListElement> | undefinedonFocus={() => const handleListFocus: (listIndex: 0 | 1) => voidhandleListFocus(1)}
React.DOMAttributes<HTMLUListElement>.onBlur?: React.FocusEventHandler<HTMLUListElement> | undefinedonBlur={const handleListBlur: () => voidhandleListBlur}
React.DOMAttributes<HTMLUListElement>.onKeyDown?: React.KeyboardEventHandler<HTMLUListElement> | undefinedonKeyDown={(e: React.KeyboardEvent<HTMLUListElement>e) => const handleListKeydown: (event: React.KeyboardEvent<HTMLUListElement>, listIndex: 0 | 1) => voidhandleListKeydown(e: React.KeyboardEvent<HTMLUListElement>e, 1)}
>
{const items2: string[]items2.Array<string>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.length > 0 ? (
const items2: string[]items2.Array<string>.map<React.JSX.Element>(callbackfn: (value: string, index: number, array: string[]) => React.JSX.Element, thisArg?: any): React.JSX.Element[]Calls a defined callback function on each element of an array, and returns an array that contains the results.map((item: stringitem, index: numberindex) => {
const const isFocused: booleanisFocused =
const focusedListIndex: 0 | 1 | nullfocusedListIndex === 1 && const focusedItemIndex: number | nullfocusedItemIndex === index: numberindex;
const const isSelected: booleanisSelected =
const selectedItem: SelectedItem | nullselectedItem?.SelectedItem.listIndex: 0 | 1 | undefinedlistIndex === 1 &&
const selectedItem: SelectedItemselectedItem?.SelectedItem.itemIndex: numberitemIndex === index: numberindex;
const const itemId: stringitemId = `list2-item-${item: stringitem}`;
return (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li
React.Attributes.key?: React.Key | null | undefinedkey={const itemId: stringitemId}
React.HTMLAttributes<T>.id?: string | undefinedid={const itemId: stringitemId}
React.HTMLAttributes<T>.className?: string | undefinedclassName={`item ${const isFocused: booleanisFocused ? "item-focused" : ""} ${
const isSelected: booleanisSelected ? "item-selected" : ""
}`}
React.HTMLAttributes<T>.role?: React.AriaRole | undefinedrole="option"
React.AriaAttributes["aria-selected"]?: Booleanish | undefinedIndicates the current "selected" state of various widgets.aria-selected={const isFocused: booleanisFocused}
React.HTMLAttributes<T>.tabIndex?: number | undefinedtabIndex={-1}
>
{item: stringitem}
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
);
})
) : (
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li React.HTMLAttributes<T>.className?: string | undefinedclassName="empty-list-message">Empty</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
)}
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
<JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div React.HTMLAttributes<HTMLDivElement>.className?: string | undefinedclassName="instructions">
<JSX.IntrinsicElements.h3: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>h3>Keyboard Instructions</JSX.IntrinsicElements.h3: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>h3>
<JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
Use <JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd>Tab</JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd> or <JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd>Shift+Tab</JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd> to focus a list.
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
Use <JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd>↑</JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd> / <JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd>↓</JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd> arrows to navigate items within
the focused list.
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
Press <JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd>Spacebar</JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd> to select/deselect the highlighted item
for moving.
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
With an item selected, use <JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd>↑</JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd> / <JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd>↓</JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd> (within the
same or another focused list) to choose the drop position.
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
Press <JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd>Enter</JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd> to drop the selected item at the
highlighted position.
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
<JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
Press <JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd>Escape</JSX.IntrinsicElements.kbd: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>kbd> to cancel a selection or leave the list.
</JSX.IntrinsicElements.li: React.DetailedHTMLProps<React.LiHTMLAttributes<HTMLLIElement>, HTMLLIElement>li>
</JSX.IntrinsicElements.ul: React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>ul>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
{/* Styles assumed to be global or handled via CSS Modules/Tailwind etc. */}
</JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>div>
);
}
List 1
- Apples
- Bananas
- Oranges
- Grapes
List 2
- Milk
- Cheese
- Yogurt
Keyboard Instructions
- Use Tab or Shift+Tab to focus a list.
- Use ↑ / ↓ arrows to navigate items within the focused list.
- Press Spacebar to select/deselect the highlighted item for moving.
- With an item selected, use ↑ / ↓ (within the same or another focused list) to choose the drop position.
- Press Enter to drop the selected item at the highlighted position.
- Press Escape to cancel a selection or leave the list.
Support Us
FormKit Drag and Drop is made with love by the FormKit team — the creators of open-source projects such as:
- FormKit - The open-source form framework for Vue.
- AutoAnimate - Add motion to your apps with a single line of code.
- Tempo - The easiest way to work with dates in JavaScript.
- ArrowJS - Reactivity without the Framework.
If you find our projects useful and would like to support their ongoing development, please consider sponsoring us on GitHub!
