Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add nested blocks inside cover block #13822

Merged
merged 2 commits into from
Mar 7, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Add conditional dark theme styles depending on image/video colors and…
… selected overlay colors
  • Loading branch information
jorgefilipecosta committed Mar 7, 2019
commit 181df1d6110f519151b5023ad9a8d232d46cf057
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/block-library/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"@wordpress/keycodes": "file:../keycodes",
"@wordpress/viewport": "file:../viewport",
"classnames": "^2.2.5",
"fast-average-color": "4.3.0",
"lodash": "^4.17.11",
"memize": "^1.0.5",
"url": "^0.11.0"
Expand Down
367 changes: 367 additions & 0 deletions packages/block-library/src/cover/edit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,367 @@
/**
* External dependencies
*/
import classnames from 'classnames';
import FastAverageColor from 'fast-average-color';
import tinycolor from 'tinycolor2';

/**
* WordPress dependencies
*/
import {
FocalPointPicker,
IconButton,
PanelBody,
RangeControl,
ToggleControl,
Toolbar,
withNotices,
} from '@wordpress/components';
import { compose } from '@wordpress/compose';
import {
BlockControls,
BlockIcon,
InnerBlocks,
InspectorControls,
MediaPlaceholder,
MediaUpload,
MediaUploadCheck,
PanelColorSettings,
withColors,
} from '@wordpress/editor';
import { Component, createRef, Fragment } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import icon from './icon';

/**
* Module Constants
*/
export const IMAGE_BACKGROUND_TYPE = 'image';
export const VIDEO_BACKGROUND_TYPE = 'video';
const ALLOWED_MEDIA_TYPES = [ 'image', 'video' ];
const INNER_BLOCKS_TEMPLATE = [
[ 'core/paragraph', {
align: 'center',
fontSize: 'large',
placeholder: __( 'Write title…' ),
} ],
];
const INNER_BLOCKS_ALLOWED_BLOCKS = [ 'core/button', 'core/heading', 'core/paragraph' ];

function retrieveFastAverageColor() {
if ( ! retrieveFastAverageColor.fastAverageColor ) {
retrieveFastAverageColor.fastAverageColor = new FastAverageColor();
}
return retrieveFastAverageColor.fastAverageColor;
}

export function backgroundImageStyles( url ) {
return url ?
{ backgroundImage: `url(${ url })` } :
{};
}

export function dimRatioToClass( ratio ) {
return ( ratio === 0 || ratio === 50 ) ?
null :
'has-background-dim-' + ( 10 * Math.round( ratio / 10 ) );
}

