diff --git a/x-pack/plugins/canvas/common/lib/constants.js b/x-pack/plugins/canvas/common/lib/constants.js
index 339a0e48d762b..8c3c5d5dcff30 100644
--- a/x-pack/plugins/canvas/common/lib/constants.js
+++ b/x-pack/plugins/canvas/common/lib/constants.js
@@ -20,3 +20,4 @@ export const FETCH_TIMEOUT = 30000; // 30 seconds
export const CANVAS_USAGE_TYPE = 'canvas';
export const DEFAULT_WORKPAD_CSS = '.canvasPage {\n\n}';
export const VALID_IMAGE_TYPES = ['gif', 'jpeg', 'png', 'svg+xml'];
+export const ASSET_MAX_SIZE = 25000;
diff --git a/x-pack/plugins/canvas/public/components/asset_manager/asset_manager.js b/x-pack/plugins/canvas/public/components/asset_manager/asset_manager.js
index bbde11f8aedac..fc32e64e667eb 100644
--- a/x-pack/plugins/canvas/public/components/asset_manager/asset_manager.js
+++ b/x-pack/plugins/canvas/public/components/asset_manager/asset_manager.js
@@ -33,10 +33,11 @@ import { ConfirmModal } from '../confirm_modal';
import { Clipboard } from '../clipboard';
import { Download } from '../download';
import { Loading } from '../loading';
+import { ASSET_MAX_SIZE } from '../../../common/lib/constants';
export class AssetManager extends React.PureComponent {
static propTypes = {
- assets: PropTypes.array,
+ assetValues: PropTypes.array,
addImageElement: PropTypes.func,
removeAsset: PropTypes.func.isRequired,
copyAsset: PropTypes.func.isRequired,
@@ -147,15 +148,13 @@ export class AssetManager extends React.PureComponent {
render() {
const { isModalVisible, loading } = this.state;
- const { assets } = this.props;
-
- const assetMaxLimit = 25000;
+ const { assetValues } = this.props;
const assetsTotal = Math.round(
- assets.reduce((total, asset) => total + asset.value.length, 0) / 1024
+ assetValues.reduce((total, { value }) => total + value.length, 0) / 1024
);
- const percentageUsed = Math.round((assetsTotal / assetMaxLimit) * 100);
+ const percentageUsed = Math.round((assetsTotal / ASSET_MAX_SIZE) * 100);
const emptyAssets = (
@@ -208,9 +207,9 @@ export class AssetManager extends React.PureComponent {
- {assets.length ? (
+ {assetValues.length ? (
- {assets.map(this.renderAsset)}
+ {assetValues.map(this.renderAsset)}
) : (
emptyAssets
@@ -221,7 +220,7 @@ export class AssetManager extends React.PureComponent {
({
- assets: Object.values(getAssets(state)), // pull values out of assets object
+ assets: getAssets(state),
selectedPage: getSelectedPage(state),
});
@@ -60,19 +60,22 @@ const mapDispatchToProps = dispatch => ({
});
const mergeProps = (stateProps, dispatchProps, ownProps) => {
- const { assets } = stateProps;
+ const { assets, selectedPage } = stateProps;
const { onAssetAdd } = dispatchProps;
+ const assetValues = Object.values(assets); // pull values out of assets object
+
return {
...ownProps,
- ...stateProps,
...dispatchProps,
+ selectedPage,
+ assetValues,
addImageElement: dispatchProps.addImageElement(stateProps.selectedPage),
onAssetAdd: file => {
const [type, subtype] = get(file, 'type', '').split('/');
if (type === 'image' && VALID_IMAGE_TYPES.indexOf(subtype) >= 0) {
return encode(file).then(dataurl => {
const type = 'dataurl';
- const existingId = findExistingAsset(type, dataurl, assets);
+ const existingId = findExistingAsset(type, dataurl, assetValues);
if (existingId) {
return existingId;
}
diff --git a/x-pack/plugins/canvas/public/components/fullscreen_control/fullscreen_control.js b/x-pack/plugins/canvas/public/components/fullscreen_control/fullscreen_control.js
index 5a4f73775f6ca..4e2bc84deef7d 100644
--- a/x-pack/plugins/canvas/public/components/fullscreen_control/fullscreen_control.js
+++ b/x-pack/plugins/canvas/public/components/fullscreen_control/fullscreen_control.js
@@ -9,6 +9,15 @@ import PropTypes from 'prop-types';
import { Shortcuts } from 'react-shortcuts';
export class FullscreenControl extends React.PureComponent {
+ keyHandler = action => {
+ const enterFullscreen = action === 'FULLSCREEN';
+ const exitFullscreen = this.props.isFullscreen && action === 'FULLSCREEN_EXIT';
+
+ if (enterFullscreen || exitFullscreen) {
+ this.toggleFullscreen();
+ }
+ };
+
toggleFullscreen = () => {
const { setFullscreen, isFullscreen } = this.props;
setFullscreen(!isFullscreen);
@@ -17,17 +26,11 @@ export class FullscreenControl extends React.PureComponent {
render() {
const { children, isFullscreen } = this.props;
- const keyHandler = action => {
- if (action === 'FULLSCREEN' || (isFullscreen && action === 'FULLSCREEN_EXIT')) {
- this.toggleFullscreen();
- }
- };
-
return (
({
});
export const Toolbar = compose(
+ pure,
connect(mapStateToProps),
getContext({
router: PropTypes.object,
diff --git a/x-pack/plugins/canvas/public/components/workpad/index.js b/x-pack/plugins/canvas/public/components/workpad/index.js
index 40ae5e1d1a664..c030cb1cabcbc 100644
--- a/x-pack/plugins/canvas/public/components/workpad/index.js
+++ b/x-pack/plugins/canvas/public/components/workpad/index.js
@@ -6,7 +6,7 @@
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
-import { compose, withState, withProps, getContext, withHandlers } from 'recompose';
+import { pure, compose, withState, withProps, getContext, withHandlers } from 'recompose';
import { transitionsRegistry } from '../../lib/transitions_registry';
import { undoHistory, redoHistory } from '../../state/actions/history';
import { fetchAllRenderables } from '../../state/actions/elements';
@@ -19,13 +19,19 @@ import {
} from '../../state/selectors/workpad';
import { Workpad as Component } from './workpad';
-const mapStateToProps = state => ({
- pages: getPages(state),
- selectedPageNumber: getSelectedPageIndex(state) + 1,
- totalElementCount: getAllElements(state).length,
- workpad: getWorkpad(state),
- isFullscreen: getFullscreen(state),
-});
+const mapStateToProps = state => {
+ const { width, height, id: workpadId, css: workpadCss } = getWorkpad(state);
+ return {
+ pages: getPages(state),
+ selectedPageNumber: getSelectedPageIndex(state) + 1,
+ totalElementCount: getAllElements(state).length,
+ width,
+ height,
+ workpadCss,
+ workpadId,
+ isFullscreen: getFullscreen(state),
+ };
+};
const mapDispatchToProps = {
undoHistory,
@@ -34,6 +40,7 @@ const mapDispatchToProps = {
};
export const Workpad = compose(
+ pure,
getContext({
router: PropTypes.object,
}),
@@ -68,17 +75,17 @@ export const Workpad = compose(
}
props.setPrevSelectedPageNumber(props.selectedPageNumber);
const transitionPage = Math.max(props.selectedPageNumber, pageNumber) - 1;
- const { transition } = props.workpad.pages[transitionPage];
+ const { transition } = props.pages[transitionPage];
if (transition) {
props.setTransition(transition);
}
- props.router.navigateTo('loadWorkpad', { id: props.workpad.id, page: pageNumber });
+ props.router.navigateTo('loadWorkpad', { id: props.workpadId, page: pageNumber });
},
}),
withHandlers({
onTransitionEnd: ({ setTransition }) => () => setTransition(null),
nextPage: props => () => {
- const pageNumber = Math.min(props.selectedPageNumber + 1, props.workpad.pages.length);
+ const pageNumber = Math.min(props.selectedPageNumber + 1, props.pages.length);
props.onPageChange(pageNumber);
},
previousPage: props => () => {
diff --git a/x-pack/plugins/canvas/public/components/workpad/workpad.js b/x-pack/plugins/canvas/public/components/workpad/workpad.js
index dcdbed0ecea65..956d8a0dbdb63 100644
--- a/x-pack/plugins/canvas/public/components/workpad/workpad.js
+++ b/x-pack/plugins/canvas/public/components/workpad/workpad.js
@@ -10,33 +10,41 @@ import { Shortcuts } from 'react-shortcuts';
import Style from 'style-it';
import { WorkpadPage } from '../workpad_page';
import { Fullscreen } from '../fullscreen';
-import { setDocTitle } from '../../lib/doc_title';
-export const Workpad = props => {
- const {
- selectedPageNumber,
- getAnimation,
- onTransitionEnd,
- pages,
- totalElementCount,
- workpad,
- fetchAllRenderables,
- undoHistory,
- redoHistory,
- setGrid, // TODO: Get rid of grid when we improve the layout engine
- grid,
- nextPage,
- previousPage,
- isFullscreen,
- } = props;
+const WORKPAD_CANVAS_BUFFER = 32; // 32px padding around the workpad
- const { height, width } = workpad;
- const bufferStyle = {
- height: isFullscreen ? height : height + 32,
- width: isFullscreen ? width : width + 32,
+export class Workpad extends React.PureComponent {
+ static propTypes = {
+ selectedPageNumber: PropTypes.number.isRequired,
+ getAnimation: PropTypes.func.isRequired,
+ onTransitionEnd: PropTypes.func.isRequired,
+ grid: PropTypes.bool.isRequired,
+ setGrid: PropTypes.func.isRequired,
+ pages: PropTypes.array.isRequired,
+ totalElementCount: PropTypes.number.isRequired,
+ isFullscreen: PropTypes.bool.isRequired,
+ width: PropTypes.number.isRequired,
+ height: PropTypes.number.isRequired,
+ workpadCss: PropTypes.string,
+ undoHistory: PropTypes.func.isRequired,
+ redoHistory: PropTypes.func.isRequired,
+ nextPage: PropTypes.func.isRequired,
+ previousPage: PropTypes.func.isRequired,
+ fetchAllRenderables: PropTypes.func.isRequired,
+ css: PropTypes.object,
};
- const keyHandler = action => {
+ keyHandler = action => {
+ const {
+ fetchAllRenderables,
+ undoHistory,
+ redoHistory,
+ nextPage,
+ previousPage,
+ grid, // TODO: Get rid of grid when we improve the layout engine
+ setGrid,
+ } = this.props;
+
// handle keypress events for editor and presentation events
// this exists in both contexts
if (action === 'REFRESH') {
@@ -63,84 +71,84 @@ export const Workpad = props => {
}
};
- setDocTitle(workpad.name);
+ render() {
+ const {
+ selectedPageNumber,
+ getAnimation,
+ onTransitionEnd,
+ pages,
+ totalElementCount,
+ width,
+ height,
+ workpadCss,
+ grid,
+ isFullscreen,
+ } = this.props;
- return (
-
-
- {!isFullscreen && (
-
- )}
+ const bufferStyle = {
+ height: isFullscreen ? height : height + WORKPAD_CANVAS_BUFFER,
+ width: isFullscreen ? width : width + WORKPAD_CANVAS_BUFFER,
+ };
-
- {({ isFullscreen, windowSize }) => {
- const scale = Math.min(windowSize.height / height, windowSize.width / width);
- const fsStyle = isFullscreen
- ? {
- transform: `scale3d(${scale}, ${scale}, 1)`,
- WebkitTransform: `scale3d(${scale}, ${scale}, 1)`,
- msTransform: `scale3d(${scale}, ${scale}, 1)`,
- // height,
- // width,
- height: windowSize.height < height ? 'auto' : height,
- width: windowSize.width < width ? 'auto' : width,
- }
- : {};
+ return (
+
+
+ {!isFullscreen && (
+
+ )}
- // NOTE: the data-shared-* attributes here are used for reporting
- return Style.it(
- workpad.css,
-
- {isFullscreen && (
-
- )}
- {pages.map((page, i) => (
-
- ))}
+
+ {({ isFullscreen, windowSize }) => {
+ const scale = Math.min(windowSize.height / height, windowSize.width / width);
+ const fsStyle = isFullscreen
+ ? {
+ transform: `scale3d(${scale}, ${scale}, 1)`,
+ WebkitTransform: `scale3d(${scale}, ${scale}, 1)`,
+ msTransform: `scale3d(${scale}, ${scale}, 1)`,
+ // height,
+ // width,
+ height: windowSize.height < height ? 'auto' : height,
+ width: windowSize.width < width ? 'auto' : width,
+ }
+ : {};
+
+ // NOTE: the data-shared-* attributes here are used for reporting
+ return Style.it(
+ workpadCss,
-
- );
- }}
-
+ className={`canvasWorkpad ${isFullscreen ? 'fullscreen' : ''}`}
+ style={fsStyle}
+ data-shared-items-count={totalElementCount}
+ >
+ {isFullscreen && (
+
+ )}
+ {pages.map((page, i) => (
+
+ ))}
+
+
+ );
+ }}
+
+
-
- );
-};
-
-Workpad.propTypes = {
- selectedPageNumber: PropTypes.number.isRequired,
- getAnimation: PropTypes.func.isRequired,
- onTransitionEnd: PropTypes.func.isRequired,
- grid: PropTypes.bool.isRequired,
- setGrid: PropTypes.func.isRequired,
- pages: PropTypes.array.isRequired,
- totalElementCount: PropTypes.number.isRequired,
- isFullscreen: PropTypes.bool.isRequired,
- workpad: PropTypes.object.isRequired,
- undoHistory: PropTypes.func.isRequired,
- redoHistory: PropTypes.func.isRequired,
- nextPage: PropTypes.func.isRequired,
- previousPage: PropTypes.func.isRequired,
- fetchAllRenderables: PropTypes.func.isRequired,
- css: PropTypes.object,
-};
+ );
+ }
+}
diff --git a/x-pack/plugins/canvas/public/components/workpad_header/index.js b/x-pack/plugins/canvas/public/components/workpad_header/index.js
index e724b97b0bb4f..66b1988c83c83 100644
--- a/x-pack/plugins/canvas/public/components/workpad_header/index.js
+++ b/x-pack/plugins/canvas/public/components/workpad_header/index.js
@@ -7,7 +7,7 @@
import { compose, withState } from 'recompose';
import { connect } from 'react-redux';
import { canUserWrite } from '../../state/selectors/app';
-import { getWorkpadName, getSelectedPage, isWriteable } from '../../state/selectors/workpad';
+import { getSelectedPage, isWriteable } from '../../state/selectors/workpad';
import { setWriteable } from '../../state/actions/workpad';
import { addElement } from '../../state/actions/elements';
import { WorkpadHeader as Component } from './workpad_header';
@@ -15,7 +15,6 @@ import { WorkpadHeader as Component } from './workpad_header';
const mapStateToProps = state => ({
isWriteable: isWriteable(state) && canUserWrite(state),
canUserWrite: canUserWrite(state),
- workpadName: getWorkpadName(state),
selectedPage: getSelectedPage(state),
});
@@ -33,10 +32,10 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => ({
});
export const WorkpadHeader = compose(
- withState('showElementModal', 'setShowElementModal', false),
connect(
mapStateToProps,
mapDispatchToProps,
mergeProps
- )
+ ),
+ withState('showElementModal', 'setShowElementModal', false)
)(Component);
diff --git a/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.js b/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.js
index 507f7a1a90d98..846c3f1ff5b1e 100644
--- a/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.js
+++ b/x-pack/plugins/canvas/public/components/workpad_header/workpad_header.js
@@ -23,122 +23,134 @@ import { WorkpadExport } from '../workpad_export';
import { FullscreenControl } from '../fullscreen_control';
import { RefreshControl } from '../refresh_control';
-export const WorkpadHeader = ({
- isWriteable,
- canUserWrite,
- toggleWriteable,
- addElement,
- setShowElementModal,
- showElementModal,
-}) => {
- const keyHandler = action => {
+export class WorkpadHeader extends React.PureComponent {
+ static propTypes = {
+ isWriteable: PropTypes.bool,
+ toggleWriteable: PropTypes.func,
+ addElement: PropTypes.func.isRequired,
+ showElementModal: PropTypes.bool,
+ setShowElementModal: PropTypes.func,
+ };
+
+ fullscreenButton = ({ toggleFullscreen }) => (
+
+
+
+ );
+
+ keyHandler = action => {
if (action === 'EDITING') {
- toggleWriteable();
+ this.props.toggleWriteable();
}
};
- const elementAdd = (
-
- setShowElementModal(false)}
- className="canvasModal--fixedSize"
- maxWidth="1000px"
- initialFocus=".canvasElements__filter"
- >
- {
- addElement(element);
- setShowElementModal(false);
- }}
- />
-
- setShowElementModal(false)}>
- Dismiss
-
-
-
-
- );
+ elementAdd = () => {
+ const { addElement, setShowElementModal } = this.props;
- let readOnlyToolTip = '';
+ return (
+
+ setShowElementModal(false)}
+ className="canvasModal--fixedSize"
+ maxWidth="1000px"
+ initialFocus=".canvasElements__filter"
+ >
+ {
+ addElement(element);
+ setShowElementModal(false);
+ }}
+ />
+
+ setShowElementModal(false)}>
+ Dismiss
+
+
+
+
+ );
+ };
- if (!canUserWrite) {
- readOnlyToolTip = "You don't have permission to edit this workpad";
- } else {
- readOnlyToolTip = isWriteable ? 'Hide editing controls' : 'Show editing controls';
- }
+ getTooltipText = () => {
+ if (!this.props.canUserWrite) {
+ return "You don't have permission to edit this workpad";
+ } else {
+ return this.props.isWriteable ? 'Hide editing controls' : 'Show editing controls';
+ }
+ };
- return (
-
- {showElementModal ? elementAdd : null}
-
-
-
-
-
-
-
-
- {({ toggleFullscreen }) => (
-
-
-
- )}
-
-
-
-
-
-
- {canUserWrite && (
-
- )}
-
- {
- toggleWriteable();
- }}
- size="s"
- aria-label={readOnlyToolTip}
- isDisabled={!canUserWrite}
- />
-
-
-
-
- {isWriteable ? (
+ render() {
+ const {
+ isWriteable,
+ canUserWrite,
+ toggleWriteable,
+ setShowElementModal,
+ showElementModal,
+ } = this.props;
+
+ return (
+
+ {showElementModal ? this.elementAdd() : null}
+
-
+
+
+
+
+
+ {this.fullscreenButton}
+
-
+
- setShowElementModal(true)}
- >
- Add element
-
+ {canUserWrite && (
+
+ )}
+
+ {
+ toggleWriteable();
+ }}
+ size="s"
+ aria-label={this.getTooltipText()}
+ isDisabled={!canUserWrite}
+ />
+
- ) : null}
-
-
- );
-};
-
-WorkpadHeader.propTypes = {
- isWriteable: PropTypes.bool,
- toggleWriteable: PropTypes.func,
- addElement: PropTypes.func.isRequired,
- showElementModal: PropTypes.bool,
- setShowElementModal: PropTypes.func,
-};
+ {isWriteable ? (
+
+
+
+
+
+
+ setShowElementModal(true)}
+ >
+ Add element
+
+
+
+
+ ) : null}
+
+
+ );
+ }
+}
diff --git a/x-pack/plugins/canvas/public/state/middleware/workpad_update.js b/x-pack/plugins/canvas/public/state/middleware/workpad_update.js
index 574168b110b55..cfb588048ecb3 100644
--- a/x-pack/plugins/canvas/public/state/middleware/workpad_update.js
+++ b/x-pack/plugins/canvas/public/state/middleware/workpad_update.js
@@ -7,14 +7,21 @@
import { duplicatePage } from '../actions/pages';
import { fetchRenderable } from '../actions/elements';
import { setWriteable } from '../actions/workpad';
-import { getPages, isWriteable } from '../selectors/workpad';
+import { getPages, getWorkpadName, isWriteable } from '../selectors/workpad';
import { getWindow } from '../../lib/get_window';
+import { setDocTitle } from '../../lib/doc_title';
export const workpadUpdate = ({ dispatch, getState }) => next => action => {
const oldIsWriteable = isWriteable(getState());
+ const oldName = getWorkpadName(getState());
next(action);
+ // This middleware updates the page title when the workpad name changes
+ if (getWorkpadName(getState()) !== oldName) {
+ setDocTitle(getWorkpadName(getState()));
+ }
+
// This middleware fetches all of the renderable elements on new, duplicate page
if (action.type === duplicatePage.toString()) {
// When a page has been duplicated, it will be added as the last page, so fetch it
@@ -22,17 +29,16 @@ export const workpadUpdate = ({ dispatch, getState }) => next => action => {
const newPage = pages[pages.length - 1];
// For each element on that page, dispatch the action to update it
- return newPage.elements.forEach(element => dispatch(fetchRenderable(element)));
+ newPage.elements.forEach(element => dispatch(fetchRenderable(element)));
}
// This middleware clears any page selection when the writeable mode changes
if (action.type === setWriteable.toString() && oldIsWriteable !== isWriteable(getState())) {
const win = getWindow();
- if (typeof win.getSelection !== 'function') {
- return;
+ // check for browser feature before using it
+ if (typeof win.getSelection === 'function') {
+ win.getSelection().collapse(document.querySelector('body'), 0);
}
-
- win.getSelection().collapse(document.querySelector('body'), 0);
}
};