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 "Text & Image" Block #9416

Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions packages/block-library/src/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
2 changes: 2 additions & 0 deletions packages/block-library/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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,
Expand Down
163 changes: 163 additions & 0 deletions packages/block-library/src/media-text/edit.js
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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I have the same remark. Do we have an issue to follow up with this? Unify the media object between the MediaUpload and the rest API?

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 );
59 changes: 59 additions & 0 deletions packages/block-library/src/media-text/editor.scss
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;
}
}
120 changes: 120 additions & 0 deletions packages/block-library/src/media-text/index.js
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>
);
},
};
Loading