diff --git a/assets/stylesheets/_z-index.scss b/assets/stylesheets/_z-index.scss index b9cd8c4032e0a..4139af987af5e 100644 --- a/assets/stylesheets/_z-index.scss +++ b/assets/stylesheets/_z-index.scss @@ -29,6 +29,8 @@ $z-layers: ( ".edit-post-header": 30, ".block-library-button__inline-link .editor-url-input__suggestions": 6, // URL suggestions for button block above sibling inserter ".block-library-image__resize-handlers": 1, // Resize handlers above sibling inserter + ".wp-block-cover__inner-container": 1, // InnerBlocks area inside cover block. + ".wp-block-cover__video-background": 0, // Video background inside cover block. // Side UI active buttons ".editor-block-mover__control": 1, diff --git a/packages/block-library/src/cover-image/index.js b/packages/block-library/src/cover-image/index.js index 2d06ec2112283..a69df89e6a921 100644 --- a/packages/block-library/src/cover-image/index.js +++ b/packages/block-library/src/cover-image/index.js @@ -23,6 +23,7 @@ import { withColors, getColorClassName, } from '@wordpress/editor'; +import deprecated from '@wordpress/deprecated'; const validAlignments = [ 'left', 'center', 'right', 'wide', 'full' ]; @@ -75,6 +76,10 @@ export const settings = { attributes: blockAttributes, + supports: { + inserter: false, + }, + transforms: { from: [ { @@ -132,6 +137,10 @@ export const settings = { withNotices, ] )( ( { attributes, setAttributes, isSelected, className, noticeOperations, noticeUI, overlayColor, setOverlayColor } ) => { + deprecated( 'The Cover Image block', { + alternative: 'the Cover block', + plugin: 'Gutenberg', + } ); const { url, title, align, contentAlign, id, hasParallax, dimRatio } = attributes; const updateAlignment = ( nextAlign ) => setAttributes( { align: nextAlign } ); const onSelectImage = ( media ) => { diff --git a/packages/block-library/src/cover-image/test/index.js b/packages/block-library/src/cover-image/test/index.js index ceb0bc9d5123c..270f59920b22c 100644 --- a/packages/block-library/src/cover-image/test/index.js +++ b/packages/block-library/src/cover-image/test/index.js @@ -9,5 +9,8 @@ describe( 'core/cover-image', () => { const wrapper = blockEditRender( name, settings ); expect( wrapper ).toMatchSnapshot(); + expect( console ).toHaveWarnedWith( + 'The Cover Image block is deprecated and will be removed. Please use the Cover block instead.' + ); } ); } ); diff --git a/packages/block-library/src/cover/edit.js b/packages/block-library/src/cover/edit.js new file mode 100644 index 0000000000000..671efcfff79fe --- /dev/null +++ b/packages/block-library/src/cover/edit.js @@ -0,0 +1,247 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +/** + * WordPress dependencies + */ +import { + IconButton, + PanelBody, + RangeControl, + ToggleControl, + Toolbar, + withNotices, +} from '@wordpress/components'; +import { Fragment, Component } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import { compose } from '@wordpress/compose'; +import { + BlockControls, + InspectorControls, + InnerBlocks, + MediaPlaceholder, + MediaUpload, + PanelColorSettings, + withColors, +} from '@wordpress/editor'; + +const INNER_BLOCKS_TEMPLATE = [ + [ 'core/paragraph', { + align: 'center', + fontSize: 'large', + placeholder: __( 'Write title…' ), + } ], +]; +const INNER_BLOCKS_ALLOWED_BLOCKS = [ + 'core/button', + 'core/heading', + 'core/paragraph', +]; +const ALLOWED_MEDIA_TYPES = [ 'image', 'video' ]; +export const IMAGE_BACKGROUND_TYPE = 'image'; +export const VIDEO_BACKGROUND_TYPE = 'video'; + +class CoverEdit extends Component { + constructor() { + super( ...arguments ); + this.onSelectMedia = this.onSelectMedia.bind( this ); + } + + onSelectMedia( media ) { + const { setAttributes } = this.props; + if ( ! media || ! media.url ) { + setAttributes( { url: undefined, id: undefined } ); + return; + } + let mediaType; + // for media selections originated from a file upload. + if ( media.media_type ) { + if ( media.media_type === IMAGE_BACKGROUND_TYPE ) { + mediaType = IMAGE_BACKGROUND_TYPE; + } 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_BACKGROUND_TYPE; + } + } else { // for media selections originated from existing files in the media library. + mediaType = media.type; + } + if ( mediaType ) { + setAttributes( { + url: media.url, + id: media.id, + backgroundType: mediaType, + } ); + return; + } + setAttributes( { url: media.url, id: media.id } ); + } + + render() { + const { + attributes, + className, + noticeOperations, + noticeUI, + overlayColor, + setAttributes, + setOverlayColor, + } = this.props; + + const { + backgroundType, + dimRatio, + hasParallax, + id, + url, + } = attributes; + + const toggleParallax = () => setAttributes( { + hasParallax: ! hasParallax, + } ); + const setDimRatio = ( ratio ) => setAttributes( { dimRatio: ratio } ); + + const style = { + ...( + backgroundType === IMAGE_BACKGROUND_TYPE ? + backgroundImageStyles( url ) : + {} + ), + backgroundColor: overlayColor.color, + }; + + const classes = classnames( + className, + dimRatioToClass( dimRatio ), + { + 'has-background-dim': dimRatio !== 0, + 'has-parallax': hasParallax, + } + ); + + const controls = ( + + { !! url && ( + + + ( + + ) } + /> + + + ) } + { !! url && ( + + + { IMAGE_BACKGROUND_TYPE === backgroundType && ( + + ) } + + + + + + ) } + + ); + + if ( ! url ) { + const icon = 'format-image'; + const label = ( 'Cover' ); + + return ( + + { controls } + + + ); + } + + return ( + + { controls } +
+ { VIDEO_BACKGROUND_TYPE === backgroundType && url && ( +
+
+ ); + } +} + +export default compose( [ + withColors( { overlayColor: 'background-color' } ), + withNotices, +] )( CoverEdit ); + +export function dimRatioToClass( ratio ) { + return ( ratio === 0 || ratio === 50 ) ? + null : + 'has-background-dim-' + ( 10 * Math.round( ratio / 10 ) ); +} + +export function backgroundImageStyles( url ) { + return url ? + { backgroundImage: `url(${ url })` } : + {}; +} diff --git a/packages/block-library/src/cover/editor.scss b/packages/block-library/src/cover/editor.scss new file mode 100644 index 0000000000000..2b7649aea7bfa --- /dev/null +++ b/packages/block-library/src/cover/editor.scss @@ -0,0 +1,3 @@ +.wp-block-cover__inner-container .editor-block-list__block { + color: inherit; +} diff --git a/packages/block-library/src/cover/index.js b/packages/block-library/src/cover/index.js new file mode 100644 index 0000000000000..aeaad2b73e189 --- /dev/null +++ b/packages/block-library/src/cover/index.js @@ -0,0 +1,118 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { + InnerBlocks, + getColorClassName, +} from '@wordpress/editor'; + +const blockAttributes = { + title: { + source: 'html', + selector: 'p', + }, + url: { + type: 'string', + }, + id: { + type: 'number', + }, + hasParallax: { + type: 'boolean', + default: false, + }, + dimRatio: { + type: 'number', + default: 50, + }, + overlayColor: { + type: 'string', + }, + customOverlayColor: { + type: 'string', + }, + backgroundType: { + type: 'string', + default: 'image', + }, +}; + +export const name = 'core/cover'; + +import { + default as CoverEdit, + IMAGE_BACKGROUND_TYPE, + VIDEO_BACKGROUND_TYPE, + dimRatioToClass, + backgroundImageStyles, +} from './edit'; + +export const settings = { + title: __( 'Cover' ), + + description: __( 'Add a full-width image or video, and layer text over it — great for headers.' ), + + icon: , + + category: 'common', + + attributes: blockAttributes, + + supports: { + align: true, + }, + + edit: CoverEdit, + + save( { attributes, className } ) { + const { + backgroundType, + customOverlayColor, + dimRatio, + hasParallax, + overlayColor, + url, + } = attributes; + const overlayColorClass = getColorClassName( + 'background-color', + overlayColor + ); + const style = backgroundType === IMAGE_BACKGROUND_TYPE ? + backgroundImageStyles( url ) : + {}; + if ( ! overlayColorClass ) { + style.backgroundColor = customOverlayColor; + } + + const classes = classnames( + className, + dimRatioToClass( dimRatio ), + overlayColorClass, + { + 'has-background-dim': dimRatio !== 0, + 'has-parallax': hasParallax, + }, + ); + + return ( +
+ { VIDEO_BACKGROUND_TYPE === backgroundType && url && (
+ ); + }, +}; diff --git a/packages/block-library/src/cover/style.scss b/packages/block-library/src/cover/style.scss new file mode 100644 index 0000000000000..99cca0bd6d342 --- /dev/null +++ b/packages/block-library/src/cover/style.scss @@ -0,0 +1,71 @@ +.wp-block-cover, +.wp-block-cover.alignleft, +.wp-block-cover.aligncenter, +.wp-block-cover.alignright, +.wp-block-cover.alignwide, +.wp-block-cover.alignfull { + display: flex; +} + +.wp-block-cover { + position: relative; + background-color: $black; + background-size: cover; + background-position: center center; + min-height: 430px; + width: 100%; + margin: 0 0 1.5em 0; + + justify-content: center; + align-items: center; + + &.has-parallax { + background-attachment: fixed; + } + + &.has-background-dim::before { + content: ""; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + background-color: inherit; + opacity: 0.5; + z-index: z-index(".wp-block-cover__inner-container"); + } + + @for $i from 1 through 10 { + &.has-background-dim.has-background-dim-#{ $i * 10 }::before { + opacity: $i * 0.1; + } + } + + .wp-block-cover__inner-container { + width: calc(100% - 70px); + z-index: z-index(".wp-block-cover__inner-container"); + color: $light-gray-100; + } + + p, + h1, + h2, + h3, + h4, + h5, + h6, + .wp-block-subhead { + color: inherit; + } +} + +.wp-block-cover__video-background { + position: absolute; + top: 50%; + left: 50%; + transform: translateX(-50%) translateY(-50%); + width: 100%; + height: 100%; + z-index: z-index(".wp-block-cover__video-background"); + object-fit: fill; +} diff --git a/packages/block-library/src/cover/test/__snapshots__/index.js.snap b/packages/block-library/src/cover/test/__snapshots__/index.js.snap new file mode 100644 index 0000000000000..d06c221b6e4ea --- /dev/null +++ b/packages/block-library/src/cover/test/__snapshots__/index.js.snap @@ -0,0 +1,92 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`core/cover block edit matches snapshot 1`] = ` +
+
+ + Cover +
+
+ Drag an image or a video, upload a new one or select a file from your library. +
+
+
+
+ + + Drop files to upload + +
+
+
+ + +
+
+
+`; diff --git a/packages/block-library/src/cover/test/index.js b/packages/block-library/src/cover/test/index.js new file mode 100644 index 0000000000000..a088404e7c41d --- /dev/null +++ b/packages/block-library/src/cover/test/index.js @@ -0,0 +1,13 @@ +/** + * Internal dependencies + */ +import { name, settings } from '../'; +import { blockEditRender } from '../../test/helpers'; + +describe( 'core/cover', () => { + test( 'block edit matches snapshot', () => { + const wrapper = blockEditRender( name, settings ); + + expect( wrapper ).toMatchSnapshot(); + } ); +} ); diff --git a/packages/block-library/src/editor.scss b/packages/block-library/src/editor.scss index 70aa75c35b7f0..5e215a1f94b72 100644 --- a/packages/block-library/src/editor.scss +++ b/packages/block-library/src/editor.scss @@ -4,6 +4,7 @@ @import "./categories/editor.scss"; @import "./code/editor.scss"; @import "./columns/editor.scss"; +@import "./cover/editor.scss"; @import "./cover-image/editor.scss"; @import "./embed/editor.scss"; @import "./file/editor.scss"; diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js index 470ad4aea9feb..e280c024bac22 100644 --- a/packages/block-library/src/index.js +++ b/packages/block-library/src/index.js @@ -24,6 +24,7 @@ import * as categories from './categories'; import * as code from './code'; import * as columns from './columns'; import * as column from './columns/column'; +import * as cover from './cover'; import * as coverImage from './cover-image'; import * as embed from './embed'; import * as file from './file'; @@ -69,6 +70,7 @@ export const registerCoreBlocks = () => { code, columns, column, + cover, coverImage, embed, ...embed.common, diff --git a/packages/block-library/src/style.scss b/packages/block-library/src/style.scss index 8ca793de2c055..e94dbd43adbc8 100644 --- a/packages/block-library/src/style.scss +++ b/packages/block-library/src/style.scss @@ -4,6 +4,7 @@ @import "./button/style.scss"; @import "./categories/style.scss"; @import "./columns/style.scss"; +@import "./cover/style.scss"; @import "./cover-image/style.scss"; @import "./embed/style.scss"; @import "./file/style.scss"; diff --git a/test/integration/full-content/fixtures/core__cover.html b/test/integration/full-content/fixtures/core__cover.html new file mode 100644 index 0000000000000..692cc679bf2e3 --- /dev/null +++ b/test/integration/full-content/fixtures/core__cover.html @@ -0,0 +1,9 @@ + +
+
+ +

Cover!

+ +
+
+ diff --git a/test/integration/full-content/fixtures/core__cover.json b/test/integration/full-content/fixtures/core__cover.json new file mode 100644 index 0000000000000..eed85fb25a843 --- /dev/null +++ b/test/integration/full-content/fixtures/core__cover.json @@ -0,0 +1,31 @@ +[ + { + "clientId": "_clientId_0", + "name": "core/cover", + "isValid": true, + "attributes": { + "title": "", + "url": "https://cldup.com/uuUqE_dXzy.jpg", + "hasParallax": false, + "dimRatio": 40, + "backgroundType": "image" + }, + "innerBlocks": [ + { + "clientId": "_clientId_0", + "name": "core/paragraph", + "isValid": true, + "attributes": { + "content": "Cover!", + "align": "center", + "dropCap": false, + "placeholder": "Write title…", + "fontSize": "large" + }, + "innerBlocks": [], + "originalContent": "

Cover!

" + } + ], + "originalContent": "
\n\t
\n\t\t\n\t
\n
" + } +] diff --git a/test/integration/full-content/fixtures/core__cover.parsed.json b/test/integration/full-content/fixtures/core__cover.parsed.json new file mode 100644 index 0000000000000..6fa81f5ba6b94 --- /dev/null +++ b/test/integration/full-content/fixtures/core__cover.parsed.json @@ -0,0 +1,28 @@ +[ + { + "blockName": "core/cover", + "attrs": { + "url": "https://cldup.com/uuUqE_dXzy.jpg", + "dimRatio": 40 + }, + "innerBlocks": [ + { + "blockName": "core/paragraph", + "attrs": { + "align": "center", + "placeholder": "Write title…", + "fontSize": "large" + }, + "innerBlocks": [], + "innerHTML": "\n\t\t

Cover!

\n\t\t" + } + ], + "innerHTML": "\n
\n\t
\n\t\t\n\t
\n
\n" + }, + { + "blockName": null, + "attrs": {}, + "innerBlocks": [], + "innerHTML": "\n" + } +] diff --git a/test/integration/full-content/fixtures/core__cover.serialized.html b/test/integration/full-content/fixtures/core__cover.serialized.html new file mode 100644 index 0000000000000..4c125e194c4e7 --- /dev/null +++ b/test/integration/full-content/fixtures/core__cover.serialized.html @@ -0,0 +1,5 @@ + +
+

Cover!

+
+