@wordpress/block-library
Version:
Block library for the WordPress editor.
271 lines (251 loc) • 7.38 kB
JavaScript
/**
* External dependencies
*/
import classnames from 'classnames';
/**
* WordPress dependencies
*/
import { __, _x } from '@wordpress/i18n';
import {
Component,
Fragment,
} from '@wordpress/element';
import {
PanelBody,
ToggleControl,
Toolbar,
withFallbackStyles,
} from '@wordpress/components';
import {
withColors,
AlignmentToolbar,
BlockControls,
ContrastChecker,
FontSizePicker,
InspectorControls,
PanelColorSettings,
RichText,
withFontSizes,
} from '@wordpress/block-editor';
import { createBlock } from '@wordpress/blocks';
import { compose } from '@wordpress/compose';
import { withSelect } from '@wordpress/data';
const { getComputedStyle } = window;
const name = 'core/paragraph';
const applyFallbackStyles = withFallbackStyles( ( node, ownProps ) => {
const { textColor, backgroundColor, fontSize, customFontSize } = ownProps.attributes;
const editableNode = node.querySelector( '[contenteditable="true"]' );
//verify if editableNode is available, before using getComputedStyle.
const computedStyles = editableNode ? getComputedStyle( editableNode ) : null;
return {
fallbackBackgroundColor: backgroundColor || ! computedStyles ? undefined : computedStyles.backgroundColor,
fallbackTextColor: textColor || ! computedStyles ? undefined : computedStyles.color,
fallbackFontSize: fontSize || customFontSize || ! computedStyles ? undefined : parseInt( computedStyles.fontSize ) || undefined,
};
} );
class ParagraphBlock extends Component {
constructor() {
super( ...arguments );
this.onReplace = this.onReplace.bind( this );
this.toggleDropCap = this.toggleDropCap.bind( this );
this.splitBlock = this.splitBlock.bind( this );
}
onReplace( blocks ) {
const { attributes, onReplace } = this.props;
onReplace( blocks.map( ( block, index ) => (
index === 0 && block.name === name ?
{ ...block,
attributes: {
...attributes,
...block.attributes,
},
} :
block
) ) );
}
toggleDropCap() {
const { attributes, setAttributes } = this.props;
setAttributes( { dropCap: ! attributes.dropCap } );
}
getDropCapHelp( checked ) {
return checked ? __( 'Showing large initial letter.' ) : __( 'Toggle to show a large initial letter.' );
}
/**
* Split handler for RichText value, namely when content is pasted or the
* user presses the Enter key.
*
* @param {?Array} before Optional before value, to be used as content
* in place of what exists currently for the
* block. If undefined, the block is deleted.
* @param {?Array} after Optional after value, to be appended in a new
* paragraph block to the set of blocks passed
* as spread.
* @param {...WPBlock} blocks Optional blocks inserted between the before
* and after value blocks.
*/
splitBlock( before, after, ...blocks ) {
const {
attributes,
insertBlocksAfter,
setAttributes,
onReplace,
} = this.props;
if ( after !== null ) {
// Append "After" content as a new paragraph block to the end of
// any other blocks being inserted after the current paragraph.
blocks.push( createBlock( name, { content: after } ) );
}
if ( blocks.length && insertBlocksAfter ) {
insertBlocksAfter( blocks );
}
const { content } = attributes;
if ( before === null ) {
// If before content is omitted, treat as intent to delete block.
onReplace( [] );
} else if ( content !== before ) {
// Only update content if it has in-fact changed. In case that user
// has created a new paragraph at end of an existing one, the value
// of before will be strictly equal to the current content.
setAttributes( { content: before } );
}
}
render() {
const {
attributes,
setAttributes,
mergeBlocks,
onReplace,
className,
backgroundColor,
textColor,
setBackgroundColor,
setTextColor,
fallbackBackgroundColor,
fallbackTextColor,
fallbackFontSize,
fontSize,
setFontSize,
isRTL,
} = this.props;
const {
align,
content,
dropCap,
placeholder,
direction,
} = attributes;
return (
<Fragment>
<BlockControls>
<AlignmentToolbar
value={ align }
onChange={ ( nextAlign ) => {
setAttributes( { align: nextAlign } );
} }
/>
{ isRTL && (
<Toolbar
controls={ [
{
icon: 'editor-ltr',
title: _x( 'Left to right', 'editor button' ),
isActive: direction === 'ltr',
onClick() {
const nextDirection = direction === 'ltr' ? undefined : 'ltr';
setAttributes( {
direction: nextDirection,
} );
},
},
] }
/>
) }
</BlockControls>
<InspectorControls>
<PanelBody title={ __( 'Text Settings' ) } className="blocks-font-size">
<FontSizePicker
fallbackFontSize={ fallbackFontSize }
value={ fontSize.size }
onChange={ setFontSize }
/>
<ToggleControl
label={ __( 'Drop Cap' ) }
checked={ !! dropCap }
onChange={ this.toggleDropCap }
help={ this.getDropCapHelp }
/>
</PanelBody>
<PanelColorSettings
title={ __( 'Color Settings' ) }
initialOpen={ false }
colorSettings={ [
{
value: backgroundColor.color,
onChange: setBackgroundColor,
label: __( 'Background Color' ),
},
{
value: textColor.color,
onChange: setTextColor,
label: __( 'Text Color' ),
},
] }
>
<ContrastChecker
{ ...{
textColor: textColor.color,
backgroundColor: backgroundColor.color,
fallbackTextColor,
fallbackBackgroundColor,
} }
fontSize={ fontSize.size }
/>
</PanelColorSettings>
</InspectorControls>
<RichText
identifier="content"
tagName="p"
className={ classnames( 'wp-block-paragraph', className, {
'has-text-color': textColor.color,
'has-background': backgroundColor.color,
'has-drop-cap': dropCap,
[ backgroundColor.class ]: backgroundColor.class,
[ textColor.class ]: textColor.class,
[ fontSize.class ]: fontSize.class,
} ) }
style={ {
backgroundColor: backgroundColor.color,
color: textColor.color,
fontSize: fontSize.size ? fontSize.size + 'px' : undefined,
textAlign: align,
direction,
} }
value={ content }
onChange={ ( nextContent ) => {
setAttributes( {
content: nextContent,
} );
} }
unstableOnSplit={ this.splitBlock }
onMerge={ mergeBlocks }
onReplace={ this.onReplace }
onRemove={ () => onReplace( [] ) }
aria-label={ content ? __( 'Paragraph block' ) : __( 'Empty block; start writing or type forward slash to choose a block' ) }
placeholder={ placeholder || __( 'Start writing or type / to choose a block' ) }
/>
</Fragment>
);
}
}
const ParagraphEdit = compose( [
withColors( 'backgroundColor', { textColor: 'color' } ),
withFontSizes( 'fontSize' ),
applyFallbackStyles,
withSelect( ( select ) => {
const { getSettings } = select( 'core/block-editor' );
return {
isRTL: getSettings().isRTL,
};
} ),
] )( ParagraphBlock );
export default ParagraphEdit;