Drag and drop
An extension of @hello-pangea/dnd (which is an actively maintained fork of react-beautiful-dnd) with a compatible API and built-in style opinions. Functionality results from 3 components working together:
<EuiDragDropContext />
: Section of your application containing the draggable elements and the drop targets.<EuiDroppable />
: Area into which items can be dropped. Contains one or more<EuiDraggable />
.<EuiDraggable />
: Items that can be dragged. Must be part of an<EuiDroppable />
Drag and drop interfaces are not well-adapted to many cases, and may be less suitable than other form types for data operations. For instance, drag and drop interaction relies heavily on spatial orientation that may not be entirely valid to all users (e.g., screen readers as the sole source of information). Similarly, users navigating by keyboard may not be afforded nuanced, dual-axis drag item manipulation.
EUI (largely due to the great work already in @hello-pangea/dnd) has and will continue to ensure accessibility where possible. With that in mind, keep your users' working context in mind.
Just the facts
EuiDraggable makes very few assumptions about what content it contains. To give affordance to draggable elements and to ensure a consistent experience, child elements must be able to accept a border and drop shadow (automatically applied via CSS). No other style opinions are applied, however.
Similarly, EuiDroppable must accept a background color overlay (automatically applied via CSS), but has no other restrictions.
All EuiDragDropContext elements are discrete and isolated; EuiDroppables and EuiDraggables cannot be shared/transferred between instances. Also, EuiDragDropContexts cannot be nested. It is recommended that a single, high-level EuiDragDropContext is used and EuiDroppables account for categorical and functional separation (see later examples).
EuiDragDropContext handles all events but makes no assumptions about the result of a drop event. As such, the following event handlers are available:
onBeforeDragStart
onDragStart
onDragUpdate
onDragEnd
(required)
EUI also provides methods for helping to deal to common action types:
reorder
: change an item's location in a droppable areacopy
: create a duplicate of an item in a different droppable areamove
: move an item to a differnt droppable area
Simple item reorder
The simplest case, demonstrating a single EuiDroppable with reorder
behavior.
Notice the ability to change rendered content based on dragging state. EuiDraggable children
is a render prop that mush return a ReactElement
. The snapshot
parameter on that function has state data that can be used to alter appearance or behavior (e.g., isDragging
).
Custom drag handle
By default the entire element surface can initiate a drag. To specify an element within as the handle and create a containing group, set customDragHandle=true
and hasInteractiveChildren=true
on the EuiDraggable.
The provided
parameter on the EuiDraggable children
render prop has all data required for functionality. Along with the customDragHandle
flag,provided.dragHandleProps
needs to be added to the intended handle element.
Icon-only custom drag handles require an accessible label. Add an aria-label="Drag handle"
attribute to your React component or HTML element that receivesprovided.dragHandleProps
.
Interactive elements
EuiDraggable can contain interactive elements such as buttons and form fields. Interactive elements require customDragHandle=true
and hasInteractiveChildren=true
on the EuiDraggable. These props will maintain drag functionality and accessibility, while enabling click, keypress, etc., events on the interactive child elements.
Move between lists
By default, all EuiDroppable elements are of the same type and will accept EuiDraggable elements from others in the same EuiDragDropContext.
The EUI move
method is demonstrated in this example.
Distinguish droppable areas by type
Setting the type
prop on an EuiDroppable element will ensure that it will only accept EuiDraggable elements from the same type of EuiDroppable.
Notice that the enabled, compatible EuiDroppable elements have a visual change that indicates they can accept the actively moving/focused EuiDraggable element.
Copyable items
For cases where collections of EuiDraggable elements are static or can be used in multiple places set cloneDraggables=true
on the parent EuiDroppable. The EuiDroppable becomes disabled (does not accept new EuiDraggable elements) in this scenario to avoid mixed content intentions.
The EUI copy
method is available and demonstrated in the example below. Note that the data point used asdraggableId
in EuiDraggable must change to allow for real duplication.
isRemovable
is used in the example for cloned items. This API is likely to change, but currently provides the visual changes with drop-to-remove interactions.
Portalled items
EuiDraggables use fixed positioning to render and animate the item being dragged. This positioning logic does not work as expected when used inside of containers that have their own stacking context.
To ensure dragging works as expected inside e.g. EuiFlyout, EuiModal or EuiPopover, use the prop usePortal
on EuiDraggable components. This will render the currently dragged element inside a portal appended to the document body (or wherever EuiPortal is configured to insert
to by default).
If the styling of the draggable content is scoped to a parent component, the styling won't be applied while dragging it when using usePortal
. This is due to the portalled position in the DOM, which changes previous hierarchical relations to other ancestor elements. To prevent this from happening, we recommend applying styling from within the EuiDraggable scope without any parent selectors.
Kitchen sink
EuiDraggables in EuiDroppables, EuiDroppables in EuiDraggables, custom drag handles, horizontal movement, vertical movement, flexbox, panel inception, you name it.
Props
EuiDragDropContext
Prop | Description and type | Default value |
---|---|---|
Prop dragHandleUsageInstructions# | Description and type Type: string | Default value |
Prop enableDefaultSensors# | Description and type Type: boolean | Default value |
Prop nonce# | Description and type Type: string | Default value |
Prop sensors# | Description and type Type: Sensor[] | Default value |
Prop autoScrollerOptions# | Description and type Customize auto scroller RecursivePartial<AutoScrollerOptions> | Default value |
EuiDraggable
Prop | Description and type | Default value |
---|---|---|
Prop children# | Description and type ReactNode to render as this component's content ReactElement<any, string | JSXElementConstructor<any>> | DraggableChildrenFn | Default value Required |
Prop index# | Description and type Type: number | Default value Required |
Prop draggableId# | Description and type Type: string | Default value Required |
Prop className# | Description and type Type: string | Default value |
Prop customDragHandle# | Description and type Whether the boolean | "custom" | Default value false |
Prop hasInteractiveChildren# | Description and type Whether the container has interactive children and should have boolean | Default value false |
Prop isRemovable# | Description and type Whether the item is currently in a position to be removed boolean | Default value false |
Prop usePortal# | Description and type Whether the currently dragged item is cloned into a portal in the body. This settings will Make sure to apply styles directly to the Draggable content as relative styling from an outside boolean | Default value false |
Prop spacing# | Description and type Adds padding to the draggable item "s" | "m" | "l" | "none" | Default value none |
Prop style# | Description and type Type: CSSProperties | Default value |
Prop aria-label# | Description and type Type: string | Default value |
Prop data-test-subj# | Description and type Type: string | Default value |
Prop css# | Description and type Type: Interpolation<Theme> | Default value |
Prop isDragDisabled# | Description and type Type: boolean | Default value false |
Prop disableInteractiveElementBlocking# | Description and type Type: boolean | Default value |
Prop shouldRespectForcePress# | Description and type Type: boolean | Default value |
EuiDroppable
Prop | Description and type | Default value |
---|---|---|
Prop children# | Description and type ReactNode to render as this component's content ReactElement<any, string | JSXElementConstructor<any>> | ReactElement<any, string | JSXElementConstructor<any>>[] | ((provided: DroppableProvided, snapshot: DroppableStateSnapshot) => ReactNode) | Default value Required |
Prop droppableId# | Description and type Type: string | Default value Required |
Prop className# | Description and type Type: string | Default value |
Prop cloneDraggables# | Description and type Makes its items immutable. Dragging creates cloned items that can be dropped elsewhere. boolean | Default value false |
Prop style# | Description and type Type: CSSProperties | Default value |
Prop spacing# | Description and type Adds padding to the droppable area "s" | "m" | "l" | "none" | Default value none |
Prop withPanel# | Description and type Adds an EuiPanel style to the droppable area boolean | Default value false |
Prop grow# | Description and type Allow the panel to flex-grow? boolean | Default value false |
Prop aria-label# | Description and type Type: string | Default value |
Prop data-test-subj# | Description and type Type: string | Default value |
Prop css# | Description and type Type: Interpolation<Theme> | Default value |
Prop renderClone# | Description and type Type: DraggableChildrenFn | Default value |