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

Surface alignment toolbar as block control #987

Merged
merged 3 commits into from
Jun 7, 2017
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
40 changes: 40 additions & 0 deletions blocks/alignment-toolbar/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* WordPress dependencies
*/
import { __ } from 'i18n';
import { Toolbar } from 'components';

const ALIGNMENT_CONTROLS = [
{
icon: 'editor-alignleft',
title: __( 'Align left' ),
align: 'left',
},
{
icon: 'editor-aligncenter',
title: __( 'Align center' ),
align: 'center',
},
{
icon: 'editor-alignright',
title: __( 'Align right' ),
align: 'right',
},
];

export default function AlignmentToolbar( { value, onChange } ) {
return (
<Toolbar
controls={ ALIGNMENT_CONTROLS.map( ( control ) => {
const { align } = control;
const isActive = ( value === align );

return {
...control,
isActive,
onClick: () => onChange( isActive ? null : align ),
};
} ) }
/>
);
}
3 changes: 2 additions & 1 deletion blocks/block-controls/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import { Fill } from 'react-slot-fill';
*/
import { Toolbar } from 'components';

export default function BlockControls( { controls } ) {
export default function BlockControls( { controls, children } ) {
return (
<Fill name="Formatting.Toolbar">
<Toolbar controls={ controls } />
Copy link
Contributor

Choose a reason for hiding this comment

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

we probably should make controls optional and avoid including this if no controls are provided

Copy link
Member Author

Choose a reason for hiding this comment

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

Technically it is optional, and <Toolbar /> will not render itself when omitted:

https://github.com/WordPress/gutenberg/blob/97ee1a3/components/toolbar/index.js#L13-L15

Example: Included Text and Quote block changes render <BlockControls /> without controls prop.

{ children }
</Fill>
);
}
56 changes: 2 additions & 54 deletions blocks/editable/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@
* External dependencies
*/
import classnames from 'classnames';
import { last, isEqual, capitalize, omitBy, forEach, merge, identity, find } from 'lodash';
import { last, isEqual, omitBy, forEach, merge, identity, find } from 'lodash';
import { nodeListToReact } from 'dom-react';
import { Fill } from 'react-slot-fill';
import 'element-closest';

/**
* WordPress dependencies
*/
import { Toolbar } from 'components';
import { BACKSPACE, DELETE } from 'utils/keycodes';

/**
Expand All @@ -20,30 +19,6 @@ import './style.scss';
import FormatToolbar from './format-toolbar';
import TinyMCE from './tinymce';

const alignmentMap = {
alignleft: 'left',
alignright: 'right',
aligncenter: 'center',
};

const ALIGNMENT_CONTROLS = [
{
icon: 'editor-alignleft',
title: wp.i18n.__( 'Align left' ),
align: 'left',
},
{
icon: 'editor-aligncenter',
title: wp.i18n.__( 'Align center' ),
align: 'center',
},
{
icon: 'editor-alignright',
title: wp.i18n.__( 'Align right' ),
align: 'right',
},
];

function createElement( type, props, ...children ) {
if ( props[ 'data-mce-bogus' ] === 'all' ) {
return null;
Expand Down Expand Up @@ -78,7 +53,6 @@ export default class Editable extends wp.element.Component {

this.state = {
formats: {},
alignment: null,
bookmark: null,
empty: ! props.value || ! props.value.length,
};
Expand Down Expand Up @@ -296,12 +270,10 @@ export default class Editable extends wp.element.Component {
}
const activeFormats = this.editor.formatter.matchAll( [ 'bold', 'italic', 'strikethrough' ] );
activeFormats.forEach( ( activeFormat ) => formats[ activeFormat ] = true );
const alignments = this.editor.formatter.matchAll( [ 'alignleft', 'aligncenter', 'alignright' ] );
const alignment = alignments.length > 0 ? alignmentMap[ alignments[ 0 ] ] : null;

const focusPosition = this.getRelativePosition( element );
const bookmark = this.editor.selection.getBookmark( 2, true );
this.setState( { alignment, bookmark, formats, focusPosition } );
this.setState( { bookmark, formats, focusPosition } );
}

updateContent() {
Expand Down Expand Up @@ -400,28 +372,13 @@ export default class Editable extends wp.element.Component {
this.editor.setDirty( true );
}

isAlignmentActive( align ) {
return this.state.alignment === align;
}

toggleAlignment( align ) {
this.editor.focus();

if ( this.isAlignmentActive( align ) ) {
this.editor.execCommand( 'JustifyNone' );
} else {
this.editor.execCommand( 'Justify' + capitalize( align ) );
}
}

render() {
const {
tagName,
style,
value,
focus,
className,
showAlignments = false,
inlineToolbar = false,
formattingControls,
placeholder,
Expand All @@ -446,15 +403,6 @@ export default class Editable extends wp.element.Component {
<div className={ classes }>
{ focus &&
<Fill name="Formatting.Toolbar">
{ showAlignments &&
<Toolbar
controls={ ALIGNMENT_CONTROLS.map( ( control ) => ( {
...control,
onClick: () => this.toggleAlignment( control.align ),
isActive: this.isAlignmentActive( control.align ),
} ) ) }
/>
}
{ ! inlineToolbar && formatToolbar }
</Fill>
}
Expand Down
5 changes: 5 additions & 0 deletions blocks/editable/tinymce.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* External dependencies
*/
import tinymce from 'tinymce';
import { isEqual } from 'lodash';

export default class TinyMCE extends wp.element.Component {
componentDidMount() {
Expand All @@ -22,6 +23,10 @@ export default class TinyMCE extends wp.element.Component {
if ( this.editorNode.getAttribute( 'data-is-empty' ) !== isEmpty ) {
this.editorNode.setAttribute( 'data-is-empty', isEmpty );
}

if ( ! isEqual( this.props.style, nextProps.style ) ) {
Object.assign( this.editorNode.style, nextProps.style );
}
}

componentWillUnmount() {
Expand Down
2 changes: 2 additions & 0 deletions blocks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,7 @@ import './library';
// Blocks are inferred from the HTML source of a post through a parsing mechanism
// and then stored as objects in state, from which it is then rendered for editing.
export * from './api';
export { default as AlignmentToolbar } from './alignment-toolbar';
export { default as BlockControls } from './block-controls';
export { default as Editable } from './editable';
export { default as MediaUploadButton } from './media-upload-button';
1 change: 0 additions & 1 deletion blocks/library/list/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@ registerBlockType( 'core/list', {
value={ values }
focus={ focus }
onFocus={ setFocus }
showAlignments
className="blocks-list" />
);
},
Expand Down
36 changes: 28 additions & 8 deletions blocks/library/quote/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { switchChildrenNodeName } from 'element';
*/
import './style.scss';
import { registerBlockType, createBlock, query as hpq } from '../../api';
import AlignmentToolbar from '../../alignment-toolbar';
import BlockControls from '../../block-controls';
import Editable from '../../editable';

const { children, query } = hpq;
Expand Down Expand Up @@ -111,11 +113,24 @@ registerBlockType( 'core/quote', {
},

edit( { attributes, setAttributes, focus, setFocus, mergeBlocks } ) {
const { value, citation, style = 1 } = attributes;
const { align, value, citation, style = 1 } = attributes;
const focusedEditable = focus ? focus.editable || 'value' : null;

return (
<blockquote className={ `blocks-quote blocks-quote-style-${ style }` }>
return [
focus && (
Copy link
Contributor

Choose a reason for hiding this comment

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

I was thinking about the focus check, I guess we could avoid it by hiding the controls using CSS instead of removing them completely from the DOM.

Copy link
Member Author

Choose a reason for hiding this comment

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

I've grown more okay with it. Seems reasonable to expect that the render logic should be responsible for deciding if controls should be shown depending on whether the block currently has focus.

<BlockControls key="controls">
<AlignmentToolbar
value={ align }
onChange={ ( nextAlign ) => {
setAttributes( { align: nextAlign } );
} }
/>
</BlockControls>
),
<blockquote
key="quote"
className={ `blocks-quote blocks-quote-style-${ style }` }
>
<Editable
value={ value }
onChange={
Expand All @@ -126,7 +141,7 @@ registerBlockType( 'core/quote', {
focus={ focusedEditable === 'value' ? focus : null }
onFocus={ () => setFocus( { editable: 'value' } ) }
onMerge={ mergeBlocks }
showAlignments
style={ { textAlign: align } }
/>
{ ( ( citation && citation.length > 0 ) || !! focus ) && (
<Editable
Expand All @@ -143,17 +158,22 @@ registerBlockType( 'core/quote', {
inline
/>
) }
</blockquote>
);
</blockquote>,
];
},

save( { attributes } ) {
const { value, citation, style = 1 } = attributes;
const { align, value, citation, style = 1 } = attributes;

return (
<blockquote className={ `blocks-quote-style-${ style }` }>
{ value && value.map( ( paragraph, i ) => (
<p key={ i }>{ paragraph }</p>
<p
key={ i }
style={ { textAlign: align ? align : null } }
>
{ paragraph }
</p>
) ) }
{ citation && citation.length > 0 && (
<footer>{ citation }</footer>
Expand Down
43 changes: 32 additions & 11 deletions blocks/library/text/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
/**
* WordPress dependencies
*/
import { Children, cloneElement } from 'element';

/**
* Internal dependencies
*/
import { registerBlockType, createBlock, query } from '../../api';
import AlignmentToolbar from '../../alignment-toolbar';
import BlockControls from '../../block-controls';
import Editable from '../../editable';

const { children } = query;
Expand All @@ -17,21 +24,28 @@ registerBlockType( 'core/text', {
content: children(),
},

defaultAttributes: {
content: <p />,
},

merge( attributes, attributesToMerge ) {
return {
content: wp.element.concatChildren( attributes.content, attributesToMerge.content ),
};
},

edit( { attributes, setAttributes, insertBlockAfter, focus, setFocus, mergeBlocks } ) {
const { content } = attributes;
const { align, content } = attributes;

return (
return [
focus && (
<BlockControls key="controls">
<AlignmentToolbar
value={ align }
onChange={ ( nextAlign ) => {
setAttributes( { align: nextAlign } );
} }
/>
</BlockControls>
),
<Editable
key="editable"
value={ content }
onChange={ ( nextContent ) => {
setAttributes( {
Expand All @@ -47,13 +61,20 @@ registerBlockType( 'core/text', {
} ) );
} }
onMerge={ mergeBlocks }
showAlignments
/>
);
style={ { textAlign: align } }
/>,
];
},

save( { attributes } ) {
const { content } = attributes;
return content;
const { align, content } = attributes;

if ( ! align ) {
return content;
}

return Children.map( content, ( paragraph ) => (
cloneElement( paragraph, { style: { textAlign: align } } )
) );
},
} );
2 changes: 1 addition & 1 deletion blocks/test/fixtures/core-text-align-right.html
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<!-- wp:core/text -->
<!-- wp:core/text align="right" -->
<p style="text-align:right;">... like this one, which is separate from the above and right aligned.</p>
<!-- /wp:core/text -->
1 change: 1 addition & 0 deletions blocks/test/fixtures/core-text-align-right.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"uid": "_uid_0",
"name": "core/text",
"attributes": {
"align": "right",
"content": [
{
"type": "p",
Expand Down
2 changes: 1 addition & 1 deletion blocks/test/fixtures/core-text-align-right.serialized.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- wp:core/text -->
<!-- wp:core/text align="right" -->
<p style="text-align:right;">... like this one, which is separate from the above and right aligned.</p>
<!-- /wp:core/text -->