diff --git a/CHANGELOG.md b/CHANGELOG.md
index 531dd9e07c8..e8de795c100 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,7 @@
## [`master`](https://github.com/elastic/eui/tree/master)
- **[Beta]** Added new `EuiSelectable` component ([#1699](https://github.com/elastic/eui/pull/1699))
+- **[Beta]** Added new drag and drop components: `EuiDragDropContext`, `EuiDraggable`, and `EuiDroppable` ([#1733](https://github.com/elastic/eui/pull/1733))
## [`9.7.2`](https://github.com/elastic/eui/tree/v9.7.2)
diff --git a/package.json b/package.json
index f34dd723398..02dd222cc41 100644
--- a/package.json
+++ b/package.json
@@ -47,6 +47,7 @@
"dependencies": {
"@types/lodash": "^4.14.116",
"@types/numeral": "^0.0.25",
+ "@types/react-beautiful-dnd": "^10.1.0",
"classnames": "^2.2.5",
"core-js": "^2.5.1",
"highlight.js": "^9.12.0",
@@ -56,6 +57,7 @@
"numeral": "^2.0.6",
"prop-types": "^15.6.0",
"react-ace": "^5.5.0",
+ "react-beautiful-dnd": "^10.1.0",
"react-color": "^2.13.8",
"react-focus-lock": "^1.17.7",
"react-input-autosize": "^2.2.1",
diff --git a/src-docs/src/components/guide_section/guide_section.js b/src-docs/src/components/guide_section/guide_section.js
index cd312f15609..b61815f4bf4 100644
--- a/src-docs/src/components/guide_section/guide_section.js
+++ b/src-docs/src/components/guide_section/guide_section.js
@@ -184,7 +184,7 @@ export class GuideSection extends Component {
const rows = propNames.map(propName => {
const {
- description: propDescription,
+ description: propDescription = '',
required,
defaultValue,
type,
diff --git a/src-docs/src/routes.js b/src-docs/src/routes.js
index ffaeb225c1e..66d1b11a379 100644
--- a/src-docs/src/routes.js
+++ b/src-docs/src/routes.js
@@ -108,6 +108,9 @@ import { DelayHideExample }
import { DescriptionListExample }
from './views/description_list/description_list_example';
+import { DragAndDropExample }
+ from './views/drag_and_drop/drag_and_drop_example';
+
import { EmptyPromptExample }
from './views/empty_prompt/empty_prompt_example';
@@ -388,6 +391,7 @@ const navigation = [{
CardExample,
CodeExample,
DescriptionListExample,
+ DragAndDropExample,
EmptyPromptExample,
HealthExample,
IconExample,
diff --git a/src-docs/src/views/drag_and_drop/drag_and_drop.js b/src-docs/src/views/drag_and_drop/drag_and_drop.js
new file mode 100644
index 00000000000..172175cb9ee
--- /dev/null
+++ b/src-docs/src/views/drag_and_drop/drag_and_drop.js
@@ -0,0 +1,41 @@
+import React, { useState } from 'react';
+import {
+ EuiDragDropContext,
+ EuiDraggable,
+ EuiDroppable,
+ EuiPanel
+} from '../../../../src/components';
+
+import { reorder } from '../../../../src/components/drag_and_drop';
+
+import { makeList } from './helper';
+
+export default () => {
+ const [list, setList] = useState(makeList(3));
+ const onDragEnd = ({ source, destination }) => {
+ if (source && destination) {
+ const items = reorder(
+ list,
+ source.index,
+ destination.index
+ );
+
+ setList(items);
+ }
+ };
+ return (
+
+
+ {list.map(({ content, id }, idx) => (
+
+ {(provided, state) => (
+
+ {content}{state.isDragging && ' ✨'}
+
+ )}
+
+ ))}
+
+
+ );
+};
diff --git a/src-docs/src/views/drag_and_drop/drag_and_drop_bare.js b/src-docs/src/views/drag_and_drop/drag_and_drop_bare.js
new file mode 100644
index 00000000000..858cf3ac3c2
--- /dev/null
+++ b/src-docs/src/views/drag_and_drop/drag_and_drop_bare.js
@@ -0,0 +1,30 @@
+import React, { useState } from 'react';
+import {
+ EuiDragDropContext,
+ EuiDraggable,
+ EuiDroppable
+} from '../../../../src/components';
+
+import { makeList } from './helper';
+
+export default () => {
+ const [list] = useState(makeList(3));
+ const onDragEnd = ({ source, destination }) => {
+ console.log(source, destination);
+ };
+ return (
+
+
+ {list.map(({ content, id }, idx) => (
+
+ {() => (
+
+ {content}
+
+ )}
+
+ ))}
+
+
+ );
+};
diff --git a/src-docs/src/views/drag_and_drop/drag_and_drop_clone.js b/src-docs/src/views/drag_and_drop/drag_and_drop_clone.js
new file mode 100644
index 00000000000..771b85c5c04
--- /dev/null
+++ b/src-docs/src/views/drag_and_drop/drag_and_drop_clone.js
@@ -0,0 +1,119 @@
+import React, { useState } from 'react';
+import {
+ EuiButtonIcon,
+ EuiDragDropContext,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiDraggable,
+ EuiDroppable,
+ EuiIcon,
+ EuiPanel
+} from '../../../../src/components';
+
+import {
+ copy,
+ reorder
+} from '../../../../src/components/drag_and_drop';
+
+import { makeId, makeList } from './helper';
+
+export default () => {
+ const [isItemRemovable, setIsItemRemovable] = useState(false);
+ const [list1, setList1] = useState(makeList(3));
+ const [list2, setList2] = useState([]);
+ const lists = { DROPPABLE_AREA_COPY_1: list1, DROPPABLE_AREA_COPY_2: list2 };
+ const actions = { DROPPABLE_AREA_COPY_1: setList1, DROPPABLE_AREA_COPY_2: setList2 };
+ const remove = (droppableId, index) => {
+ const list = Array.from(lists[droppableId]);
+ list.splice(index, 1);
+
+ actions[droppableId](list);
+ };
+ const onDragUpdate = ({ source, destination }) => {
+ const shouldRemove = !destination && source.droppableId === 'DROPPABLE_AREA_COPY_2';
+ setIsItemRemovable(shouldRemove);
+ };
+ const onDragEnd = ({ source, destination }) => {
+ if (source && destination) {
+ if (source.droppableId === destination.droppableId) {
+ const items = reorder(
+ lists[destination.droppableId],
+ source.index,
+ destination.index
+ );
+
+ actions[destination.droppableId](items);
+ } else {
+ const sourceId = source.droppableId;
+ const destinationId = destination.droppableId;
+ const result = copy(
+ lists[sourceId],
+ lists[destinationId],
+ source,
+ destination,
+ {
+ property: 'id',
+ modifier: makeId
+ }
+ );
+
+ actions[sourceId](result[sourceId]);
+ actions[destinationId](result[destinationId]);
+ }
+ } else if (!destination && source.droppableId === 'DROPPABLE_AREA_COPY_2') {
+ remove(source.droppableId, source.index);
+ }
+ };
+ return (
+
+
+
+
+
+ {list1.map(({ content, id }, idx) => (
+
+
+ {content}
+
+
+ ))}
+
+
+
+
+
+
+ {list2.length ?
+ (
+ list2.map(({ content, id }, idx) => (
+
+
+
+ {content}
+
+ {isItemRemovable ? (
+
+ ) : (
+ remove('DROPPABLE_AREA_COPY_2', idx)}
+ />
+ )}
+
+
+
+
+ ))
+ ) : (
+
+ Drop Items Here
+
+ )}
+
+
+
+
+
+ );
+};
diff --git a/src-docs/src/views/drag_and_drop/drag_and_drop_complex.js b/src-docs/src/views/drag_and_drop/drag_and_drop_complex.js
new file mode 100644
index 00000000000..5d1a409bf75
--- /dev/null
+++ b/src-docs/src/views/drag_and_drop/drag_and_drop_complex.js
@@ -0,0 +1,90 @@
+import React, { useState } from 'react';
+import {
+ EuiDragDropContext,
+ EuiDraggable,
+ EuiDroppable,
+ EuiButtonIcon,
+ EuiPanel
+} from '../../../../src/components';
+
+import { move, reorder } from '../../../../src/components/drag_and_drop';
+
+import { makeList } from './helper';
+
+export default () => {
+ const [list, setList] = useState([1, 2]);
+ const [list1, setList1] = useState(makeList(3));
+ const [list2, setList2] = useState(makeList(3, 4));
+ const lists = { COMPLEX_DROPPABLE_PARENT: list, COMPLEX_DROPPABLE_AREA_1: list1, COMPLEX_DROPPABLE_AREA_2: list2 };
+ const actions = { COMPLEX_DROPPABLE_PARENT: setList, COMPLEX_DROPPABLE_AREA_1: setList1, COMPLEX_DROPPABLE_AREA_2: setList2 };
+ const onDragEnd = ({ source, destination }) => {
+ if (source && destination) {
+ if (source.droppableId === destination.droppableId) {
+ const items = reorder(
+ lists[destination.droppableId],
+ source.index,
+ destination.index
+ );
+
+ actions[destination.droppableId](items);
+ } else {
+ const sourceId = source.droppableId;
+ const destinationId = destination.droppableId;
+ const result = move(
+ lists[sourceId],
+ lists[destinationId],
+ source,
+ destination
+ );
+
+ actions[sourceId](result[sourceId]);
+ actions[destinationId](result[destinationId]);
+ }
+
+ }
+ };
+ return (
+
+
+
+ {list.map((did, didx) => (
+
+ {(provided) => (
+
+
+
+ {lists[`COMPLEX_DROPPABLE_AREA_${did}`].map(({ content, id }, idx) => (
+
+
+ {content}
+
+
+ ))}
+
+
+ )}
+
+ ))}
+
+
+
+ );
+};
diff --git a/src-docs/src/views/drag_and_drop/drag_and_drop_custom_handle.js b/src-docs/src/views/drag_and_drop/drag_and_drop_custom_handle.js
new file mode 100644
index 00000000000..d0086cf55ad
--- /dev/null
+++ b/src-docs/src/views/drag_and_drop/drag_and_drop_custom_handle.js
@@ -0,0 +1,53 @@
+import React, { useState } from 'react';
+import {
+ EuiDragDropContext,
+ EuiDraggable,
+ EuiDroppable,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiIcon,
+ EuiPanel
+} from '../../../../src/components';
+
+import { reorder } from '../../../../src/components/drag_and_drop';
+
+import { makeList } from './helper';
+
+export default () => {
+ const [list, setList] = useState(makeList(3));
+ const onDragEnd = ({ source, destination }) => {
+ if (source && destination) {
+ const items = reorder(
+ list,
+ source.index,
+ destination.index
+ );
+
+ setList(items);
+ }
+ };
+ return (
+
+
+ {list.map(({ content, id }, idx) => (
+
+ {(provided) => (
+
+
+
+
+
+
+
+
+ {content}
+
+
+
+ )}
+
+ ))}
+
+
+ );
+};
diff --git a/src-docs/src/views/drag_and_drop/drag_and_drop_disable_blocking.js b/src-docs/src/views/drag_and_drop/drag_and_drop_disable_blocking.js
new file mode 100644
index 00000000000..0fd54c5ed7e
--- /dev/null
+++ b/src-docs/src/views/drag_and_drop/drag_and_drop_disable_blocking.js
@@ -0,0 +1,39 @@
+import React, { useState } from 'react';
+import {
+ EuiButton,
+ EuiDragDropContext,
+ EuiDraggable,
+ EuiDroppable
+} from '../../../../src/components';
+
+import { reorder } from '../../../../src/components/drag_and_drop';
+
+import { makeList } from './helper';
+
+export default () => {
+ const [list, setList] = useState(makeList(3));
+ const onDragEnd = ({ source, destination }) => {
+ if (source && destination) {
+ const items = reorder(
+ list,
+ source.index,
+ destination.index
+ );
+
+ setList(items);
+ }
+ };
+ return (
+
+
+ {list.map(({ content, id }, idx) => (
+
+ alert(`${content} clicked!`)}>
+ {content}
+
+
+ ))}
+
+
+ );
+};
diff --git a/src-docs/src/views/drag_and_drop/drag_and_drop_example.js b/src-docs/src/views/drag_and_drop/drag_and_drop_example.js
new file mode 100644
index 00000000000..bef1d0ddb6e
--- /dev/null
+++ b/src-docs/src/views/drag_and_drop/drag_and_drop_example.js
@@ -0,0 +1,332 @@
+import React from 'react';
+import { GuideSectionTypes } from '../../components';
+import {
+ EuiCallOut,
+ EuiCode,
+ EuiDragDropContext,
+ EuiDraggable,
+ EuiDroppable,
+ EuiLink,
+ EuiSpacer,
+ EuiText
+} from '../../../../src/components';
+import { renderToHtml } from '../../services';
+
+import DragAndDropBare from './drag_and_drop_bare';
+const dragAndDropBareSource = require('!!raw-loader!./drag_and_drop_bare');
+const dragAndDropBareHtml = renderToHtml(DragAndDropBare);
+
+import DragAndDrop from './drag_and_drop';
+const dragAndDropSource = require('!!raw-loader!./drag_and_drop');
+const dragAndDropHtml = renderToHtml(DragAndDrop);
+
+import DragAndDropCustomHandle from './drag_and_drop_custom_handle';
+const dragAndDropCustomHandleSource = require('!!raw-loader!./drag_and_drop_custom_handle');
+const dragAndDropCustomHandleHtml = renderToHtml(DragAndDropCustomHandle);
+
+import DragAndDropDisableBlocking from './drag_and_drop_disable_blocking';
+const dragAndDropDisableBlockingSource = require('!!raw-loader!./drag_and_drop_disable_blocking');
+const dragAndDropDisableBlockingHtml = renderToHtml(DragAndDropDisableBlocking);
+
+import DragAndDropMoveLists from './drag_and_drop_move_lists';
+const dragAndDropMoveListsSource = require('!!raw-loader!./drag_and_drop_move_lists');
+const dragAndDropMoveListsHtml = renderToHtml(DragAndDropMoveLists);
+
+import DragAndDropTypes from './drag_and_drop_types';
+const dragAndDropTypesSource = require('!!raw-loader!./drag_and_drop_types');
+const dragAndDropTypesHtml = renderToHtml(DragAndDropTypes);
+
+import DragAndDropClone from './drag_and_drop_clone';
+const dragAndDropCloneSource = require('!!raw-loader!./drag_and_drop_clone');
+const dragAndDropCloneHtml = renderToHtml(DragAndDropClone);
+
+import DragAndDropComplex from './drag_and_drop_complex';
+const dragAndDropComplexSource = require('!!raw-loader!./drag_and_drop_complex');
+const dragAndDropComplexHtml = renderToHtml(DragAndDropComplex);
+
+export const DragAndDropExample = {
+ title: 'Drag And Drop',
+ beta: true,
+ intro: (
+
+
+
+ An extension of react-beautiful-dnd with a compatible API and built-in style opinions.
+ Functionality results from 3 components working together:
+
+
+ -
+ {``}:
+ Section of your application containing the draggable elements and the drop targets.
+
+ -
+ {``}: Area into which items can be dropped.
+ Contains {``}s.
+
+ -
+ {``}: Items that can be dragged.
+ Must be part of an {``}
+
+
+
+
+
+
+
+
+ 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 entirelty 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 react-beautiful-dnd) has and will continue to ensure accessibility where possible.
+ With that in mind, keep your users' working context in mind.`}
+
+
+
+
+
+ ),
+ sections: [
+ {
+ title: 'Just the facts',
+ source: [
+ {
+ type: GuideSectionTypes.JS,
+ code: dragAndDropBareSource
+ },
+ {
+ type: GuideSectionTypes.HTML,
+ code: dragAndDropBareHtml
+ }
+ ],
+ text: (
+
+
+ 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 eventing 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 area`}
+ - copy: create a duplicate of an item in a different droppable area
+ - move: move an item to a differnt droppable area
+
+
+ ),
+ props: { EuiDragDropContext, EuiDraggable, EuiDroppable },
+ demo:
+ },
+ {
+ title: 'Simple item reorder',
+ source: [
+ {
+ type: GuideSectionTypes.JS,
+ code: dragAndDropSource
+ },
+ {
+ type: GuideSectionTypes.HTML,
+ code: dragAndDropHtml
+ }
+ ],
+ text: (
+
+
+ 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).
+
+
+ ),
+ props: { EuiDragDropContext, EuiDraggable, EuiDroppable },
+ demo:
+ },
+ {
+ title: 'Custom drag handle',
+ source: [
+ {
+ type: GuideSectionTypes.JS,
+ code: dragAndDropCustomHandleSource
+ },
+ {
+ type: GuideSectionTypes.HTML,
+ code: dragAndDropCustomHandleHtml
+ }
+ ],
+ text: (
+
+
+ By default the entire element surface can initiate a drag.
+ To specify a certain element within as the handle, set
+ customDragHandle=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.
+
+
+ ),
+ demo:
+ },
+ {
+ title: 'Interactive elements',
+ source: [
+ {
+ type: GuideSectionTypes.JS,
+ code: dragAndDropDisableBlockingSource
+ },
+ {
+ type: GuideSectionTypes.HTML,
+ code: dragAndDropDisableBlockingHtml
+ }
+ ],
+ text: (
+
+
+ EuiDraggable elements can contain interactive elements such as buttons and form fields by adding the
+ disableInteractiveElementBlocking prop. This will keep drag functionality while also enabling click, etc.,
+ events on the interactive child elements.
+
+
+ ),
+ demo:
+ },
+ {
+ title: 'Move between lists',
+ source: [
+ {
+ type: GuideSectionTypes.JS,
+ code: dragAndDropMoveListsSource
+ },
+ {
+ type: GuideSectionTypes.HTML,
+ code: dragAndDropMoveListsHtml
+ }
+ ],
+ text: (
+
+
+ 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.
+
+
+ ),
+ demo:
+ },
+ {
+ title: 'Distinguish droppable areas by type',
+ source: [
+ {
+ type: GuideSectionTypes.JS,
+ code: dragAndDropTypesSource
+ },
+ {
+ type: GuideSectionTypes.HTML,
+ code: dragAndDropTypesHtml
+ }
+ ],
+ text: (
+
+
+ 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.
+
+
+ ),
+ demo:
+ },
+ {
+ title: 'Copyable items',
+ source: [
+ {
+ type: GuideSectionTypes.JS,
+ code: dragAndDropCloneSource
+ },
+ {
+ type: GuideSectionTypes.HTML,
+ code: dragAndDropCloneHtml
+ }
+ ],
+ text: (
+
+
+ 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 as
+ draggableId 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.
+
+
+ ),
+ demo:
+ }, {
+ title: 'We have fun',
+ source: [
+ {
+ type: GuideSectionTypes.JS,
+ code: dragAndDropComplexSource
+ },
+ {
+ type: GuideSectionTypes.HTML,
+ code: dragAndDropComplexHtml
+ }
+ ],
+ text: (
+
+
+ EuiDraggables in EuiDroppables, EuiDroppables in
+ EuiDraggables, custom drag handles, horizontal movement, vertical movement, flexbox,
+ EuiPanel Inception, you name it.
+
+
+ ),
+ demo:
+ }
+ ]
+};
diff --git a/src-docs/src/views/drag_and_drop/drag_and_drop_move_lists.js b/src-docs/src/views/drag_and_drop/drag_and_drop_move_lists.js
new file mode 100644
index 00000000000..65f427501d3
--- /dev/null
+++ b/src-docs/src/views/drag_and_drop/drag_and_drop_move_lists.js
@@ -0,0 +1,98 @@
+import React, { useState } from 'react';
+import {
+ EuiDragDropContext,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiDraggable,
+ EuiDroppable,
+ EuiIcon,
+ EuiPanel
+} from '../../../../src/components';
+
+import {
+ move,
+ reorder
+} from '../../../../src/components/drag_and_drop';
+
+import { makeList } from './helper';
+
+export default () => {
+ const [list1, setList1] = useState(makeList(3));
+ const [list2, setList2] = useState(makeList(3, 4));
+ const onDragEnd = ({ source, destination }) => {
+ const lists = { DROPPABLE_AREA_1: list1, DROPPABLE_AREA_2: list2 };
+ const actions = { DROPPABLE_AREA_1: setList1, DROPPABLE_AREA_2: setList2 };
+ if (source && destination) {
+ if (source.droppableId === destination.droppableId) {
+ const items = reorder(
+ lists[destination.droppableId],
+ source.index,
+ destination.index
+ );
+
+ actions[destination.droppableId](items);
+ } else {
+ const sourceId = source.droppableId;
+ const destinationId = destination.droppableId;
+ const result = move(
+ lists[sourceId],
+ lists[destinationId],
+ source,
+ destination
+ );
+
+ actions[sourceId](result[sourceId]);
+ actions[destinationId](result[destinationId]);
+ }
+
+ }
+ };
+ return (
+
+
+
+
+
+ {list1.length > 0 ? (
+ list1.map(({ content, id }, idx) => (
+
+ {(provided, state) => (
+
+ {content}{state.isDragging && ' ✨'}
+
+ )}
+
+ ))
+ ) : (
+
+
+
+ )}
+
+
+
+
+
+
+ {list2.length > 0 ? (
+ list2.map(({ content, id }, idx) => (
+
+ {(provided, state) => (
+
+ {content}{state.isDragging && ' ✨'}
+
+ )}
+
+ ))
+ ) : (
+
+
+
+ )}
+
+
+
+
+
+ );
+};
diff --git a/src-docs/src/views/drag_and_drop/drag_and_drop_types.js b/src-docs/src/views/drag_and_drop/drag_and_drop_types.js
new file mode 100644
index 00000000000..ddbfedcd0e6
--- /dev/null
+++ b/src-docs/src/views/drag_and_drop/drag_and_drop_types.js
@@ -0,0 +1,101 @@
+import React, { useState } from 'react';
+import {
+ EuiDragDropContext,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiDraggable,
+ EuiDroppable,
+ EuiPanel
+} from '../../../../src/components';
+
+import {
+ move,
+ reorder
+} from '../../../../src/components/drag_and_drop';
+
+import { makeList } from './helper';
+
+export default () => {
+ const [list1, setList1] = useState(makeList(3));
+ const [list2, setList2] = useState(makeList(3, 4));
+ const [list3, setList3] = useState(makeList(3, 7));
+ const onDragEnd = ({ source, destination }) => {
+ const lists = { DROPPABLE_AREA_TYPE_1: list1, DROPPABLE_AREA_TYPE_2: list2, DROPPABLE_AREA_TYPE_3: list3 };
+ const actions = { DROPPABLE_AREA_TYPE_1: setList1, DROPPABLE_AREA_TYPE_2: setList2, DROPPABLE_AREA_TYPE_3: setList3 };
+ if (source && destination) {
+ if (source.droppableId === destination.droppableId) {
+ const items = reorder(
+ lists[destination.droppableId],
+ source.index,
+ destination.index
+ );
+
+ actions[destination.droppableId](items);
+ } else {
+ const sourceId = source.droppableId;
+ const destinationId = destination.droppableId;
+ const result = move(
+ lists[sourceId],
+ lists[destinationId],
+ source,
+ destination
+ );
+
+ actions[sourceId](result[sourceId]);
+ actions[destinationId](result[destinationId]);
+ }
+
+ }
+ };
+ return (
+
+
+
+
+
+ {list1.map(({ content, id }, idx) => (
+
+ {(provided, state) => (
+
+ {content}{state.isDragging && ' ✨'}
+
+ )}
+
+ ))}
+
+
+
+
+
+
+ {list2.map(({ content, id }, idx) => (
+
+ {(provided, state) => (
+
+ {content}{state.isDragging && ' ✨'}
+
+ )}
+
+ ))}
+
+
+
+
+
+
+ {list3.map(({ content, id }, idx) => (
+
+ {(provided, state) => (
+
+ {content}{state.isDragging && ' ✨'}
+
+ )}
+
+ ))}
+
+
+
+
+
+ );
+};
diff --git a/src-docs/src/views/drag_and_drop/helper.js b/src-docs/src/views/drag_and_drop/helper.js
new file mode 100644
index 00000000000..2915968e3c8
--- /dev/null
+++ b/src-docs/src/views/drag_and_drop/helper.js
@@ -0,0 +1,10 @@
+import { htmlIdGenerator } from '../../../../src/services';
+
+export const makeId = htmlIdGenerator();
+
+export const makeList = (number, start = 1) => Array.from({ length: number }, (v, k) => k + start).map(el => {
+ return {
+ content: `Item ${el}`,
+ id: makeId()
+ };
+});
diff --git a/src/components/drag_and_drop/__snapshots__/drag_drop_context.test.tsx.snap b/src/components/drag_and_drop/__snapshots__/drag_drop_context.test.tsx.snap
new file mode 100644
index 00000000000..0cebb4a6dc4
--- /dev/null
+++ b/src/components/drag_and_drop/__snapshots__/drag_drop_context.test.tsx.snap
@@ -0,0 +1,3 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`EuiDragDropContext is rendered 1`] = `
`;
diff --git a/src/components/drag_and_drop/__snapshots__/draggable.test.tsx.snap b/src/components/drag_and_drop/__snapshots__/draggable.test.tsx.snap
new file mode 100644
index 00000000000..6ca5b4cb5db
--- /dev/null
+++ b/src/components/drag_and_drop/__snapshots__/draggable.test.tsx.snap
@@ -0,0 +1,55 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`EuiDraggable can be given ReactElement children 1`] = `
+
+`;
+
+exports[`EuiDraggable is rendered 1`] = `
+
+`;
diff --git a/src/components/drag_and_drop/__snapshots__/droppable.test.tsx.snap b/src/components/drag_and_drop/__snapshots__/droppable.test.tsx.snap
new file mode 100644
index 00000000000..daf52206633
--- /dev/null
+++ b/src/components/drag_and_drop/__snapshots__/droppable.test.tsx.snap
@@ -0,0 +1,27 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`EuiDroppable can be given ReactElement children 1`] = `
+
+`;
+
+exports[`EuiDroppable is rendered 1`] = `
+
+`;
diff --git a/src/components/drag_and_drop/_draggable.scss b/src/components/drag_and_drop/_draggable.scss
new file mode 100644
index 00000000000..6f7481654c6
--- /dev/null
+++ b/src/components/drag_and_drop/_draggable.scss
@@ -0,0 +1,43 @@
+// sass-lint:disable no-empty-rulesets
+
+.euiDraggable {
+ &.euiDraggable--isDragging {
+ // Overriding inline styles on JS-inserted HTML elements
+ z-index: $euiZLevel9 !important; // sass-lint:disable-line no-important
+ }
+
+ &.euiDraggable--hasClone:not(.euiDraggable--isDragging) {
+ // Overriding inline styles on JS-inserted HTML elements
+ transform: none !important; // sass-lint:disable-line no-important
+ }
+
+ &.euiDraggable--withoutDropAnimation {
+ // Overriding inline styles on JS-inserted HTML elements
+ transition-duration: .001s !important; // sass-lint:disable-line no-important
+ }
+
+ &:focus > .euiDraggable__item,
+ &.euiDraggable--hasCustomDragHandle > .euiDraggable__item [data-react-beautiful-dnd-drag-handle]:focus {
+ @include euiFocusRing;
+ }
+
+ .euiDraggable__item {
+ &.euiDraggable__item--isDisabled {
+ cursor: not-allowed;
+ }
+
+ &.euiDraggable__item--isDragging {
+ // TODO: Resolve below
+ // Commenting this out for now,
+ // I'm thinking about adding an optional prop to auto-add these styles versus always having them
+ // @include euiBottomShadow;
+ // @include euiFocusRing;
+ }
+ }
+}
+
+@each $size, $spacing in $euiDragAndDropSpacing {
+ .euiDraggable--#{$size} {
+ padding: $spacing;
+ }
+}
diff --git a/src/components/drag_and_drop/_droppable.scss b/src/components/drag_and_drop/_droppable.scss
new file mode 100644
index 00000000000..48db048d5d6
--- /dev/null
+++ b/src/components/drag_and_drop/_droppable.scss
@@ -0,0 +1,37 @@
+@import '../panel/mixins';
+
+.euiDroppable {
+ $euiDroppableColor: $euiColorSecondary;
+ transition: background-color $euiAnimSpeedExtraSlow ease;
+
+ &.euiDroppable--isDraggingType:not(.euiDroppable--isDisabled) {
+ background-color: transparentize($euiDroppableColor, .9);
+
+ &.euiDroppable--isDraggingOver {
+ background-color: transparentize($euiDroppableColor, .75);
+ }
+ }
+
+ .euiDroppable__placeholder {
+ &.euiDroppable__placeholder--isHidden {
+ // Overriding inline styles on JS-inserted HTML elements
+ display: none !important; // sass-lint:disable-line no-important
+ }
+ }
+}
+
+@include euiPanel('euiDroppable--withPanel');
+
+.euiDroppable--noGrow {
+ flex-grow: 0;
+}
+
+.euiDroppable--grow {
+ flex-grow: 1;
+}
+
+@each $size, $spacing in $euiDragAndDropSpacing {
+ .euiDroppable--#{$size} {
+ padding: $spacing;
+ }
+}
diff --git a/src/components/drag_and_drop/_index.scss b/src/components/drag_and_drop/_index.scss
new file mode 100644
index 00000000000..e3ae587b279
--- /dev/null
+++ b/src/components/drag_and_drop/_index.scss
@@ -0,0 +1,3 @@
+@import 'variables';
+@import 'draggable';
+@import 'droppable';
diff --git a/src/components/drag_and_drop/_variables.scss b/src/components/drag_and_drop/_variables.scss
new file mode 100644
index 00000000000..65dc0d6a8b0
--- /dev/null
+++ b/src/components/drag_and_drop/_variables.scss
@@ -0,0 +1,5 @@
+$euiDragAndDropSpacing: (
+ s: ($euiSizeXS / 2),
+ m: ($euiSizeS / 2),
+ l: ($euiSize / 2),
+);
diff --git a/src/components/drag_and_drop/drag_drop_context.test.tsx b/src/components/drag_and_drop/drag_drop_context.test.tsx
new file mode 100644
index 00000000000..069a9005c6b
--- /dev/null
+++ b/src/components/drag_and_drop/drag_drop_context.test.tsx
@@ -0,0 +1,42 @@
+import React from 'react';
+import { render, mount } from 'enzyme';
+
+import { findTestSubject } from '../../test';
+import { requiredProps } from '../../test/required_props';
+
+import { EuiDragDropContext } from './';
+import { EuiDragDropContextContext } from './drag_drop_context';
+
+describe('EuiDragDropContext', () => {
+ test('is rendered', () => {
+ const handler = jest.fn();
+ const component = render(
+
+
+
+ );
+
+ expect(component).toMatchSnapshot();
+ });
+
+ describe('custom behavior', () => {
+ describe('isDraggingType', () => {
+ test('is set on proprietary context', () => {
+ const handler = jest.fn();
+ const component = mount(
+
+
+ {value => (
+
+ {value.hasOwnProperty('isDraggingType') ? 'true' : 'false'}
+
+ )}
+
+
+ );
+
+ expect(findTestSubject(component, 'child').text()).toBe('true');
+ });
+ });
+ });
+});
diff --git a/src/components/drag_and_drop/drag_drop_context.tsx b/src/components/drag_and_drop/drag_drop_context.tsx
new file mode 100644
index 00000000000..b76cb6d87e3
--- /dev/null
+++ b/src/components/drag_and_drop/drag_drop_context.tsx
@@ -0,0 +1,58 @@
+import React, { FunctionComponent, useState } from 'react';
+import {
+ DragDropContext,
+ DragDropContextProps,
+ DropResult,
+ DragStart,
+ ResponderProvided,
+} from 'react-beautiful-dnd';
+
+// export interface EuiDragDropContextProps extends DragDropContextProps {}
+
+export const EuiDragDropContextContext = React.createContext({
+ isDraggingType: null,
+});
+
+export const EuiDragDropContext: FunctionComponent = ({
+ onBeforeDragStart,
+ onDragStart,
+ onDragUpdate,
+ onDragEnd,
+ children,
+ ...rest
+}) => {
+ const [isDraggingType, setIsDraggingType] = useState();
+ const euiOnDragStart = (
+ start: DragStart,
+ provided: ResponderProvided
+ ): void => {
+ setIsDraggingType(start.type);
+ if (onDragStart) {
+ onDragStart(start, provided);
+ }
+ };
+ const euiOnDragEnd = (
+ result: DropResult,
+ provided: ResponderProvided
+ ): void => {
+ setIsDraggingType(null);
+ if (onDragEnd) {
+ onDragEnd(result, provided);
+ }
+ };
+ return (
+
+
+ {children}
+
+
+ );
+};
diff --git a/src/components/drag_and_drop/draggable.test.tsx b/src/components/drag_and_drop/draggable.test.tsx
new file mode 100644
index 00000000000..efc4e029657
--- /dev/null
+++ b/src/components/drag_and_drop/draggable.test.tsx
@@ -0,0 +1,39 @@
+import React from 'react';
+import { render } from 'enzyme';
+
+// import { findTestSubject } from '../../test';
+import { requiredProps } from '../../test/required_props';
+
+import { EuiDragDropContext, EuiDraggable, EuiDroppable } from './';
+
+describe('EuiDraggable', () => {
+ test('is rendered', () => {
+ const handler = jest.fn();
+ const component = render(
+
+
+
+ {() => Hello
}
+
+
+
+ );
+
+ expect(component).toMatchSnapshot();
+ });
+
+ test('can be given ReactElement children', () => {
+ const handler = jest.fn();
+ const component = render(
+
+
+
+ Hello
+
+
+
+ );
+
+ expect(component).toMatchSnapshot();
+ });
+});
diff --git a/src/components/drag_and_drop/draggable.tsx b/src/components/drag_and_drop/draggable.tsx
new file mode 100644
index 00000000000..4537368ac64
--- /dev/null
+++ b/src/components/drag_and_drop/draggable.tsx
@@ -0,0 +1,109 @@
+import React, {
+ CSSProperties,
+ Fragment,
+ FunctionComponent,
+ ReactElement,
+ cloneElement,
+ useContext,
+} from 'react';
+import { Draggable, DraggableProps } from 'react-beautiful-dnd';
+import classNames from 'classnames';
+import { CommonProps, Omit, keysOf } from '../common';
+import { EuiDroppableContext } from './droppable';
+
+const spacingToClassNameMap = {
+ none: null,
+ s: 'euiDraggable--s',
+ m: 'euiDraggable--m',
+ l: 'euiDraggable--l',
+};
+
+export const SPACING = keysOf(spacingToClassNameMap);
+export type EuiDraggableSpacing = keyof typeof spacingToClassNameMap;
+
+export interface EuiDraggableProps
+ extends CommonProps,
+ Omit {
+ children: ReactElement | DraggableProps['children'];
+ className?: string;
+ /**
+ * Whether the `children` will provide and set up its own drag handle
+ */
+ customDragHandle?: boolean;
+ /**
+ * Whether the item is currently in a position to be removed
+ */
+ isRemovable?: boolean;
+ /**
+ * Adds padding to the draggable item
+ */
+ spacing?: EuiDraggableSpacing;
+ style?: CSSProperties;
+}
+
+export const EuiDraggable: FunctionComponent = ({
+ customDragHandle = false,
+ draggableId,
+ isDragDisabled = false,
+ isRemovable = false,
+ index,
+ children,
+ className,
+ spacing = 'none',
+ style,
+ ...rest
+}) => {
+ const { cloneItems } = useContext(EuiDroppableContext);
+
+ return (
+
+ {(provided, snapshot) => {
+ const classes = classNames(
+ 'euiDraggable',
+ {
+ 'euiDraggable--hasClone': cloneItems,
+ 'euiDraggable--hasCustomDragHandle': customDragHandle,
+ 'euiDraggable--isDragging': snapshot.isDragging,
+ 'euiDraggable--withoutDropAnimation': isRemovable,
+ },
+ spacingToClassNameMap[spacing],
+ className
+ );
+ const childClasses = classNames('euiDraggable__item', {
+ 'euiDraggable__item--hasCustomDragHandle': customDragHandle,
+ 'euiDraggable__item--isDisabled': isDragDisabled,
+ 'euiDraggable__item--isDragging': snapshot.isDragging,
+ 'euiDraggable__item--isDropAnimating': snapshot.isDropAnimating,
+ });
+ const DraggableElement =
+ typeof children === 'function'
+ ? children(provided, snapshot)
+ : (children as ReactElement); // as specified by `DraggableProps`
+ return (
+
+
+ {cloneElement(DraggableElement, {
+ className: classNames(
+ DraggableElement.props.className,
+ childClasses
+ ),
+ })}
+
+ {cloneItems &&
+ (snapshot.isDragging && (
+
+ {DraggableElement}
+
+ ))}
+
+ );
+ }}
+
+ );
+};
diff --git a/src/components/drag_and_drop/droppable.test.tsx b/src/components/drag_and_drop/droppable.test.tsx
new file mode 100644
index 00000000000..e47b5e35a42
--- /dev/null
+++ b/src/components/drag_and_drop/droppable.test.tsx
@@ -0,0 +1,61 @@
+import React from 'react';
+import { render, mount } from 'enzyme';
+
+import { findTestSubject } from '../../test';
+import { requiredProps } from '../../test/required_props';
+
+import { EuiDragDropContext, EuiDroppable } from './';
+import { EuiDroppableContext } from './droppable';
+
+describe('EuiDroppable', () => {
+ test('is rendered', () => {
+ const handler = jest.fn();
+ const component = render(
+
+ {() => }
+
+ );
+
+ expect(component).toMatchSnapshot();
+ });
+
+ test('can be given ReactElement children', () => {
+ const handler = jest.fn();
+ const component = render(
+
+
+
+
+
+ );
+
+ expect(component).toMatchSnapshot();
+ });
+
+ describe('custom behavior', () => {
+ describe('cloneDraggables', () => {
+ const handler = jest.fn();
+ const component = mount(
+
+
+
+ {({ cloneItems }) => (
+
+ {cloneItems ? 'true' : 'false'}
+
+ )}
+
+
+
+ );
+
+ test('sets `cloneItems` on proprietary context', () => {
+ expect(findTestSubject(component, 'child').text()).toBe('true');
+ });
+
+ test('sets `isDropDisabled`', () => {
+ expect(component.find('.euiDroppable--isDisabled').length).toBe(1);
+ });
+ });
+ });
+});
diff --git a/src/components/drag_and_drop/droppable.tsx b/src/components/drag_and_drop/droppable.tsx
new file mode 100644
index 00000000000..35fc0b101b3
--- /dev/null
+++ b/src/components/drag_and_drop/droppable.tsx
@@ -0,0 +1,113 @@
+import React, {
+ CSSProperties,
+ FunctionComponent,
+ ReactElement,
+ useContext,
+} from 'react';
+import { Droppable, DroppableProps } from 'react-beautiful-dnd';
+import classNames from 'classnames';
+import { CommonProps, Omit, keysOf } from '../common';
+import { EuiDragDropContextContext } from './drag_drop_context';
+
+const spacingToClassNameMap = {
+ none: null,
+ s: 'euiDroppable--s',
+ m: 'euiDroppable--m',
+ l: 'euiDroppable--l',
+};
+
+export const SPACING = keysOf(spacingToClassNameMap);
+export type EuiDroppableSpacing = keyof typeof spacingToClassNameMap;
+
+export interface EuiDroppableProps
+ extends CommonProps,
+ Omit {
+ children: ReactElement | DroppableProps['children'];
+ className?: string;
+ /**
+ * Makes its items immutable. Dragging creates cloned items that can be dropped elsewhere.
+ */
+ cloneDraggables?: boolean;
+ style?: CSSProperties;
+ /**
+ * Adds padding to the droppable area
+ */
+ spacing?: EuiDroppableSpacing;
+ /**
+ * Adds an EuiPanel style to the droppable area
+ */
+ withPanel?: boolean;
+ /**
+ * Allow the panel to flex-grow?
+ */
+ grow?: boolean;
+}
+
+export const EuiDroppableContext = React.createContext({
+ cloneItems: false,
+});
+
+export const EuiDroppable: FunctionComponent = ({
+ droppableId,
+ direction,
+ isDropDisabled = false,
+ children,
+ className,
+ cloneDraggables = false,
+ spacing = 'none',
+ style,
+ type = 'EUI_DEFAULT',
+ withPanel = false,
+ grow = false,
+ ...rest
+}) => {
+ const { isDraggingType } = useContext(EuiDragDropContextContext);
+ const dropIsDisabled: boolean = cloneDraggables ? true : isDropDisabled;
+ return (
+
+ {(provided, snapshot) => {
+ const classes = classNames(
+ 'euiDroppable',
+ {
+ 'euiDroppable--isDisabled': dropIsDisabled,
+ 'euiDroppable--isDraggingOver': snapshot.isDraggingOver,
+ 'euiDroppable--isDraggingType': isDraggingType === type,
+ 'euiDroppable--withPanel': withPanel,
+ 'euiDroppable--grow': grow,
+ 'euiDroppable--noGrow': !grow,
+ },
+ spacingToClassNameMap[spacing],
+ className
+ );
+ const placeholderClasses = classNames('euiDroppable__placeholder', {
+ 'euiDroppable__placeholder--isHidden': cloneDraggables,
+ });
+ const DroppableElement =
+ typeof children === 'function'
+ ? children(provided, snapshot)
+ : children;
+ return (
+
+
+ {DroppableElement}
+
+
{provided.placeholder}
+
+ );
+ }}
+
+ );
+};
diff --git a/src/components/drag_and_drop/index.ts b/src/components/drag_and_drop/index.ts
new file mode 100644
index 00000000000..8ca9763f8fb
--- /dev/null
+++ b/src/components/drag_and_drop/index.ts
@@ -0,0 +1,4 @@
+export { EuiDragDropContext } from './drag_drop_context';
+export { EuiDraggable } from './draggable';
+export { EuiDroppable } from './droppable';
+export { copy, move, reorder } from './services';
diff --git a/src/components/drag_and_drop/services.ts b/src/components/drag_and_drop/services.ts
new file mode 100644
index 00000000000..a726609e985
--- /dev/null
+++ b/src/components/drag_and_drop/services.ts
@@ -0,0 +1,63 @@
+import { DraggableLocation } from 'react-beautiful-dnd';
+
+interface DropResult {
+ [droppableId: string]: any[];
+}
+
+export const reorder = (
+ list: [],
+ startIndex: number,
+ endIndex: number
+): Array<{}> => {
+ const result = Array.from(list);
+ const [removed] = result.splice(startIndex, 1);
+ result.splice(endIndex, 0, removed);
+
+ return result;
+};
+
+export const move = (
+ sourceList: any[],
+ destinationList: any[],
+ dropResultSource: DraggableLocation,
+ dropResultDestination: DraggableLocation
+): DropResult => {
+ const sourceClone = [...sourceList];
+ const destClone = [...destinationList];
+ const [removed] = sourceClone.splice(dropResultSource.index, 1);
+
+ destClone.splice(dropResultDestination.index, 0, removed);
+
+ return {
+ [dropResultSource.droppableId]: sourceClone,
+ [dropResultDestination.droppableId]: destClone,
+ };
+};
+
+export const copy = (
+ sourceList: any[],
+ destinationList: any[],
+ dropResultSource: DraggableLocation,
+ dropResultDestination: DraggableLocation,
+ /* Each EuiDraggable needs a unique ID, otherwise subsequent drag attempts on the to-be-copied
+ * element may result instead in dragging a previously created duplicate of that Draggable.
+ * `idModification` gives implementers better control over creating unique IDs when copying.
+ */
+ idModification: {
+ property: string | number;
+ modifier: () => string | number;
+ }
+): DropResult => {
+ const sourceClone = [...sourceList];
+ const destClone = [...destinationList];
+
+ destClone.splice(dropResultDestination.index, 0, {
+ ...sourceList[dropResultSource.index],
+ [idModification.property]: idModification.modifier(),
+ });
+
+ return {
+ [dropResultSource.droppableId]: sourceClone,
+ [dropResultDestination.droppableId]: destClone,
+ };
+};
diff --git a/src/components/index.js b/src/components/index.js
index d117aa42a7d..b93742d96cd 100644
--- a/src/components/index.js
+++ b/src/components/index.js
@@ -92,6 +92,12 @@ export {
EuiDescriptionListDescription,
} from './description_list';
+export {
+ EuiDragDropContext,
+ EuiDraggable,
+ EuiDroppable,
+} from './drag_and_drop';
+
export {
EuiEmptyPrompt,
} from './empty_prompt';
diff --git a/src/components/index.scss b/src/components/index.scss
index acb5c1154ef..b76165c3ef2 100644
--- a/src/components/index.scss
+++ b/src/components/index.scss
@@ -18,6 +18,7 @@
@import 'context_menu/index';
@import 'date_picker/index';
@import 'description_list/index';
+@import 'drag_and_drop/index';
@import 'empty_prompt/index';
@import 'error_boundary/index';
@import 'expression/index';
@@ -58,4 +59,3 @@
@import 'tool_tip/index';
@import 'text/index';
@import 'series_chart/index';
-
diff --git a/yarn.lock b/yarn.lock
index ec0f7c6718e..4fccc6de3c2 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -750,6 +750,14 @@
"@babel/helper-plugin-utils" "^7.0.0"
"@babel/plugin-transform-typescript" "^7.1.0"
+"@babel/runtime-corejs2@^7.3.4":
+ version "7.4.0"
+ resolved "https://registry.yarnpkg.com/@babel/runtime-corejs2/-/runtime-corejs2-7.4.0.tgz#72e94a5b59300482c2a334c06958021057c30da8"
+ integrity sha512-54PQxRRKLRYTrF89Z9tyjJ+uoxyn05Avt1Ou6UT1x5QWpdo1FPQ15i/lGL+ZLvE5yxzlTO7u2oE06VCU9IYijA==
+ dependencies:
+ core-js "^2.6.5"
+ regenerator-runtime "^0.13.2"
+
"@babel/runtime@^7.0.0":
version "7.3.1"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.3.1.tgz#574b03e8e8a9898eaf4a872a92ea20b7846f6f2a"
@@ -757,6 +765,13 @@
dependencies:
regenerator-runtime "^0.12.0"
+"@babel/runtime@^7.1.2":
+ version "7.3.4"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.3.4.tgz#73d12ba819e365fcf7fd152aed56d6df97d21c83"
+ integrity sha512-IvfvnMdSaLBateu0jfsYIpZTxAc2cKEXEMiezGGN75QcBcecDUKd3PgLAncT0oOgxKy8dd8hrJKj9MfzgfZd6g==
+ dependencies:
+ regenerator-runtime "^0.12.0"
+
"@babel/template@7.0.0-beta.36":
version "7.0.0-beta.36"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.36.tgz#02e903de5d68bd7899bce3c5b5447e59529abb00"
@@ -977,6 +992,13 @@
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.9.tgz#f2d14df87b0739041bc53a7d75e3d77d726a3ec0"
integrity sha512-Nha5b+jmBI271jdTMwrHiNXM+DvThjHOfyZtMX9kj/c/LUj2xiLHsG/1L3tJ8DjAoQN48cHwUwtqBotjyXaSdQ==
+"@types/react-beautiful-dnd@^10.1.0":
+ version "10.1.0"
+ resolved "https://registry.yarnpkg.com/@types/react-beautiful-dnd/-/react-beautiful-dnd-10.1.0.tgz#6aa032519051a793154abaca2989d0f070a01680"
+ integrity sha512-f2qsG2vlydADYF535EmtWj5J71baIOplXcSdticKuQ3Ierf+jtbcOA1Ptb9H3lAcQgscJC64NlkVt6wTQlV7WQ==
+ dependencies:
+ "@types/react" "*"
+
"@types/react-dom@^16.8.2":
version "16.8.2"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.8.2.tgz#9bd7d33f908b243ff0692846ef36c81d4941ad12"
@@ -3205,6 +3227,11 @@ core-js@^2.5.7:
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e"
integrity sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==
+core-js@^2.6.5:
+ version "2.6.5"
+ resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.5.tgz#44bc8d249e7fb2ff5d00e0341a7ffb94fbf67895"
+ integrity sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==
+
core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@@ -3376,6 +3403,13 @@ crypto-random-string@^1.0.0:
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e"
integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=
+css-box-model@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/css-box-model/-/css-box-model-1.1.1.tgz#c9fd8e7a8b1d59d41d6812fd1765433f671b2ee0"
+ integrity sha512-ZxbuLFeAPEDb0wPbGfT7783Vb00MVAkvOlMKwr0kA2PD5EGxk6P3MAhedvVuyVJCWb54bb+6HQ7pdPYENf8AZw==
+ dependencies:
+ tiny-invariant "^1.0.3"
+
css-color-names@0.0.4, css-color-names@^0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
@@ -6353,6 +6387,13 @@ hoist-non-react-statics@^2.2.1:
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz#343db84c6018c650778898240135a1420ee22ce0"
integrity sha1-ND24TGAYxlB3iJgkATWhQg7iLOA=
+hoist-non-react-statics@^3.1.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz#b09178f0122184fb95acf525daaecb4d8f45958b"
+ integrity sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==
+ dependencies:
+ react-is "^16.7.0"
+
hosted-git-info@^2.1.4:
version "2.5.0"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c"
@@ -8471,7 +8512,7 @@ lolex@^2.2.0, lolex@^2.3.2:
resolved "https://registry.yarnpkg.com/lolex/-/lolex-2.3.2.tgz#85f9450425103bf9e7a60668ea25dc43274ca807"
integrity sha512-A5pN2tkFj7H0dGIAM6MFvHKMJcPnjZsOMvR7ujCjfgW5TbV6H9vb1PgxLtHvjqNZTHsUolz+6/WEO0N1xNx2ng==
-loose-envify@^1.0.0, loose-envify@^1.3.1:
+loose-envify@^1.0.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
@@ -8673,6 +8714,11 @@ mem@^4.0.0:
mimic-fn "^1.0.0"
p-is-promise "^1.1.0"
+memoize-one@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.0.0.tgz#d55007dffefb8de7546659a1722a5d42e128286e"
+ integrity sha512-7g0+ejkOaI9w5x6LvQwmj68kUj6rxROywPSCqmclG/HBacmFnZqhVscQ8kovkn9FBCNJmOz6SY42+jnvZzDWdw==
+
memory-fs@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.2.0.tgz#f2bb25368bc121e391c2520de92969caee0a0290"
@@ -11123,6 +11169,15 @@ prop-types@^15.5.8, prop-types@^15.6.2:
loose-envify "^1.3.1"
object-assign "^4.1.1"
+prop-types@^15.6.1:
+ version "15.7.2"
+ resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
+ integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
+ dependencies:
+ loose-envify "^1.4.0"
+ object-assign "^4.1.1"
+ react-is "^16.8.1"
+
protochain@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/protochain/-/protochain-1.0.5.tgz#991c407e99de264aadf8f81504b5e7faf7bfa260"
@@ -11273,6 +11328,11 @@ querystringify@^2.0.0:
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.0.tgz#7ded8dfbf7879dcc60d0a644ac6754b283ad17ef"
integrity sha512-sluvZZ1YiTLD5jsqZcDmFyV2EwToyXZBfpoVOmktMmW+VEnhgakFHnasVph65fOjGPTWN0Nw3+XQaSeMayr0kg==
+raf-schd@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.0.tgz#9855756c5045ff4ed4516e14a47719387c3c907b"
+ integrity sha512-m7zq0JkIrECzw9mO5Zcq6jN4KayE34yoIS9hJoiZNXyOAT06PPA8PrR+WtJIeFW09YjUfNkMMN9lrmAt6BURCA==
+
raf@^3.1.0, raf@^3.4.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.0.tgz#a28876881b4bc2ca9117d4138163ddb80f781575"
@@ -11376,6 +11436,20 @@ react-ace@^5.5.0:
lodash.isequal "^4.1.1"
prop-types "^15.5.8"
+react-beautiful-dnd@^10.1.0:
+ version "10.1.0"
+ resolved "https://registry.yarnpkg.com/react-beautiful-dnd/-/react-beautiful-dnd-10.1.0.tgz#11b8958cfba5c54cb013dcff797efd15a3e0f333"
+ integrity sha512-oij2ZLIQ6SFmqy/MiXbuO8HUFHQ39gaPsTYj0Ewk0XwbLM32L+diVP0NXbEK7nhYv5lF2jviXGnda+BYMi6+nA==
+ dependencies:
+ "@babel/runtime-corejs2" "^7.3.4"
+ css-box-model "^1.1.1"
+ memoize-one "^5.0.0"
+ prop-types "^15.6.1"
+ raf-schd "^4.0.0"
+ react-redux "^5.0.7"
+ redux "^4.0.1"
+ tiny-invariant "^1.0.3"
+
react-clientside-effect@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/react-clientside-effect/-/react-clientside-effect-1.2.0.tgz#db823695f75e9616a5e4dd6d908e5ea627fb2516"
@@ -11435,6 +11509,11 @@ react-input-autosize@^2.2.1:
dependencies:
prop-types "^15.5.8"
+react-is@^16.6.0, react-is@^16.8.1:
+ version "16.8.3"
+ resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.3.tgz#4ad8b029c2a718fc0cfc746c8d4e1b7221e5387d"
+ integrity sha512-Y4rC1ZJmsxxkkPuMLwvKvlL1Zfpbcu+Bf4ZigkHup3v9EfdYhAlWAaVyA19olXq2o2mGn0w+dFKvk3pVVlYcIA==
+
react-is@^16.7.0, react-is@^16.8.2:
version "16.8.2"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.2.tgz#09891d324cad1cb0c1f2d91f70a71a4bee34df0f"
@@ -11445,6 +11524,11 @@ react-is@~16.3.0:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.3.2.tgz#f4d3d0e2f5fbb6ac46450641eb2e25bf05d36b22"
integrity sha512-ybEM7YOr4yBgFd6w8dJqwxegqZGJNBZl6U27HnGKuTZmDvVrD5quWOK/wAnMywiZzW+Qsk+l4X2c70+thp/A8Q==
+react-lifecycles-compat@^3.0.0:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
+ integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
+
react-motion@^0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/react-motion/-/react-motion-0.5.2.tgz#0dd3a69e411316567927917c6626551ba0607316"
@@ -11466,6 +11550,19 @@ react-redux@^5.0.6:
loose-envify "^1.1.0"
prop-types "^15.5.10"
+react-redux@^5.0.7:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.1.1.tgz#88e368682c7fa80e34e055cd7ac56f5936b0f52f"
+ integrity sha512-LE7Ned+cv5qe7tMV5BPYkGQ5Lpg8gzgItK07c67yHvJ8t0iaD9kPFPAli/mYkiyJYrs2pJgExR2ZgsGqlrOApg==
+ dependencies:
+ "@babel/runtime" "^7.1.2"
+ hoist-non-react-statics "^3.1.0"
+ invariant "^2.2.4"
+ loose-envify "^1.1.0"
+ prop-types "^15.6.1"
+ react-is "^16.6.0"
+ react-lifecycles-compat "^3.0.0"
+
react-router-redux@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/react-router-redux/-/react-router-redux-4.0.8.tgz#227403596b5151e182377dab835b5d45f0f8054e"
@@ -11803,6 +11900,14 @@ redux@^3.7.2:
loose-envify "^1.1.0"
symbol-observable "^1.0.3"
+redux@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.1.tgz#436cae6cc40fbe4727689d7c8fae44808f1bfef5"
+ integrity sha512-R7bAtSkk7nY6O/OYMVR9RiBI+XghjF9rlbl5806HJbQph0LJVHZrU5oaO4q70eUKiqMRqm4y07KLTlMZ2BlVmg==
+ dependencies:
+ loose-envify "^1.4.0"
+ symbol-observable "^1.2.0"
+
regenerate-unicode-properties@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-7.0.0.tgz#107405afcc4a190ec5ed450ecaa00ed0cafa7a4c"
@@ -11830,6 +11935,11 @@ regenerator-runtime@^0.12.0:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de"
integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==
+regenerator-runtime@^0.13.2:
+ version "0.13.2"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz#32e59c9a6fb9b1a4aff09b4930ca2d4477343447"
+ integrity sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==
+
regenerator-transform@^0.13.3:
version "0.13.3"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.13.3.tgz#264bd9ff38a8ce24b06e0636496b2c856b57bcbb"
@@ -13458,6 +13568,11 @@ symbol-observable@^1.0.3:
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.1.0.tgz#5c68fd8d54115d9dfb72a84720549222e8db9b32"
integrity sha512-dQoid9tqQ+uotGhuTKEY11X4xhyYePVnqGSoSm3OGKh2E8LZ6RPULp1uXTctk33IeERlrRJYoVSBglsL05F5Uw==
+symbol-observable@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
+ integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
+
symbol-tree@^3.2.2:
version "3.2.2"
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6"
@@ -13699,6 +13814,11 @@ timsort@^0.3.0:
resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
+tiny-invariant@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.0.3.tgz#91efaaa0269ccb6271f0296aeedb05fc3e067b7a"
+ integrity sha512-ytQx8T4DL8PjlX53yYzcIC0WhIZbpR0p1qcYjw2pHu3w6UtgWwFJQ/02cnhOnBBhlFx/edUIfcagCaQSe3KMWg==
+
tinycolor2@^1.1.2, tinycolor2@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.1.tgz#f4fad333447bc0b07d4dc8e9209d8f39a8ac77e8"