Skip to content

Commit

Permalink
Try/even better previews (#16873)
Browse files Browse the repository at this point in the history
* First stab at a proof of concept.

* Attempt to programmatically determine dest dimensions

* Add README stub.

* Updates to use default args for width and height

Addresses #16113 (comment)

* Update class namespaces to match package

Addresses #16113 (comment)

* Add width and height as Effect Hook deps

Addresses #16113 (comment)

* Use correct `createRef` from @wordpress/element

Was previously using React createRef.

Addresses #16113 (comment)

* Revert "Update class namespaces to match package"

This reverts commit 24252fa887986548839e7d9a6fe2639807ed0bc6.

* Migrate to useRef hook

* Adds basic unit test

Still a WIP

* Refactor to avoid unecessary state and simplify implementation of scaling

* Add some padding to the preview

Avoids Blocks butting up against the edges of the preview, especially if the preview wrapper has rounded corners which can cause the Blocks to be clipped.

* Refactor to utilise JS template strings

* WIP: Adds zoom for small individual blocks

With small individual Blocks showing the preview at “actual size” (even when scaled to fit) is a bit odd. Everything still looks very zoomed out and far away.

Updates to check DOM for largest element within the Block and use that width to calculate a “zoomed” scale value so that the contents of the Block itself is shown zoomed in.

Still WIP

* Updates to target block contents more reliably and to calc entire box model in width

* block-preview: render scaled preview in a shadow dom

* try: setting styles to shadow dom elements

* Revert "try: setting styles to shadow dom elements"

This reverts commit ea573f9.

The reason being that there is no benefit of using shadow DOM and it introduces additional complexity.

* Removes attempt at auto zoom on Block contents

After much experimenting we’ve decided that attempting to reliably detect the visual size of the Block’s visual contents is impossible. This is due to inconsistencies in the DOM markup of each Block.

See #16113 (comment)

* Updates to remove scale by default in favour of opt in via prop

As discussed here #16113 (comment), there is little point in having thumbnail preview and large popover preview showing the same thing.

Update to introduce a prop to opt in to scaling the preview. By default the preview is no auto-scaled.

Apply `true` value to `BlockPreview` to ensure that Block Styles popover preview is scaled to fit.

* Adds scaleFactor prop to provide control for non dynamically scaled preview

* Adjust scaleFactor for Block Styles preview

* Fix to ensure hooks are called unconditionally to obey rules of Hooks

See https://reactjs.org/docs/hooks-rules.html

* Removes padding and border from preview on designer advice

* Only apply dimensions to preview content when dynamically scaling

* block-preview: a different approach attempt.

* Correct typo in util function name

* Seek the firstChild of the Block DOM node

* Tidy up effect timers and improve comments

* Dynamically calculate widths and offsets of preview container

Removes hardcoded values.

* Updates to account for widths of all Blocks passed into to preview

Preivously we only considered a single Block scenario. However, the recent addition of multi block support for Previews means we need to ensure we’re measuring the widths of _all_ Blocks.

* Revert class additions to Blocks

* Adds optional scope to DOM utils. Use to limit scope in Block width comparison.

* Update packages/block-editor/src/components/block-preview/index.js

Co-Authored-By: Riad Benguella <benguella@gmail.com>

* Allow prop based opt out from scaling

* Fix to ensure smallest Block is found even if smaller than container

Preivously we were default to the container width being the largest item. In fact we always want to determine the largest Block contents element and use that to determine the scale.

* Remove unecessary Math.min usage

* core/button: adjust dims of button wrapper

* apply vertical alignment

* set scale factor to 0.9

* rollback testing purpose comment

* cpre/button: keep adjusting styles for preview

* rename vars and css class names

* adjust rebase

* restore isScaled factor scale

* set the scale adjustment in the proper place

* update README file

* core/button: tidy edit preview styles

* coer/button: tweak button preview

* preview-block: ensure make the preview visible.

* block-preview: hide dropzone

* core/button: adjust preview for thumb and full sizes

* core/button: set same width for preview

* core/button: because it's !important

* core/button: set nowrap button in preview

* core/button: set nowrap onlu for button

* core/quote: adjust quote size

* Make previews overflow to the bottom.

Currently still has a bug where the large `.block-editor-block-switcher__preview` pane behaves like it's previewing a taller block, when it's not

* Revert "core/button: set nowrap onlu for button"

It breaks previews of long buttons

This reverts commit 758468c.

* Fix unit test

* core/button: apply nowrap only to buttons

* Try a fixed canvas width

* Fix blocks editor styles

* core/button: centering preview

* core/button: adjust only into teh styles preview

* block-preview: remove scaleAdjustment property

* block-preview: hide inserte element in preview

* block-preview: just pick up the first block to scale

* block-preview: fix set tall class. X position (wip)

* popover: remove commented lines

* Refactor the preview

* Remove debug code

* simplify preview resets

* Fix the preview recomputing

* Vertical alignining small blocks

* block-editor: restore viewportWidth as a property

* Update Readme docs

Props @marekhrabe.

* Remove tests for now

We'll revisit when we have a better idea what specifically we should be testing

* block-preview: simplify comparision

* Remove readme updates that break tests

* Update docs properly.

See 6f29c27
  • Loading branch information
retrofox authored and gziolo committed Aug 29, 2019
1 parent 89f75d1 commit eef7bc6
Show file tree
Hide file tree
Showing 9 changed files with 217 additions and 82 deletions.
9 changes: 7 additions & 2 deletions packages/block-editor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,16 @@ Undocumented declaration.

<a name="BlockPreview" href="#BlockPreview">#</a> **BlockPreview**

BlockPreview renders a preview given an array of blocks.
BlockPreview renders a preview of a block or array of blocks.

_Related_

- <https://github.com/WordPress/gutenberg/blob/master/packages/block-editor/src/components/block-preview/README.md>

_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_

Expand Down
14 changes: 8 additions & 6 deletions packages/block-editor/src/components/block-preview/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,22 @@ Render the component passing in the required props:
```jsx
<BlockPreview
blocks={ blocks }
isScaled={ false }
viewportWidth={ 800 }
/>
```

## 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.
129 changes: 106 additions & 23 deletions packages/block-editor/src/components/block-preview/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<Disabled
aria-hidden
className={
classnames(
className,
'block-editor-block-preview',
'editor-styles-wrapper',
{
'is-scaled': isScaled,
}
)
}
<div ref={ previewRef } className="block-editor-block-preview__container" aria-hidden>
<Disabled style={ previewStyles } className={ contentClassNames }>
<BlockList />
</Disabled>
</div>
);
}

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 (
<BlockEditorProvider
value={ renderedBlocks }
settings={ settings }
>
<BlockEditorProvider
value={ castArray( blocks ) }
settings={ settings }
>
<BlockList renderAppender={ false } />
</BlockEditorProvider>
</Disabled>
{
/*
* The key prop is used to force recomputing the preview
* by remounting the component, ScaledBlockPreview is not meant to
* be rerendered.
*/
}
<ScaledBlockPreview
key={ recompute }
blocks={ renderedBlocks }
viewportWidth={ viewportWidth }
/>
</BlockEditorProvider>
);
}

/**
* 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 ) => {
Expand Down
77 changes: 65 additions & 12 deletions packages/block-editor/src/components/block-preview/style.scss
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
7 changes: 1 addition & 6 deletions packages/block-editor/src/components/block-styles/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,7 @@ function BlockStyles( {
aria-label={ style.label || style.name }
>
<div className="editor-block-styles__item-preview block-editor-block-styles__item-preview">
<BlockPreview
isScaled
blocks={ cloneBlock( block, {
className: styleClassName,
} ) }
/>
<BlockPreview blocks={ cloneBlock( block, { className: styleClassName } ) } />
</div>
<div className="editor-block-styles__item-label block-editor-block-styles__item-label">
{ style.label || style.name }
Expand Down
24 changes: 9 additions & 15 deletions packages/block-editor/src/components/block-styles/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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%;
}
}

Expand Down
15 changes: 13 additions & 2 deletions packages/block-editor/src/utils/dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand Down
4 changes: 4 additions & 0 deletions packages/block-library/src/button/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading

0 comments on commit eef7bc6

Please sign in to comment.