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

[Try] Remove inline style attributes paragraph and button. #3669

Closed
Closed
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 blocks/api/categories.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const categories = [
{ slug: 'widgets', title: __( 'Widgets' ) },
{ slug: 'embed', title: __( 'Embed' ) },
{ slug: 'reusable-blocks', title: __( 'My Reusable Blocks' ) },
{ slug: 'hidden', title: __( 'Hidden blocks' ) },
];

/**
Expand Down
2 changes: 1 addition & 1 deletion blocks/api/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ export function createBlockWithFallback( name, innerHTML, attributes ) {

// Include in set only if type were determined.
// TODO do we ever expect there to not be an unknown type handler?
if ( blockType && ( innerHTML || name !== fallbackBlock ) ) {
if ( blockType && ( innerHTML || name !== fallbackBlock ) && ! blockType.isComputedBlock ) {
// TODO allow blocks to opt-in to receiving a tree instead of a string.
// Gradually convert all blocks to this new format, then remove the
// string serialization.
Expand Down
26 changes: 24 additions & 2 deletions blocks/api/serializer.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
import { isEmpty, reduce, isObject, castArray, compact, startsWith } from 'lodash';
import { assign, isEmpty, reduce, isObject, castArray, compact, startsWith, values } from 'lodash';
import { html as beautifyHtml } from 'js-beautify';

/**
Expand All @@ -13,6 +13,7 @@ import { Component, createElement, renderToString, cloneElement, Children } from
* Internal dependencies
*/
import { getBlockType, getUnknownTypeHandlerName } from './registration';
import { createBlock } from './factory';
import { applyFilters } from '../hooks';

/**
Expand Down Expand Up @@ -214,12 +215,33 @@ export function serializeBlock( block ) {
}
}

export function serializeStyles( blocks ) {
const customStyles = values( reduce( blocks, ( memo, block ) => {
const blockName = block.name;
const blockType = getBlockType( blockName );
if ( blockType && blockType.saveStyles ) {
const { attributes } = block;
const blockStyles = blockType.saveStyles( { attributes } );
if ( blockStyles ) {
return assign( memo, blockStyles );
}
}
return memo;
}, {} ) ).join( '' );
if ( customStyles ) {
return serializeBlock( createBlock( 'core/style', {
content: customStyles,
} ) ).replace( /\n\n/g, '\n' );
}
return '';
}

/**
* Takes a block or set of blocks and returns the serialized post content.
*
* @param {Array} blocks Block(s) to serialize
* @return {String} The post content
*/
export default function serialize( blocks ) {
return castArray( blocks ).map( serializeBlock ).join( '\n\n' );
return `${ castArray( blocks ).map( serializeBlock ).join( '\n\n' ) }\n${ serializeStyles( blocks ) }`;
}
23 changes: 20 additions & 3 deletions blocks/library/button/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/**
* External dependencies
*/
import classnames from 'classnames';
/**
* WordPress dependencies
*/
Expand All @@ -11,6 +15,7 @@ import { Dashicon, IconButton, PanelColor, withFallbackStyles } from '@wordpress
import './editor.scss';
import './style.scss';
import { registerBlockType } from '../../api';
import { generateClassBackgroundColor, generateClassColor, generateStyleBackgroundColor, generateStyleColor } from '../../style-generator';
import Editable from '../../editable';
import UrlInput from '../../url-input';
import BlockControls from '../../block-controls';
Expand Down Expand Up @@ -202,13 +207,25 @@ registerBlockType( 'core/button', {

save( { attributes } ) {
const { url, text, title, align, color, textColor } = attributes;

const className = classnames( {
[ `align${ align }` ]: align,
[ generateClassBackgroundColor( color ) ]: color,
} );
return (
<div className={ `align${ align }` } style={ { backgroundColor: color } }>
<a href={ url } title={ title } style={ { color: textColor } }>
<div className={ className ? className : undefined }>
<a href={ url } className={ textColor ? `custom-color ${ generateClassColor( textColor ) }` : undefined } title={ title }>
{ text }
</a>
</div>
);
},

saveStyles( { attributes } ) {
const { color, textColor } = attributes;
return {
...generateStyleBackgroundColor( color ),
...generateStyleColor( textColor ),
};
},

} );
5 changes: 4 additions & 1 deletion blocks/library/button/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@ $blocks-button__height: 46px;

a {
box-shadow: none !important;
color: $white;
cursor: pointer;
text-decoration: none !important;
}

a:not(.custom-color) {
color: $white;
}

