From e50efbf5ca24c8e817c7feee30d0bd3b8344e7c9 Mon Sep 17 00:00:00 2001 From: Jorge Date: Fri, 19 Oct 2018 13:26:49 +0100 Subject: [PATCH] Add "Text & Image" Block (#9416) This commit implements a block that contains two areas one for a media element (image/video) and one area for blocks (buttons, paragraphs etc). The elements of both areas are vertically aligned. Here instead of middle blocks, the parent block will self-contain the media area (that allows images or videos), so we have only one simple nested area for the content. --- packages/block-library/src/editor.scss | 1 + packages/block-library/src/index.js | 2 + packages/block-library/src/media-text/edit.js | 163 ++++++++++++++++++ .../block-library/src/media-text/editor.scss | 59 +++++++ .../block-library/src/media-text/index.js | 120 +++++++++++++ .../src/media-text/media-container.js | 125 ++++++++++++++ .../block-library/src/media-text/style.scss | 35 ++++ packages/block-library/src/style.scss | 1 + .../fixtures/core__media-text.html | 12 ++ .../fixtures/core__media-text.json | 32 ++++ .../fixtures/core__media-text.parsed.json | 27 +++ .../fixtures/core__media-text.serialized.html | 5 + .../core__media-text__image-alt-no-align.html | 12 ++ .../core__media-text__image-alt-no-align.json | 32 ++++ ...media-text__image-alt-no-align.parsed.json | 28 +++ ...a-text__image-alt-no-align.serialized.html | 5 + ..._media-text__media-right-custom-width.html | 12 ++ ..._media-text__media-right-custom-width.json | 33 ++++ ...text__media-right-custom-width.parsed.json | 31 ++++ ...__media-right-custom-width.serialized.html | 5 + .../fixtures/core__media-text__video.html | 12 ++ .../fixtures/core__media-text__video.json | 32 ++++ .../core__media-text__video.parsed.json | 27 +++ .../core__media-text__video.serialized.html | 5 + 24 files changed, 816 insertions(+) create mode 100644 packages/block-library/src/media-text/edit.js create mode 100644 packages/block-library/src/media-text/editor.scss create mode 100644 packages/block-library/src/media-text/index.js create mode 100644 packages/block-library/src/media-text/media-container.js create mode 100644 packages/block-library/src/media-text/style.scss create mode 100644 test/integration/full-content/fixtures/core__media-text.html create mode 100644 test/integration/full-content/fixtures/core__media-text.json create mode 100644 test/integration/full-content/fixtures/core__media-text.parsed.json create mode 100644 test/integration/full-content/fixtures/core__media-text.serialized.html create mode 100644 test/integration/full-content/fixtures/core__media-text__image-alt-no-align.html create mode 100644 test/integration/full-content/fixtures/core__media-text__image-alt-no-align.json create mode 100644 test/integration/full-content/fixtures/core__media-text__image-alt-no-align.parsed.json create mode 100644 test/integration/full-content/fixtures/core__media-text__image-alt-no-align.serialized.html create mode 100644 test/integration/full-content/fixtures/core__media-text__media-right-custom-width.html create mode 100644 test/integration/full-content/fixtures/core__media-text__media-right-custom-width.json create mode 100644 test/integration/full-content/fixtures/core__media-text__media-right-custom-width.parsed.json create mode 100644 test/integration/full-content/fixtures/core__media-text__media-right-custom-width.serialized.html create mode 100644 test/integration/full-content/fixtures/core__media-text__video.html create mode 100644 test/integration/full-content/fixtures/core__media-text__video.json create mode 100644 test/integration/full-content/fixtures/core__media-text__video.parsed.json create mode 100644 test/integration/full-content/fixtures/core__media-text__video.serialized.html diff --git a/packages/block-library/src/editor.scss b/packages/block-library/src/editor.scss index 58c8803144a312..31c6eccaf829c1 100644 --- a/packages/block-library/src/editor.scss +++ b/packages/block-library/src/editor.scss @@ -14,6 +14,7 @@ @import "./image/editor.scss"; @import "./latest-comments/editor.scss"; @import "./latest-posts/editor.scss"; +@import "./media-text/editor.scss"; @import "./list/editor.scss"; @import "./more/editor.scss"; @import "./nextpage/editor.scss"; diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js index 01644d7bd20d65..ecb354f8c373e5 100644 --- a/packages/block-library/src/index.js +++ b/packages/block-library/src/index.js @@ -28,6 +28,7 @@ import * as cover from './cover'; import * as embed from './embed'; import * as file from './file'; import * as html from './html'; +import * as mediaText from './media-text'; import * as latestComments from './latest-comments'; import * as latestPosts from './latest-posts'; import * as list from './list'; @@ -76,6 +77,7 @@ export const registerCoreBlocks = () => { file, window.wp && window.wp.oldEditor ? classic : null, // Only add the classic block in WP Context html, + mediaText, latestComments, latestPosts, missing, diff --git a/packages/block-library/src/media-text/edit.js b/packages/block-library/src/media-text/edit.js new file mode 100644 index 00000000000000..7570b2cc7b0530 --- /dev/null +++ b/packages/block-library/src/media-text/edit.js @@ -0,0 +1,163 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { + BlockControls, + InnerBlocks, + InspectorControls, + PanelColorSettings, + withColors, +} from '@wordpress/editor'; +import { Component, Fragment } from '@wordpress/element'; +import { Toolbar } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import MediaContainer from './media-container'; + +/** + * Constants + */ +const ALLOWED_BLOCKS = [ 'core/button', 'core/paragraph', 'core/heading', 'core/list' ]; +const TEMPLATE = [ + [ 'core/paragraph', { fontSize: 'large', placeholder: 'Content…' } ], +]; + +class MediaTextEdit extends Component { + constructor() { + super( ...arguments ); + + this.onSelectMedia = this.onSelectMedia.bind( this ); + this.onWidthChange = this.onWidthChange.bind( this ); + this.commitWidthChange = this.commitWidthChange.bind( this ); + this.state = { + mediaWidth: null, + }; + } + + onSelectMedia( media ) { + const { setAttributes } = this.props; + + let mediaType; + // for media selections originated from a file upload. + if ( media.media_type ) { + if ( media.media_type === 'image' ) { + mediaType = 'image'; + } else { + // only images and videos are accepted so if the media_type is not an image we can assume it is a video. + // video contain the media type of 'file' in the object returned from the rest api. + mediaType = 'video'; + } + } else { // for media selections originated from existing files in the media library. + mediaType = media.type; + } + + setAttributes( { + mediaAlt: media.alt, + mediaId: media.id, + mediaType, + mediaUrl: media.url, + } ); + } + + onWidthChange( width ) { + this.setState( { + mediaWidth: width, + } ); + } + + commitWidthChange( width ) { + const { setAttributes } = this.props; + + setAttributes( { + mediaWidth: width, + } ); + this.setState( { + mediaWidth: null, + } ); + } + + renderMediaArea() { + const { attributes } = this.props; + const { mediaAlt, mediaId, mediaPosition, mediaType, mediaUrl, mediaWidth } = attributes; + + return ( + + ); + } + + render() { + const { + attributes, + className, + backgroundColor, + setAttributes, + setBackgroundColor, + } = this.props; + const { mediaPosition, mediaWidth } = attributes; + const temporaryMediaWidth = this.state.mediaWidth; + const classNames = classnames( className, { + 'has-media-on-the-right': 'right' === mediaPosition, + [ backgroundColor.class ]: backgroundColor.class, + } ); + const widthString = `${ temporaryMediaWidth || mediaWidth }%`; + const style = { + gridTemplateColumns: 'right' === mediaPosition ? `auto ${ widthString }` : `${ widthString } auto`, + backgroundColor: backgroundColor.color, + }; + const colorSettings = [ { + value: backgroundColor.color, + onChange: setBackgroundColor, + label: __( 'Background Color' ), + } ]; + const toolbarControls = [ { + icon: 'align-pull-left', + title: __( 'Show media on left' ), + isActive: mediaPosition === 'left', + onClick: () => setAttributes( { mediaPosition: 'left' } ), + }, { + icon: 'align-pull-right', + title: __( 'Show media on right' ), + isActive: mediaPosition === 'right', + onClick: () => setAttributes( { mediaPosition: 'right' } ), + } ]; + return ( + + + + + + + +
+ { this.renderMediaArea() } + +
+
+ ); + } +} + +export default withColors( 'backgroundColor' )( MediaTextEdit ); diff --git a/packages/block-library/src/media-text/editor.scss b/packages/block-library/src/media-text/editor.scss new file mode 100644 index 00000000000000..b0eccc5aace159 --- /dev/null +++ b/packages/block-library/src/media-text/editor.scss @@ -0,0 +1,59 @@ +.wp-block-media-text, +.wp-block-media-text.aligncenter { + grid-template-areas: + "media-text-media media-text-content" + "resizer resizer"; +} + +.wp-block-media-text.has-media-on-the-right, +.wp-block-media-text.has-media-on-the-right.aligncenter { + grid-template-areas: + "media-text-content media-text-media" + "resizer resizer"; +} + +.wp-block-media-text .__resizable_base__ { + grid-area: resizer; +} + +.wp-block-media-text .editor-media-container__resizer { + grid-area: media-text-media; + align-self: center; + // The resizer sets a inline width but as we are using a grid layout, + // we set the width on container. + width: 100% !important; +} + +.wp-block-media-text .editor-inner-blocks { + word-break: break-word; + grid-area: media-text-content; + text-align: initial; + padding: 0 8% 0 8%; +} + +.wp-block-media-text > .editor-inner-blocks > .editor-block-list__layout > .editor-block-list__block { + max-width: unset; +} + +figure.block-library-media-text__media-container { + margin: 0; + height: 100%; + width: 100%; +} + +.wp-block-media-text .block-library-media-text__media-container img, +.wp-block-media-text .block-library-media-text__media-container video { + margin-bottom: -8px; + width: 100%; +} + +.editor-media-container__resizer .components-resizable-box__handle { + display: none; +} + +.editor-block-list__block.is-selected, +.editor-block-list__block.is-focused { + .editor-media-container__resizer .components-resizable-box__handle { + display: block; + } +} diff --git a/packages/block-library/src/media-text/index.js b/packages/block-library/src/media-text/index.js new file mode 100644 index 00000000000000..ed08b7b551c29a --- /dev/null +++ b/packages/block-library/src/media-text/index.js @@ -0,0 +1,120 @@ +/** + * External dependencies + */ +import { noop } from 'lodash'; +import classnames from 'classnames'; + +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { + InnerBlocks, + getColorClassName, +} from '@wordpress/editor'; + +/** + * Internal dependencies + */ +import edit from './edit'; + +const DEFAULT_MEDIA_WIDTH = 50; + +export const name = 'core/media-text'; + +export const settings = { + title: __( 'Media & Text' ), + + icon: , + + category: 'layout', + + keywords: [ __( 'image' ), __( 'video' ), __( 'half' ) ], + + attributes: { + align: { + type: 'string', + default: 'wide', + }, + backgroundColor: { + type: 'string', + }, + customBackgroundColor: { + type: 'string', + }, + mediaAlt: { + type: 'string', + source: 'attribute', + selector: 'figure img', + attribute: 'alt', + default: '', + }, + mediaPosition: { + type: 'string', + default: 'left', + }, + mediaId: { + type: 'number', + }, + mediaUrl: { + type: 'string', + source: 'attribute', + selector: 'figure video,figure img', + attribute: 'src', + }, + mediaType: { + type: 'string', + }, + mediaWidth: { + type: 'number', + default: 50, + }, + }, + + supports: { + align: [ 'wide', 'full' ], + }, + + edit, + + save( { attributes } ) { + const { + backgroundColor, + customBackgroundColor, + mediaAlt, + mediaPosition, + mediaType, + mediaUrl, + mediaWidth, + } = attributes; + const mediaTypeRenders = { + image: () => {, + video: () =>