class CoverEdit extends Component {
constructor() {
super( ...arguments );
this.state = {
isDark: false,
};
this.imageRef = createRef();
this.videoRef = createRef();
this.changeIsDarkIfRequired = this.changeIsDarkIfRequired.bind( this );
}

componentDidMount() {
this.handleBackgroundMode();
}

componentDidUpdate( prevProps ) {
this.handleBackgroundMode( prevProps );
}

render() {
const {
attributes,
setAttributes,
className,
noticeOperations,
noticeUI,
overlayColor,
setOverlayColor,
} = this.props;
const {
backgroundType,
dimRatio,
focalPoint,
hasParallax,
id,
url,
} = attributes;
const onSelectMedia = ( media ) => {
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.
// Videos 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.
if (
media.type !== IMAGE_BACKGROUND_TYPE &&
media.type !== VIDEO_BACKGROUND_TYPE
) {
return;
}
mediaType = media.type;
}

setAttributes( {
url: media.url,
id: media.id,
backgroundType: mediaType,
} );
};
const toggleParallax = () => setAttributes( { hasParallax: ! hasParallax } );
const setDimRatio = ( ratio ) => setAttributes( { dimRatio: ratio } );

const style = {
...(
backgroundType === IMAGE_BACKGROUND_TYPE ?
backgroundImageStyles( url ) :
{}
),
backgroundColor: overlayColor.color,
};

if ( focalPoint ) {
style.backgroundPosition = `${ focalPoint.x * 100 }% ${ focalPoint.y * 100 }%`;
}

const controls = (
<Fragment>
<BlockControls>
{ !! url && (
<Fragment>
<MediaUploadCheck>
<Toolbar>
<MediaUpload
onSelect={ onSelectMedia }
allowedTypes={ ALLOWED_MEDIA_TYPES }
value={ id }
render={ ( { open } ) => (
<IconButton
className="components-toolbar__control"
label={ __( 'Edit media' ) }
icon="edit"
onClick={ open }
/>
) }
/>
</Toolbar>
</MediaUploadCheck>
</Fragment>
) }
</BlockControls>
{ !! url && (
<InspectorControls>
<PanelBody title={ __( 'Cover Settings' ) }>
{ IMAGE_BACKGROUND_TYPE === backgroundType && (
<ToggleControl
label={ __( 'Fixed Background' ) }
checked={ hasParallax }
onChange={ toggleParallax }
/>
) }
{ IMAGE_BACKGROUND_TYPE === backgroundType && ! hasParallax && (
<FocalPointPicker
label={ __( 'Focal Point Picker' ) }
url={ url }
value={ focalPoint }
onChange={ ( value ) => setAttributes( { focalPoint: value } ) }
/>
) }
<PanelColorSettings
title={ __( 'Overlay' ) }
initialOpen={ true }
colorSettings={ [ {
value: overlayColor.color,
onChange: setOverlayColor,
label: __( 'Overlay Color' ),
} ] }
>
<RangeControl
label={ __( 'Background Opacity' ) }
value={ dimRatio }
onChange={ setDimRatio }
min={ 0 }
max={ 100 }
step={ 10 }
/>
</PanelColorSettings>
</PanelBody>
</InspectorControls>
) }
</Fragment>
);

if ( ! url ) {
const placeholderIcon = <BlockIcon icon={ icon } />;
const label = __( 'Cover' );

return (
<Fragment>
{ controls }
<MediaPlaceholder
icon={ placeholderIcon }
className={ className }
labels={ {
title: label,
instructions: __( 'Drag an image or a video, upload a new one or select a file from your library.' ),
} }
onSelect={ onSelectMedia }
accept="image/*,video/*"
allowedTypes={ ALLOWED_MEDIA_TYPES }
notices={ noticeUI }
onError={ noticeOperations.createErrorNotice }
/>
</Fragment>
);
}

const classes = classnames(
className,
dimRatioToClass( dimRatio ),
{
'is-dark-theme': this.state.isDark,
'has-background-dim': dimRatio !== 0,
'has-parallax': hasParallax,
}
);

return (
<Fragment>
{ controls }
<div
data-url={ url }
style={ style }
className={ classes }
>
{ IMAGE_BACKGROUND_TYPE === backgroundType && (
// Used only to programmatically check if the image is dark or not
<img
ref={ this.imageRef }
aria-hidden
alt=""
style={ {
display: 'none',
} }
src={ url }
/>
) }
{ VIDEO_BACKGROUND_TYPE === backgroundType && (
<video
ref={ this.videoRef }
className="wp-block-cover__video-background"
autoPlay
muted
loop
src={ url }
/>
) }
<div className="wp-block-cover__inner-container">
<InnerBlocks
template={ INNER_BLOCKS_TEMPLATE }
allowedBlocks={ INNER_BLOCKS_ALLOWED_BLOCKS }
/>
</div>
</div>
</Fragment>
);
}

handleBackgroundMode( prevProps ) {
const { attributes, overlayColor } = this.props;
const { dimRatio, url } = attributes;
// If opacity is greater than 50 the dominant color is the overlay color,
// so use that color for the dark mode computation.
if ( dimRatio > 50 ) {
if (
prevProps &&
prevProps.attributes.dimRatio > 50 &&
prevProps.overlayColor.color === overlayColor.color
) {
// No relevant prop changes happened there is no need to apply any change.
return;
}
if ( ! overlayColor.color ) {
// If no overlay color exists the overlay color is black (isDark )
this.changeIsDarkIfRequired( true );
return;
}
this.changeIsDarkIfRequired(
tinycolor( overlayColor.color ).isDark()
);
return;
}
// If opacity is lower than 50 the dominant color is the image or video color,
// so use that color for the dark mode computation.

if (
prevProps &&
prevProps.attributes.dimRatio <= 50 &&
prevProps.attributes.url === url
) {
// No relevant prop changes happened there is no need to apply any change.
return;
}
const { backgroundType } = attributes;

let element;

switch ( backgroundType ) {
case IMAGE_BACKGROUND_TYPE:
element = this.imageRef.current;
break;
case VIDEO_BACKGROUND_TYPE:
element = this.videoRef.current;
break;
}
if ( ! element ) {
return;
}
retrieveFastAverageColor().getColorAsync( element, ( color ) => {
this.changeIsDarkIfRequired( color.isDark );
} );
}

changeIsDarkIfRequired( newIsDark ) {
if ( this.state.isDark !== newIsDark ) {
this.setState( {
isDark: newIsDark,
} );
}
}
}

export default compose( [
withColors( { overlayColor: 'background-color' } ),
withNotices,
] )( CoverEdit );
Loading