Skip to content

Commit 6ecd6a8

Browse files
authored
Merge pull request #987 from WordPress/update/text-block-align
Surface alignment toolbar as block control
2 parents 311582e + cb815ad commit 6ecd6a8

File tree

11 files changed

+114
-77
lines changed

11 files changed

+114
-77
lines changed

blocks/alignment-toolbar/index.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* WordPress dependencies
3+
*/
4+
import { __ } from 'i18n';
5+
import { Toolbar } from 'components';
6+
7+
const ALIGNMENT_CONTROLS = [
8+
{
9+
icon: 'editor-alignleft',
10+
title: __( 'Align left' ),
11+
align: 'left',
12+
},
13+
{
14+
icon: 'editor-aligncenter',
15+
title: __( 'Align center' ),
16+
align: 'center',
17+
},
18+
{
19+
icon: 'editor-alignright',
20+
title: __( 'Align right' ),
21+
align: 'right',
22+
},
23+
];
24+
25+
export default function AlignmentToolbar( { value, onChange } ) {
26+
return (
27+
<Toolbar
28+
controls={ ALIGNMENT_CONTROLS.map( ( control ) => {
29+
const { align } = control;
30+
const isActive = ( value === align );
31+
32+
return {
33+
...control,
34+
isActive,
35+
onClick: () => onChange( isActive ? null : align ),
36+
};
37+
} ) }
38+
/>
39+
);
40+
}

blocks/block-controls/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ import { Fill } from 'react-slot-fill';
88
*/
99
import { Toolbar } from 'components';
1010