&.aligncenter {
display: inline-block;
}
Expand Down
1 change: 1 addition & 0 deletions blocks/library/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ import './video';
import './audio';
import './reusable-block';
import './paragraph';
import './style';
21 changes: 18 additions & 3 deletions blocks/library/paragraph/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import './editor.scss';
import './style.scss';
import { registerBlockType, createBlock, setDefaultBlockName } from '../../api';
import { blockAutocompleter, userAutocompleter } from '../../autocompleters';
import { generateClass, generateStyle } from '../../style-generator';
import AlignmentToolbar from '../../alignment-toolbar';
import BlockAlignmentToolbar from '../../block-alignment-toolbar';
import BlockControls from '../../block-controls';
Expand Down Expand Up @@ -265,20 +266,34 @@ registerBlockType( 'core/paragraph', {

save( { attributes } ) {
const { width, align, content, dropCap, backgroundColor, textColor, fontSize } = attributes;
const styles = {
backgroundColor: backgroundColor,
color: textColor,
fontSize: fontSize && fontSize + 'px',
textAlign: align,
};
const customClass = generateClass( styles, 'custom-paragraph-' );
const className = classnames( {
[ `align${ width }` ]: width,
'has-background': backgroundColor,
'has-drop-cap': dropCap,
[ customClass ]: customClass,
} );

return <p className={ className ? className : undefined }>{ content }</p>;
},

saveStyles( { attributes } ) {
Copy link
Member

@gziolo gziolo Nov 27, 2017

Choose a reason for hiding this comment

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

What if the user picks white as color and black as background-color for both button and paragraph? Shouldn't we share the same class names for both blocks? With the current implementation, we will create one combined class for all properties that paragraph has.

In other words, if it would work also to have:

<p class="color-white background-color-black" />

when user picks the colors specified for the theme.

Copy link
Member Author

Choose a reason for hiding this comment

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

We can also use in the paragraph the same approach as in the button. So classes can be reused in different block types, but for now, I intentionally used different approaches so we can see what is possible. The plan for colors from the theme and default Gutenberg palette is to use a set of classes this will only be used for colors outside the ones in the palette :)

const { align, backgroundColor, textColor, fontSize } = attributes;
const styles = {
backgroundColor: backgroundColor,
color: textColor,
fontSize: fontSize,
fontSize: fontSize && fontSize + 'px',
textAlign: align,
};

return <p style={ styles } className={ className ? className : undefined }>{ content }</p>;
return generateStyle( styles, generateClass( styles, 'custom-paragraph-' ) );
},

} );

setDefaultBlockName( 'core/paragraph' );
36 changes: 36 additions & 0 deletions blocks/library/style/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import { registerBlockType } from '../../api';

registerBlockType( 'core/style', {
title: __( 'Style' ),

icon: 'editor-code',

category: 'hidden',

attributes: {
content: {
type: 'string',
source: 'html',
},
},

isComputedBlock: true,

supportHTML: true,

edit( ) {
return [];
},

save( { attributes } ) {
return `<style type="text/css">${ attributes.content }</style>\n`;
},
} );
49 changes: 49 additions & 0 deletions blocks/style-generator/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* External dependencies
*/
import {
kebabCase,
reduce,
} from 'lodash';

/**
* WordPress dependencies
*/
import { hash, hashColor } from '@wordpress/utils';

export function generateClass( styleObject, prefix = '' ) {
const hashString = hash( styleObject );
return hashString ? prefix + hashString : undefined;
}

export function generateStyle( styleObject, className ) {
if ( ! styleObject ) {
return undefined;
}
const styleString = reduce( styleObject, ( memo, value, property ) => (
value ? `${ memo }\n\t${ kebabCase( property ) }: ${ value };` : memo
), '' );
if ( ! styleString ) {
return undefined;
}
const useClass = className || generateClass( styleObject );
return {
[ useClass ]: `.${ useClass } {${ styleString }\n}`,
};
}

export function generateClassBackgroundColor( color ) {
return color ? `background-color-${ hashColor( color ) }` : undefined;
}

export function generateStyleBackgroundColor( color, className ) {
return color ? generateStyle( { backgroundColor: color }, className || generateClassBackgroundColor( color ) ) : undefined;
}

export function generateClassColor( color ) {
return color ? `color-${ hashColor( color ) }` : undefined;
}

export function generateStyleColor( color, className ) {
return color ? generateStyle( { color }, className || generateClassColor( color ) ) : undefined;
}
2 changes: 1 addition & 1 deletion editor/components/inserter/menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ export class InserterMenu extends Component {
}

renderCategories( visibleBlocksByCategory ) {
return getCategories().map(
return getCategories().filter( category => category.slug !== 'hidden' ).map(
( category ) => this.renderCategory( category, visibleBlocksByCategory[ category.slug ] )
);
}
Expand Down
5 changes: 5 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"dom-scroll-into-view": "1.2.1",
"element-closest": "2.0.2",
"escape-string-regexp": "1.0.5",
"hash-string": "1.0.0",
"hpq": "1.2.0",
"jed": "1.1.1",
"js-beautify": "1.6.14",
Expand Down
27 changes: 27 additions & 0 deletions utils/hash.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* External dependencies
*/
import tinycolor from 'tinycolor2';
import hashString from 'hash-string';
import {
reduce,
some,
} from 'lodash';

export function hash( collection ) {
if ( ! some( collection ) ) {
return undefined;
}
return hashString(
reduce( collection, ( memo, value ) => memo + value, '' )
).toString( 36 );
}

export function hashColor( color ) {
const tinyColor = tinycolor( color );
const colorName = tinyColor.toName();
if ( colorName ) {
return colorName;
}
return hash( { color } );
}
1 change: 1 addition & 0 deletions utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ export { decodeEntities };

export * from './blob-cache';
export * from './mediaupload';
export * from './hash';

export { viewPort };