-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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.
- Loading branch information
1 parent
fd64343
commit e50efbf
Showing
24 changed files
with
816 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ( | ||
<MediaContainer | ||
className="block-library-media-text__media-container" | ||
onSelectMedia={ this.onSelectMedia } | ||
onWidthChange={ this.onWidthChange } | ||
commitWidthChange={ this.commitWidthChange } | ||
{ ...{ mediaAlt, mediaId, mediaType, mediaUrl, mediaPosition, mediaWidth } } | ||
/> | ||
); | ||
} | ||
|
||
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 ( | ||
<Fragment> | ||
<InspectorControls> | ||
<PanelColorSettings | ||
title={ __( 'Color Settings' ) } | ||
initialOpen={ false } | ||
colorSettings={ colorSettings } | ||
/> | ||
</InspectorControls> | ||
<BlockControls> | ||
<Toolbar | ||
controls={ toolbarControls } | ||
/> | ||
</BlockControls> | ||
<div className={ classNames } style={ style } > | ||
{ this.renderMediaArea() } | ||
<InnerBlocks | ||
allowedBlocks={ ALLOWED_BLOCKS } | ||
template={ TEMPLATE } | ||
/> | ||
</div> | ||
</Fragment> | ||
); | ||
} | ||
} | ||
|
||
export default withColors( 'backgroundColor' )( MediaTextEdit ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 17h8v-2h-8v2zM3 19h8V5H3v14zM13 9h8V7h-8v2zm0 4h8v-2h-8v2z" /></svg>, | ||
|
||
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: () => <img src={ mediaUrl } alt={ mediaAlt } />, | ||
video: () => <video controls src={ mediaUrl } />, | ||
}; | ||
|
||
const backgroundClass = getColorClassName( 'background-color', backgroundColor ); | ||
const className = classnames( { | ||
'has-media-on-the-right': 'right' === mediaPosition, | ||
[ backgroundClass ]: backgroundClass, | ||
} ); | ||
|
||
let gridTemplateColumns; | ||
if ( mediaWidth !== DEFAULT_MEDIA_WIDTH ) { | ||
gridTemplateColumns = 'right' === mediaPosition ? `auto ${ mediaWidth }%` : `${ mediaWidth }% auto`; | ||
} | ||
const style = { | ||
backgroundColor: backgroundClass ? undefined : customBackgroundColor, | ||
gridTemplateColumns, | ||
}; | ||
return ( | ||
<div className={ className } style={ style }> | ||
<figure className="wp-block-media-text__media" > | ||
{ ( mediaTypeRenders[ mediaType ] || noop )() } | ||
</figure> | ||
<div className="wp-block-media-text__content"> | ||
<InnerBlocks.Content /> | ||
</div> | ||
</div> | ||
); | ||
}, | ||
}; |
Oops, something went wrong.