11-
export default function BlockControls( { controls } ) {
11+
export default function BlockControls( { controls, children } ) {
1212
return (
1313
<Fill name="Formatting.Toolbar">
1414
<Toolbar controls={ controls } />
15+
{ children }
1516
</Fill>
1617
);
1718
}

blocks/editable/index.js

Lines changed: 2 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,14 @@
22
* External dependencies
33
*/
44
import classnames from 'classnames';
5-
import { last, isEqual, capitalize, omitBy, forEach, merge, identity, find } from 'lodash';
5+
import { last, isEqual, omitBy, forEach, merge, identity, find } from 'lodash';
66
import { nodeListToReact } from 'dom-react';
77
import { Fill } from 'react-slot-fill';
88
import 'element-closest';
99

1010
/**
1111
* WordPress dependencies
1212
*/
13-
import { Toolbar } from 'components';
1413
import { BACKSPACE, DELETE } from 'utils/keycodes';
1514

1615
/**
@@ -20,30 +19,6 @@ import './style.scss';
2019
import FormatToolbar from './format-toolbar';
2120
import TinyMCE from './tinymce';
2221

23-
const alignmentMap = {
24-
alignleft: 'left',
25-
alignright: 'right',
26-
aligncenter: 'center',
27-
};
28-
29-
const ALIGNMENT_CONTROLS = [
30-
{
31-
icon: 'editor-alignleft',
32-
title: wp.i18n.__( 'Align left' ),
33-
align: 'left',
34-
},
35-
{
36-
icon: 'editor-aligncenter',
37-
title: wp.i18n.__( 'Align center' ),
38-
align: 'center',
39-
},
40-
{
41-
icon: 'editor-alignright',
42-
title: wp.i18n.__( 'Align right' ),
43-
align: 'right',
44-
},
45-
];
46-
4722
function createElement( type, props, ...children ) {
4823
if ( props[ 'data-mce-bogus' ] === 'all' ) {
4924
return null;
@@ -78,7 +53,6 @@ export default class Editable extends wp.element.Component {
7853

7954
this.state = {
8055
formats: {},
81-
alignment: null,
8256
bookmark: null,
8357
empty: ! props.value || ! props.value.length,
8458
};
@@ -296,12 +270,10 @@ export default class Editable extends wp.element.Component {
296270
}
297271
const activeFormats = this.editor.formatter.matchAll( [ 'bold', 'italic', 'strikethrough' ] );
298272
activeFormats.forEach( ( activeFormat ) => formats[ activeFormat ] = true );
299-
const alignments = this.editor.formatter.matchAll( [ 'alignleft', 'aligncenter', 'alignright' ] );
300-
const alignment = alignments.length > 0 ? alignmentMap[ alignments[ 0 ] ] : null;
301273

302274
const focusPosition = this.getRelativePosition( element );
303275
const bookmark = this.editor.selection.getBookmark( 2, true );
304-
this.setState( { alignment, bookmark, formats, focusPosition } );
276+
this.setState( { bookmark, formats, focusPosition } );
305277
}
306278

307279
updateContent() {
@@ -400,28 +372,13 @@ export default class Editable extends wp.element.Component {
400372
this.editor.setDirty( true );
401373
}
402374

403-
isAlignmentActive( align ) {
404-
return this.state.alignment === align;
405-
}
406-
407-
toggleAlignment( align ) {
408-
this.editor.focus();
409-
410-
if ( this.isAlignmentActive( align ) ) {
411-
this.editor.execCommand( 'JustifyNone' );
412-
} else {
413-
this.editor.execCommand( 'Justify' + capitalize( align ) );
414-
}
415-
}
416-
417375
render() {
418376
const {
419377
tagName,
420378
style,
421379
value,
422380
focus,
423381
className,
424-
showAlignments = false,
425382
inlineToolbar = false,
426383
formattingControls,
427384
placeholder,
@@ -446,15 +403,6 @@ export default class Editable extends wp.element.Component {
446403
<div className={ classes }>
447404
{ focus &&
448405
<Fill name="Formatting.Toolbar">
449-
{ showAlignments &&
450-
<Toolbar
451-
controls={ ALIGNMENT_CONTROLS.map( ( control ) => ( {
452-
...control,
453-
onClick: () => this.toggleAlignment( control.align ),
454-
isActive: this.isAlignmentActive( control.align ),
455-
} ) ) }
456-
/>
457-
}
458406
{ ! inlineToolbar && formatToolbar }
459407
</Fill>
460408
}

blocks/editable/tinymce.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* External dependencies
33
*/
44
import tinymce from 'tinymce';
5+
import { isEqual } from 'lodash';
56

67
export default class TinyMCE extends wp.element.Component {
78
componentDidMount() {
@@ -22,6 +23,10 @@ export default class TinyMCE extends wp.element.Component {
2223
if ( this.editorNode.getAttribute( 'data-is-empty' ) !== isEmpty ) {
2324
this.editorNode.setAttribute( 'data-is-empty', isEmpty );
2425
}
26+
27+
if ( ! isEqual( this.props.style, nextProps.style ) ) {
28+
Object.assign( this.editorNode.style, nextProps.style );
29+
}
2530
}
2631

2732
componentWillUnmount() {

blocks/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,7 @@ import './library';
1313
// Blocks are inferred from the HTML source of a post through a parsing mechanism
1414
// and then stored as objects in state, from which it is then rendered for editing.
1515
export * from './api';
16+
export { default as AlignmentToolbar } from './alignment-toolbar';
17+
export { default as BlockControls } from './block-controls';
1618
export { default as Editable } from './editable';
1719
export { default as MediaUploadButton } from './media-upload-button';

blocks/library/list/index.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,6 @@ registerBlockType( 'core/list', {
130130
value={ values }
131131
focus={ focus }
132132
onFocus={ setFocus }
133-
showAlignments
134133
className="blocks-list" />
135134
);
136135
},

blocks/library/quote/index.js

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import { switchChildrenNodeName } from 'element';
88
*/
99
import './style.scss';
1010
import { registerBlockType, createBlock, query as hpq } from '../../api';
11+
import AlignmentToolbar from '../../alignment-toolbar';
12+
import BlockControls from '../../block-controls';
1113
import Editable from '../../editable';
1214

1315
const { children, query } = hpq;
@@ -111,11 +113,24 @@ registerBlockType( 'core/quote', {
111113
},
112114

113115
edit( { attributes, setAttributes, focus, setFocus, mergeBlocks } ) {
114-
const { value, citation, style = 1 } = attributes;
116+
const { align, value, citation, style = 1 } = attributes;
115117
const focusedEditable = focus ? focus.editable || 'value' : null;
116118

117-
return (
118-
<blockquote className={ `blocks-quote blocks-quote-style-${ style }` }>
119+
return [
120+
focus && (
121+
<BlockControls key="controls">
122+
<AlignmentToolbar
123+
value={ align }
124+
onChange={ ( nextAlign ) => {
125+
setAttributes( { align: nextAlign } );
126+
} }
127+
/>
128+
</BlockControls>
129+
),
130+
<blockquote
131+
key="quote"
132+
className={ `blocks-quote blocks-quote-style-${ style }` }
133+
>
119134
<Editable
120135
value={ value }
121136
onChange={
@@ -126,7 +141,7 @@ registerBlockType( 'core/quote', {
126141
focus={ focusedEditable === 'value' ? focus : null }
127142
onFocus={ () => setFocus( { editable: 'value' } ) }
128143
onMerge={ mergeBlocks }
129-
showAlignments
144+
style={ { textAlign: align } }
130145
/>
131146
{ ( ( citation && citation.length > 0 ) || !! focus ) && (
132147
<Editable
@@ -143,17 +158,22 @@ registerBlockType( 'core/quote', {
143158
inline
144159
/>
145160
) }
146-
</blockquote>
147-
);
161+
</blockquote>,
162+
];
148163
},
149164

150165
save( { attributes } ) {
151-
const { value, citation, style = 1 } = attributes;
166+
const { align, value, citation, style = 1 } = attributes;
152167

153168
return (
154169
<blockquote className={ `blocks-quote-style-${ style }` }>
155170
{ value && value.map( ( paragraph, i ) => (
156-
<p key={ i }>{ paragraph }</p>
171+
<p
172+
key={ i }
173+
style={ { textAlign: align ? align : null } }
174+
>
175+
{ paragraph }
176+
</p>
157177
) ) }
158178
{ citation && citation.length > 0 && (
159179
<footer>{ citation }</footer>

blocks/library/text/index.js

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1+
/**
2+
* WordPress dependencies
3+
*/
4+
import { Children, cloneElement } from 'element';
5+
16
/**
27
* Internal dependencies
38
*/
49
import { registerBlockType, createBlock, query } from '../../api';
10+
import AlignmentToolbar from '../../alignment-toolbar';
11+
import BlockControls from '../../block-controls';
512
import Editable from '../../editable';
613

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

20-
defaultAttributes: {
21-
content: <p />,
22-
},
23-
2427
merge( attributes, attributesToMerge ) {
2528
return {
2629
content: wp.element.concatChildren( attributes.content, attributesToMerge.content ),
2730
};
2831
},
2932

3033
edit( { attributes, setAttributes, insertBlockAfter, focus, setFocus, mergeBlocks } ) {
31-
const { content } = attributes;
34+
const { align, content } = attributes;
3235

33-
return (
36+
return [
37+
focus && (
38+
<BlockControls key="controls">
39+
<AlignmentToolbar
40+
value={ align }
41+
onChange={ ( nextAlign ) => {
42+
setAttributes( { align: nextAlign } );
43+
} }
44+
/>
45+
</BlockControls>
46+
),
3447
<Editable
48+
key="editable"
3549
value={ content }
3650
onChange={ ( nextContent ) => {
3751
setAttributes( {
@@ -47,13 +61,20 @@ registerBlockType( 'core/text', {
4761
} ) );
4862
} }
4963
onMerge={ mergeBlocks }
50-
showAlignments
51-
/>
52-
);
64+
style={ { textAlign: align } }
65+
/>,
66+
];
5367
},
5468

5569
save( { attributes } ) {
56-
const { content } = attributes;
57-
return content;
70+
const { align, content } = attributes;
71+
72+
if ( ! align ) {
73+
return content;
74+
}
75+
76+
return Children.map( content, ( paragraph ) => (
77+
cloneElement( paragraph, { style: { textAlign: align } } )
78+
) );
5879
},
5980
} );
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
<!-- wp:core/text -->
1+
<!-- wp:core/text align="right" -->
22
<p style="text-align:right;">... like this one, which is separate from the above and right aligned.</p>
33
<!-- /wp:core/text -->

blocks/test/fixtures/core-text-align-right.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"uid": "_uid_0",
44
"name": "core/text",
55
"attributes": {
6+
"align": "right",
67
"content": [
78
{
89
"type": "p",

0 commit comments

Comments
 (0)