From 2f114fbc4e97793dee329b84bde03a125109a4a4 Mon Sep 17 00:00:00 2001 From: Catherine Liu Date: Fri, 30 Mar 2018 14:08:18 -0700 Subject: [PATCH] Page Preview Thumbnail (#375) * Created dom_preview and page_preview components * temporarily display page number on non active pages. Need to fix page thumbnail width issue * Added page number to page controls * Rendered preview for all pages instead of just the active one * Fixed background color on thumbnails. Added box-shadow to active page * Removed background color from page controls * Changed background color on page controls * Changed prop 'id' to 'elementId' in dom_preview * Removed unused style in page_manager.less * Removed jQuery from dom_preview * Updated styles for page preview * Added text styles back to page-index * Removed unused code * Hides element controls in page preview * Removed debounce for page preview update * Changes propTypes and observer to static variables. Disconnects observer on unmount * Added radix to parseInt in dom_preview * Added debounce to update function in dom_preview * Updated styles for page manager. References css variables instead of hard-coded values --- public/components/dom_preview/dom_preview.js | 86 +++++++++++++++++++ public/components/dom_preview/index.js | 3 + .../element_wrapper/element_controls.js | 1 + .../components/page_manager/page_manager.js | 24 +++--- .../components/page_manager/page_manager.less | 47 +++++++--- public/components/page_preview/index.js | 4 + .../page_controls.js | 5 +- .../components/page_preview/page_preview.js | 59 +++++++++++++ public/components/page_stack/page_stack.js | 1 + 9 files changed, 205 insertions(+), 25 deletions(-) create mode 100644 public/components/dom_preview/dom_preview.js create mode 100644 public/components/dom_preview/index.js create mode 100644 public/components/page_preview/index.js rename public/components/{page_manager => page_preview}/page_controls.js (79%) create mode 100644 public/components/page_preview/page_preview.js diff --git a/public/components/dom_preview/dom_preview.js b/public/components/dom_preview/dom_preview.js new file mode 100644 index 0000000000000..c8996ec6e7160 --- /dev/null +++ b/public/components/dom_preview/dom_preview.js @@ -0,0 +1,86 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { debounce } from 'lodash'; + +export class DomPreview extends React.Component { + static container = null; + static content = null; + static observer = null; + + static propTypes = { + elementId: PropTypes.string.isRequired, + height: PropTypes.number.isRequired, + setPagePreviewWidth: PropTypes.func, + }; + + componentDidMount() { + const original = document.querySelector(`#${this.props.elementId}`); + + const update = this.update(original); + update(); + + const slowUpdate = debounce(update, 250); + + this.observer = new MutationObserver(slowUpdate); + // configuration of the observer: + const config = { attributes: true, childList: true, subtree: true }; + // pass in the target node, as well as the observer options + this.observer.observe(original, config); + } + + componentWillUnmount() { + this.observer.disconnect(); + } + + update = original => () => { + const thumb = original.cloneNode(true); + + const originalStyle = window.getComputedStyle(original, null); + const originalWidth = parseInt(originalStyle.getPropertyValue('width'), 10); + const originalHeight = parseInt(originalStyle.getPropertyValue('height'), 10); + + const thumbHeight = this.props.height; + const scale = thumbHeight / originalHeight; + const thumbWidth = originalWidth * scale; + + if (this.content) { + if (this.content.hasChildNodes()) this.content.removeChild(this.content.firstChild); + this.content.appendChild(thumb); + } + // Copy canvas data + const originalCanvas = original.querySelectorAll('canvas'); + const thumbCanvas = thumb.querySelectorAll('canvas'); + + // Cloned canvas elements are blank and need to be explicitly redrawn + if (originalCanvas.length > 0) { + Array.from(originalCanvas).map((img, i) => + thumbCanvas[i].getContext('2d').drawImage(img, 0, 0) + ); + } + + this.props.setPagePreviewWidth(thumbWidth + 2); + + this.container.style.cssText = `width: ${thumbWidth}; height: ${thumbHeight}; overflow: 'hidden',`; + + this.content.style.cssText = `transform: scale(${scale}); transform-origin: top left; height: ${originalHeight}; width: ${originalWidth};`; + + thumb.style.cssText = 'top: 0; left: 0;'; + }; + + render() { + return ( +
{ + this.container = container; + }} + className="dom-preview" + > +
{ + this.content = content; + }} + /> +
+ ); + } +} diff --git a/public/components/dom_preview/index.js b/public/components/dom_preview/index.js new file mode 100644 index 0000000000000..0b0f9b3a5e69d --- /dev/null +++ b/public/components/dom_preview/index.js @@ -0,0 +1,3 @@ +import { DomPreview as Component } from './dom_preview'; + +export const DomPreview = Component; diff --git a/public/components/element_wrapper/element_controls.js b/public/components/element_wrapper/element_controls.js index 227de7a2cc679..fe8ee2f49f59c 100644 --- a/public/components/element_wrapper/element_controls.js +++ b/public/components/element_wrapper/element_controls.js @@ -52,6 +52,7 @@ export const ElementControls = pure(props => { )}
diff --git a/public/components/page_manager/page_manager.js b/public/components/page_manager/page_manager.js index 76acab2a13c4b..d0e6eb7201e1e 100644 --- a/public/components/page_manager/page_manager.js +++ b/public/components/page_manager/page_manager.js @@ -1,9 +1,8 @@ import React from 'react'; import PropTypes from 'prop-types'; import { ConfirmModal } from '../confirm_modal'; -import { RemoveIcon } from '../remove_icon'; import { Link } from '../link'; -import { PageControls } from './page_controls'; +import { PagePreview } from '../page_preview'; import './page_manager.less'; export class PageManager extends React.PureComponent { @@ -39,14 +38,15 @@ export class PageManager extends React.PureComponent { params={{ id: workpadId, page: pageNumber }} aria-label={`Load page number ${pageNumber}`} > -
- -
{pageNumber}
- -
+ ); }; @@ -58,8 +58,8 @@ export class PageManager extends React.PureComponent {
{pages.map(this.renderPage)} -
-
+
+
+
diff --git a/public/components/page_manager/page_manager.less b/public/components/page_manager/page_manager.less index 2fa14113f99d8..e9943dce45577 100644 --- a/public/components/page_manager/page_manager.less +++ b/public/components/page_manager/page_manager.less @@ -1,18 +1,21 @@ -@import (reference) "../../style/variables"; +@import (reference) '../../style/variables'; .canvas__page-manager { .canvas__page-manager--pages { - white-space: nowrap; overflow-y: hidden; overflow-x: auto; - max-height: 148px; + max-height: @spacingXXXL * 3; padding-top: @spacingS; margin-bottom: @spacingS; } + .canvas__page-manager--pages a { + box-shadow: none; + padding-left: @spacingXS; + } + .canvas__page-manager--page.active { - background-color: @trueWhite !important; - color: @textMain !important; + box-shadow: 0 0 1px 2px @blue; } .canvas__page-manager--page:hover { @@ -21,7 +24,6 @@ } .canvas__page-manager--page { - background-color: @darkGrey; position: relative; color: @mediumGrey; border: @borderStyle; @@ -29,8 +31,8 @@ vertical-align: top; font-weight: bold; text-align: center; - width: 77px; - height: 100px; + width: @spacingXXXXL; + height: @spacingXXXL * 2; margin: 0 @spacingM @spacingS 0; cursor: pointer; @@ -41,6 +43,8 @@ position: absolute; text-align: center; width: 100%; + background-color: @darkestGrey; + opacity: 0.75; > * { padding: @spacingXS; @@ -68,8 +72,8 @@ .canvas__page-manager--page-remove { display: none; position: absolute; - top: -8px; - right: -12px; + top: -@spacingS; + right: -@spacingXS * 3; } .canvas__page-manager--page-controls { @@ -77,8 +81,27 @@ } .canvas__page-manager--page-index { - line-height: 100px; - font-size: @textLarge; + overflow: hidden; + height: @spacingXXXL * 2; + } + + .canvas--interactable-actions, + .canvas--interactable-meta, + .canvas--interactable-resize, + .canvas__element--remove-icon { + display: none; + } + } + + .canvas__page-manager--page-add { + display: inline-block; + line-height: @spacingXXL * 2; + font-size: @textXXLarge; + padding: 0 @spacingS; + color: @mediumGrey; + + &:hover { + color: @trueWhite; } } } diff --git a/public/components/page_preview/index.js b/public/components/page_preview/index.js new file mode 100644 index 0000000000000..5605f3c3f5520 --- /dev/null +++ b/public/components/page_preview/index.js @@ -0,0 +1,4 @@ +import { compose, withState } from 'recompose'; +import { PagePreview as Component } from './page_preview'; + +export const PagePreview = compose(withState('width', 'setWidth', 77))(Component); diff --git a/public/components/page_manager/page_controls.js b/public/components/page_preview/page_controls.js similarity index 79% rename from public/components/page_manager/page_controls.js rename to public/components/page_preview/page_controls.js index e68f3d5ef26ea..d0fe5160155ad 100644 --- a/public/components/page_manager/page_controls.js +++ b/public/components/page_preview/page_controls.js @@ -1,8 +1,10 @@ import React from 'react'; import PropTypes from 'prop-types'; -export const PageControls = ({ pageId, duplicatePage, movePage }) => ( +export const PageControls = ({ pageId, pageNumber, duplicatePage, movePage }) => (
+ {`${pageNumber}`} +
movePage(pageId, -1)} @@ -17,6 +19,7 @@ export const PageControls = ({ pageId, duplicatePage, movePage }) => ( PageControls.propTypes = { pageId: PropTypes.string.isRequired, + pageNumber: PropTypes.number.isRequired, duplicatePage: PropTypes.func.isRequired, movePage: PropTypes.func.isRequired, }; diff --git a/public/components/page_preview/page_preview.js b/public/components/page_preview/page_preview.js new file mode 100644 index 0000000000000..794667a989c21 --- /dev/null +++ b/public/components/page_preview/page_preview.js @@ -0,0 +1,59 @@ +import React, { PureComponent } from 'react'; +import PropTypes from 'prop-types'; +import { DomPreview } from '../dom_preview'; +import { RemoveIcon } from '../remove_icon'; +import { PageControls } from './page_controls'; + +export class PagePreview extends PureComponent { + static propTypes = { + page: PropTypes.object.isRequired, + height: PropTypes.number.isRequired, + pageNumber: PropTypes.number.isRequired, + movePage: PropTypes.func, + duplicatePage: PropTypes.func, + selectedPage: PropTypes.string, + confirmDelete: PropTypes.func, + width: PropTypes.number, + setWidth: PropTypes.func, + }; + + render() { + const { + page, + pageNumber, + height, + selectedPage, + movePage, + duplicatePage, + confirmDelete, + width, + setWidth, + } = this.props; + + return ( +
+
+ setWidth(width)} + /> +
+ + +
+ ); + } +} diff --git a/public/components/page_stack/page_stack.js b/public/components/page_stack/page_stack.js index f4e33de53c664..285d46bd28b3c 100644 --- a/public/components/page_stack/page_stack.js +++ b/public/components/page_stack/page_stack.js @@ -7,6 +7,7 @@ export const PageStack = ({ pages, selectedPageId, height, width }) => { return pages.map(page => (