diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md index 477c8657e40db..1d230d45fc3a2 100644 --- a/packages/block-editor/README.md +++ b/packages/block-editor/README.md @@ -124,11 +124,16 @@ Undocumented declaration. # **BlockPreview** -BlockPreview renders a preview given an array of blocks. +BlockPreview renders a preview of a block or array of blocks. + +_Related_ + +- _Parameters_ -- _props_ `Object`: Component props. +- _blocks_ `(Array|Object)`: A block instance (object) or an array of blocks to be previewed. +- _viewportWidth_ `number`: Width of the preview container in pixels. Controls at what size the blocks will be rendered inside the preview. Default: 700. _Returns_ diff --git a/packages/block-editor/src/components/block-preview/README.md b/packages/block-editor/src/components/block-preview/README.md index 10aaec2dd4001..b415f2bbc2237 100644 --- a/packages/block-editor/src/components/block-preview/README.md +++ b/packages/block-editor/src/components/block-preview/README.md @@ -10,20 +10,22 @@ Render the component passing in the required props: ```jsx ``` ## Props ### `blocks` -* **Type:** `array|object` +* **Type:** `Array|Object` * **Default:** `undefined` A block instance (object) or a blocks array you would like to render a preview. -### `isScaled` -* **Type:** `Boolean` -* **Default:** `false` +### `viewportWidth` +* **Type:** `Int` +* **Default:** `700` -Use this if you need to render previews in smaller areas, like block thumbnails. +Width of the preview container in pixels. Controls at what size the blocks will be rendered inside the preview. + +`viewportWidth` can be used to simulate how blocks look on different device sizes or to make sure make sure multiple previews will be rendered with the same scale, regardless of their content. diff --git a/packages/block-editor/src/components/block-preview/index.js b/packages/block-editor/src/components/block-preview/index.js index b63fd4abbf7dd..d8235eea0c1dd 100644 --- a/packages/block-editor/src/components/block-preview/index.js +++ b/packages/block-editor/src/components/block-preview/index.js @@ -9,46 +9,129 @@ import classnames from 'classnames'; */ import { Disabled } from '@wordpress/components'; import { withSelect } from '@wordpress/data'; +import { useLayoutEffect, useState, useRef, useReducer, useMemo } from '@wordpress/element'; /** * Internal dependencies */ import BlockEditorProvider from '../provider'; import BlockList from '../block-list'; +import { getBlockPreviewContainerDOMNode } from '../../utils/dom'; -export function BlockPreview( { blocks, settings, className, isScaled } ) { - if ( ! blocks ) { +function ScaledBlockPreview( { blocks, viewportWidth } ) { + const previewRef = useRef( null ); + + const [ isTallPreview, setIsTallPreview ] = useState( false ); + const [ isReady, setIsReady ] = useState( false ); + const [ previewScale, setPreviewScale ] = useState( 1 ); + const [ { x, y }, setPosition ] = useState( { x: 0, y: 0 } ); + + // Dynamically calculate the scale factor + useLayoutEffect( () => { + // Timer - required to account for async render of `BlockEditorProvider` + const timerId = setTimeout( () => { + const containerElement = previewRef.current; + if ( ! containerElement ) { + return; + } + + // If we're previewing a single block, scale the preview to fit it. + if ( blocks.length === 1 ) { + const block = blocks[ 0 ]; + const previewElement = getBlockPreviewContainerDOMNode( block.clientId, containerElement ); + if ( ! previewElement ) { + return; + } + + const containerElementRect = containerElement.getBoundingClientRect(); + const scaledElementRect = previewElement.getBoundingClientRect(); + + const scale = containerElementRect.width / scaledElementRect.width || 1; + const offsetX = scaledElementRect.left - containerElementRect.left; + const offsetY = ( containerElementRect.height > scaledElementRect.height * scale ) ? + ( containerElementRect.height - ( scaledElementRect.height * scale ) ) / 2 : 0; + + setIsTallPreview( scaledElementRect.height * scale > containerElementRect.height ); + setPreviewScale( scale ); + setPosition( { x: offsetX * scale, y: offsetY } ); + + // Hack: we need to reset the scaled elements margins + previewElement.style.marginTop = '0'; + } else { + const containerElementRect = containerElement.getBoundingClientRect(); + setPreviewScale( containerElementRect.width / viewportWidth ); + setIsTallPreview( true ); + } + + setIsReady( true ); + }, 100 ); + + // Cleanup + return () => { + if ( timerId ) { + window.clearTimeout( timerId ); + } + }; + }, [] ); + + if ( ! blocks || blocks.length === 0 ) { return null; } + + const previewStyles = { + transform: `scale(${ previewScale })`, + visibility: isReady ? 'visible' : 'hidden', + left: -x, + top: y, + width: viewportWidth, + }; + + const contentClassNames = classnames( 'block-editor-block-preview__content editor-styles-wrapper', { + 'is-tall-preview': isTallPreview, + 'is-ready': isReady, + } ); + return ( - + + + + + ); +} + +export function BlockPreview( { blocks, viewportWidth = 700, settings } ) { + const renderedBlocks = useMemo( () => castArray( blocks ), [ blocks ] ); + const [ recompute, triggerRecompute ] = useReducer( ( state ) => state + 1, 0 ); + useLayoutEffect( triggerRecompute, [ blocks ] ); + return ( + - - - - + { + /* + * The key prop is used to force recomputing the preview + * by remounting the component, ScaledBlockPreview is not meant to + * be rerendered. + */ + } + + ); } /** - * BlockPreview renders a preview given an array of blocks. + * BlockPreview renders a preview of a block or array of blocks. * - * @param {Object} props Component props. + * @see https://github.com/WordPress/gutenberg/blob/master/packages/block-editor/src/components/block-preview/README.md * + * @param {Array|Object} blocks A block instance (object) or an array of blocks to be previewed. + * @param {number} viewportWidth Width of the preview container in pixels. Controls at what size the blocks will be rendered inside the preview. Default: 700. * @return {WPElement} Rendered element. */ export default withSelect( ( select ) => { diff --git a/packages/block-editor/src/components/block-preview/style.scss b/packages/block-editor/src/components/block-preview/style.scss index ff42e7b3b4dcc..76c2f4ccaf10a 100644 --- a/packages/block-editor/src/components/block-preview/style.scss +++ b/packages/block-editor/src/components/block-preview/style.scss @@ -1,34 +1,87 @@ -.block-editor-block-preview { +// This is the preview that shows up to the right of the thumbnail when hovering. +.block-editor-block-switcher__preview { padding: $block-padding; font-family: $editor-font; overflow: hidden; width: 100%; + pointer-events: none; + display: none; + + @include break-medium { + display: block; + } + + .block-editor-block-preview__content { + font-family: $editor-font; + + > div { + font-family: $editor-font; + } + + &:not(.is-tall-preview) { + // Vertical alignment. + margin-top: 50%; + } + } + + .block-editor-block-preview__title { + margin-bottom: 10px; + color: $dark-gray-300; + } +} + +// These rules ensure the preview scales smoothly regardless of the container size. +.block-editor-block-preview__container { + // In the component, a top padding is provided as an inline style to provid an aspect-ratio. + // This positioning enables the content to sit on top of that padding to fit. + position: relative; + + // The preview component measures the pixel width of this item, so as to calculate the scale factor. + // But without this baseline width, it collapses to 0. + width: 100%; +} + +.block-editor-block-preview__content { + // This element receives inline styles for width, height, and transform-scale. + // Those inline styles are calculated to fit a perfect thumbnail. + + // Position above the padding. + position: absolute; + + // Vertical alignment. It works with the transform: translate(-50%, -50%)`, + top: 0; + left: 0; + + // Important to set the origin. + transform-origin: top left; + + // Resetting paddings, margins, and other. + text-align: initial; + margin: 0; + overflow: visible; + min-height: auto; - // Resetting the block editor paddings and margins .block-editor-block-list__layout, .block-editor-block-list__block { padding: 0; } + .editor-block-list__block-edit [data-block] { - margin-top: 0; + margin: 0; } > div section { height: auto; } - > .reusable-block-indicator { - display: none; + &.is-tall-preview { + top: 4px; } + .block-editor-block-list__insertion-point, + .block-editor-block-drop-zone, + .reusable-block-indicator, .block-list-appender { display: none; } - - &.is-scaled { - > div { - transform: scale(0.9); - transform-origin: center top; - } - } } diff --git a/packages/block-editor/src/components/block-styles/index.js b/packages/block-editor/src/components/block-styles/index.js index f9270094e4708..c5f7797aa1db8 100644 --- a/packages/block-editor/src/components/block-styles/index.js +++ b/packages/block-editor/src/components/block-styles/index.js @@ -122,12 +122,7 @@ function BlockStyles( { aria-label={ style.label || style.name } >
- +
{ style.label || style.name } diff --git a/packages/block-editor/src/components/block-styles/style.scss b/packages/block-editor/src/components/block-styles/style.scss index 988e7563878fc..e5eb662d93b1f 100644 --- a/packages/block-editor/src/components/block-styles/style.scss +++ b/packages/block-editor/src/components/block-styles/style.scss @@ -12,6 +12,7 @@ overflow: hidden; border-radius: $radius-round-rectangle; padding: $grid-size-small * 1.5; + padding-top: calc(50% * 0.75 - #{ $grid-size-small } * 1.5); &:focus { @include block-style__focus(); @@ -30,28 +31,21 @@ } } +// Show a little preview thumbnail for style variations. .block-editor-block-styles__item-preview { outline: $border-width solid transparent; // Shown in Windows High Contrast mode. - border: 1px solid rgba($dark-gray-900, 0.2); - overflow: hidden; padding: 0; - text-align: initial; + border: $border-width solid rgba($dark-gray-900, 0.2); border-radius: $radius-round-rectangle; display: flex; - height: 60px; + overflow: hidden; background: $white; + padding-top: 75%; + margin-top: -75%; - // Actual preview contents. - .block-editor-block-preview__content { - transform: scale(0.7); - transform-origin: center center; - width: 100%; - - // Unset some of the styles that might be inherited from the editor style. - margin: 0; - padding: 0; - overflow: visible; - min-height: auto; + .block-editor-block-preview__container { + padding-top: 0; + margin-top: -75%; } } diff --git a/packages/block-editor/src/utils/dom.js b/packages/block-editor/src/utils/dom.js index f6d3328d2fd86..fd732b40c5318 100644 --- a/packages/block-editor/src/utils/dom.js +++ b/packages/block-editor/src/utils/dom.js @@ -4,11 +4,22 @@ * in cases where isolated behaviors need remote access to a block node. * * @param {string} clientId Block client ID. + * @param {Element} scope an optional DOM Element to which the selector should be scoped * * @return {Element} Block DOM node. */ -export function getBlockDOMNode( clientId ) { - return document.querySelector( '[data-block="' + clientId + '"]' ); +export function getBlockDOMNode( clientId, scope = document ) { + return scope.querySelector( '[data-block="' + clientId + '"]' ); +} + +export function getBlockPreviewContainerDOMNode( clientId, scope ) { + const domNode = getBlockDOMNode( clientId, scope ); + + if ( ! domNode ) { + return; + } + + return domNode.firstChild || domNode; } /** diff --git a/packages/block-library/src/button/edit.js b/packages/block-library/src/button/edit.js index b258486b98920..e70cc00b27854 100644 --- a/packages/block-library/src/button/edit.js +++ b/packages/block-library/src/button/edit.js @@ -33,6 +33,10 @@ import { const { getComputedStyle } = window; const applyFallbackStyles = withFallbackStyles( ( node, ownProps ) => { + if ( node ) { + node.classList.add( 'wp-block-button-wrapper' ); + } + const { textColor, backgroundColor } = ownProps; const backgroundColorValue = backgroundColor && backgroundColor.color; const textColorValue = textColor && textColor.color; diff --git a/packages/block-library/src/button/editor.scss b/packages/block-library/src/button/editor.scss index 9fe1a9afabd1b..da2116d2fc492 100644 --- a/packages/block-library/src/button/editor.scss +++ b/packages/block-library/src/button/editor.scss @@ -39,22 +39,6 @@ [data-rich-text-placeholder]::after { opacity: 0.8; } - - // Don't let the placeholder text wrap in the variation preview. - .block-editor-block-preview__content & { - max-width: 100%; - - .wp-block-button__link { - max-width: 100%; - overflow: hidden; - // Override is allowed here only because the rich text instance in - // a preview is not editable. - // To do: use the `save` function to preview a block transform, not - // the `edit` function. - white-space: nowrap !important; - text-overflow: ellipsis; - } - } } .wp-block-button__inline-link { @@ -91,3 +75,7 @@ margin-top: $grid-size-large; } } + +.wp-block-button-wrapper { + display: table; +}