diff --git a/package.json b/package.json
index 8c5fea39b8d4d..fe9d8cd085981 100644
--- a/package.json
+++ b/package.json
@@ -63,6 +63,7 @@
"prop-types": "^15.6.1",
"raw-loader": "0.5.1",
"react": "^16.2.0",
+ "react-beautiful-dnd": "^8.0.7",
"react-datetime": "^2.14.0",
"react-dom": "^16.2.0",
"react-dropzone": "^4.2.9",
@@ -135,4 +136,4 @@
"sinon": "^4.5.0",
"through2": "^2.0.3"
}
-}
\ No newline at end of file
+}
diff --git a/public/components/page_manager/page_controls.js b/public/components/page_manager/page_controls.js
deleted file mode 100644
index 2826f74451e78..0000000000000
--- a/public/components/page_manager/page_controls.js
+++ /dev/null
@@ -1,72 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import { EuiFlexGroup, EuiFlexItem, EuiButtonIcon } from '@elastic/eui';
-
-export const PageControls = ({ pageId, onDelete, onDuplicate, movePage }) => {
- const handleDuplicate = ev => {
- ev.preventDefault();
- onDuplicate(pageId);
- };
-
- const handleDelete = ev => {
- ev.preventDefault();
- onDelete(pageId);
- };
-
- const handleMove = position => ev => {
- ev.preventDefault();
- movePage(pageId, position);
- };
-
- return (
-
-
-
- {
- handleMove(-1)(ev);
- }}
- />
-
-
-
-
-
-
-
-
- {
- handleMove(+1)(ev);
- }}
- />
-
-
-
- );
-};
-
-PageControls.propTypes = {
- pageId: PropTypes.string.isRequired,
- pageNumber: PropTypes.number.isRequired,
- onDelete: PropTypes.func.isRequired,
- onDuplicate: PropTypes.func.isRequired,
- movePage: PropTypes.func.isRequired,
-};
diff --git a/public/components/page_manager/page_manager.js b/public/components/page_manager/page_manager.js
index 37d3aec35d6ae..8309c7ab851e9 100644
--- a/public/components/page_manager/page_manager.js
+++ b/public/components/page_manager/page_manager.js
@@ -1,10 +1,10 @@
import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
import { EuiIcon, EuiFlexGroup, EuiFlexItem, EuiText, EuiToolTip } from '@elastic/eui';
+import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
import { ConfirmModal } from '../confirm_modal';
import { Link } from '../link';
import { PagePreview } from '../page_preview';
-import { PageControls } from './page_controls';
export class PageManager extends React.PureComponent {
static propTypes = {
@@ -12,6 +12,7 @@ export class PageManager extends React.PureComponent {
workpadId: PropTypes.string.isRequired,
addPage: PropTypes.func.isRequired,
movePage: PropTypes.func.isRequired,
+ previousPage: PropTypes.func.isRequired,
duplicatePage: PropTypes.func.isRequired,
removePage: PropTypes.func.isRequired,
selectedPage: PropTypes.string,
@@ -19,6 +20,51 @@ export class PageManager extends React.PureComponent {
setDeleteId: PropTypes.func.isRequired,
};
+ state = {
+ showTrayPop: true,
+ };
+
+ componentDidMount() {
+ // gives the tray pop animation time to finish
+ setTimeout(() => {
+ this.scrollToActivePage();
+ this.setState({ showTrayPop: false });
+ }, 1000);
+ }
+
+ componentDidUpdate(prevProps) {
+ // scrolls to the active page on the next tick, otherwise new pages don't scroll completely into view
+ if (prevProps.selectedPage !== this.props.selectedPage) setTimeout(this.scrollToActivePage, 0);
+ }
+
+ scrollToActivePage = () => {
+ if (this.activePageRef && this.pageListRef) {
+ const pageOffset = this.activePageRef.offsetLeft;
+ const {
+ left: pageLeft,
+ right: pageRight,
+ width: pageWidth,
+ } = this.activePageRef.getBoundingClientRect();
+ const {
+ left: listLeft,
+ right: listRight,
+ width: listWidth,
+ } = this.pageListRef.getBoundingClientRect();
+
+ if (pageLeft < listLeft)
+ this.pageListRef.scrollTo({
+ left: pageOffset,
+ behavior: 'smooth',
+ });
+ if (pageRight > listRight) {
+ this.pageListRef.scrollTo({
+ left: pageOffset - listWidth + pageWidth,
+ behavior: 'smooth',
+ });
+ }
+ }
+ };
+
confirmDelete = pageId => {
this.props.setDeleteId(pageId);
};
@@ -26,8 +72,21 @@ export class PageManager extends React.PureComponent {
resetDelete = () => this.props.setDeleteId(null);
doDelete = () => {
+ const { previousPage, removePage, deleteId, selectedPage } = this.props;
this.resetDelete();
- this.props.removePage(this.props.deleteId);
+ if (deleteId === selectedPage) previousPage();
+ removePage(deleteId);
+ };
+
+ onDragEnd = ({ draggableId: pageId, source, destination }) => {
+ // dropped outside the list
+ if (!destination) {
+ return;
+ }
+
+ const position = destination.index - source.index;
+
+ this.props.movePage(pageId, position);
};
renderPage = (page, i) => {
@@ -35,58 +94,77 @@ export class PageManager extends React.PureComponent {
const pageNumber = i + 1;
return (
-
-
-
-
- {pageNumber}
-
-
-
-
-
-
-
-
-
-
+
+ {provided => (
+ {
+ if (page.id === selectedPage) this.activePageRef = el;
+ provided.innerRef(el);
+ }}
+ {...provided.draggableProps}
+ {...provided.dragHandleProps}
+ >
+
+
+
+ {pageNumber}
+
+
+
+
+
+
+
+
+
+ )}
+
);
};
render() {
const { pages, addPage, deleteId } = this.props;
+ const { showTrayPop } = this.state;
return (
-
-
- {pages.map(this.renderPage)}
-
-
+
+
+ {provided => (
+ {
+ this.pageListRef = el;
+ provided.innerRef(el);
+ }}
+ {...provided.droppableProps}
+ >
+ {pages.map(this.renderPage)}
+ {provided.placeholder}
+
+ )}
+
+
div {
+ .canvasPageManager--trayPop > div {
animation: trayPop $euiAnimSpeedNormal $euiAnimSlightResistance;
opacity: 0;
animation-fill-mode: forwards;
}
-
- @for $i from 1 through 10 {
- .canvasPageManager__pageList > div:nth-child(#{$i}n) {
+ @for $i from 1 through 20 {
+ .canvasPageManager--trayPop > div:nth-child(#{$i}n) {
animation-delay: #{$i * 0.05}s;
}
}
@@ -39,7 +38,7 @@
opacity: 0;
animation: buttonPop $euiAnimSpeedNormal $euiAnimSlightResistance;
animation-fill-mode: forwards;
- height: 160px;
+ height: 144px;
}
.canvasPageManager__addPageTip {
@@ -50,11 +49,12 @@
.canvasPageManager__page {
padding: $euiSize $euiSize $euiSize $euiSizeS;
color: inherit;
- min-height: 144px;
- max-height: 160px;
+ min-height: 100px;
+ max-height: 144px;
- &:focus, &-isActive {
- background-color: darken($euiColorLightestShade, 10%);
+ &:focus,
+ &-isActive {
+ background-color: transparentize(darken($euiColorLightestShade, 30%), 0.5);
outline: none;
text-decoration: none;
}
@@ -65,22 +65,18 @@
}
}
- &:hover {
+ &:hover,
+ &:focus {
text-decoration: none;
.canvasPageManager__pagePreview {
- @include euiBottomShadowMedium($opacity: .3);
+ @include euiBottomShadowMedium($opacity: 0.3);
}
.canvasPageManager__controls {
visibility: visible;
opacity: 1;
}
-
- .canvasPageManager__removeIcon {
- visibility: visible;
- opacity: 1;
- }
}
&-isActive {
@@ -122,11 +118,14 @@
}
.canvasPageManager__controls {
- margin-top: $euiSizeS;
+ position: absolute;
+ right: $euiSizeS;
+ top: $euiSizeS;
visibility: hidden;
opacity: 0;
transition: opacity $euiAnimSpeedFast $euiAnimSlightResistance;
- transition-delay: $euiAnimSpeedNormal;
+ transition-delay: $euiAnimSpeedExtraSlow;
+ background: transparentize($euiColorGhost, 0.5);
border-radius: $euiBorderRadius;
}
}
diff --git a/public/components/page_preview/page_controls.js b/public/components/page_preview/page_controls.js
new file mode 100644
index 0000000000000..a331b57f97a1d
--- /dev/null
+++ b/public/components/page_preview/page_controls.js
@@ -0,0 +1,50 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { EuiFlexGroup, EuiFlexItem, EuiButtonIcon, EuiToolTip } from '@elastic/eui';
+
+export const PageControls = ({ pageId, onDelete, onDuplicate }) => {
+ const handleDuplicate = ev => {
+ ev.preventDefault();
+ onDuplicate(pageId);
+ };
+
+ const handleDelete = ev => {
+ ev.preventDefault();
+ onDelete(pageId);
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+PageControls.propTypes = {
+ pageId: PropTypes.string.isRequired,
+ pageNumber: PropTypes.number.isRequired,
+ onDelete: PropTypes.func.isRequired,
+ onDuplicate: PropTypes.func.isRequired,
+};
diff --git a/public/components/page_preview/page_preview.js b/public/components/page_preview/page_preview.js
index 6e8a135252e56..21e46f0b503e9 100644
--- a/public/components/page_preview/page_preview.js
+++ b/public/components/page_preview/page_preview.js
@@ -1,18 +1,21 @@
import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { DomPreview } from '../dom_preview';
+import { PageControls } from './page_controls';
export class PagePreview extends PureComponent {
static propTypes = {
page: PropTypes.object.isRequired,
height: PropTypes.number.isRequired,
pageNumber: PropTypes.number.isRequired,
- width: PropTypes.number,
- setWidth: PropTypes.func,
+ width: PropTypes.number.isRequired,
+ setWidth: PropTypes.func.isRequired,
+ duplicatePage: PropTypes.func.isRequired,
+ confirmDelete: PropTypes.func.isRequired,
};
render() {
- const { page, pageNumber, height, width, setWidth } = this.props;
+ const { page, pageNumber, height, width, setWidth, duplicatePage, confirmDelete } = this.props;
return (
);
diff --git a/public/components/toolbar/toolbar.js b/public/components/toolbar/toolbar.js
index fe56480014113..7b622702858ea 100644
--- a/public/components/toolbar/toolbar.js
+++ b/public/components/toolbar/toolbar.js
@@ -55,7 +55,7 @@ export const Toolbar = props => {
);
const trays = {
- pageManager: ,
+ pageManager: ,
workpadloader: workpadLoader,
expression: !elementIsSelected ? null : ,
};