Skip to content

Commit

Permalink
Add global styles sidebar at edit site screen (#24250)
Browse files Browse the repository at this point in the history
  • Loading branch information
nosolosw authored Sep 8, 2020
1 parent f899b3e commit 5eed5c5
Show file tree
Hide file tree
Showing 11 changed files with 758 additions and 175 deletions.
81 changes: 47 additions & 34 deletions lib/global-styles.php
Original file line number Diff line number Diff line change
Expand Up @@ -349,23 +349,26 @@ function gutenberg_experimental_global_styles_get_block_data() {
is_string( $block_type->supports['__experimentalSelector'] )
) {
$block_data[ $block_name ] = array(
'selector' => $block_type->supports['__experimentalSelector'],
'supports' => $supports,
'selector' => $block_type->supports['__experimentalSelector'],
'supports' => $supports,
'blockName' => $block_name,
);
} elseif (
isset( $block_type->supports['__experimentalSelector'] ) &&
is_array( $block_type->supports['__experimentalSelector'] )
) {
foreach ( $block_type->supports['__experimentalSelector'] as $key => $selector ) {
$block_data[ $key ] = array(
'selector' => $selector,
'supports' => $supports,
'selector' => $selector,
'supports' => $supports,
'blockName' => $block_name,
);
}
} else {
$block_data[ $block_name ] = array(
'selector' => '.wp-block-' . str_replace( '/', '-', str_replace( 'core/', '', $block_name ) ),
'supports' => $supports,
'selector' => '.wp-block-' . str_replace( '/', '-', str_replace( 'core/', '', $block_name ) ),
'supports' => $supports,
'blockName' => $block_name,
);
}
}
Expand Down Expand Up @@ -411,7 +414,7 @@ function gutenberg_experimental_global_styles_flatten_styles_tree( $styles ) {
*
* @return string Stylesheet.
*/
function gutenberg_experimental_global_styles_resolver( $tree ) {
function gutenberg_experimental_global_styles_get_stylesheet( $tree ) {
$stylesheet = '';
$block_data = gutenberg_experimental_global_styles_get_block_data();
foreach ( array_keys( $tree ) as $block_name ) {
Expand Down Expand Up @@ -561,15 +564,14 @@ function gutenberg_experimental_global_styles_normalize_schema( $tree ) {
* Takes data from the different origins (core, theme, and user)
* and returns the merged result.
*
* @return array Merged trees.
* @return array Merged trees
*/
function gutenberg_experimental_global_styles_get_merged_origins() {
$core = gutenberg_experimental_global_styles_get_core();
$theme = gutenberg_experimental_global_styles_get_theme();
$user = gutenberg_experimental_global_styles_get_user();
$merged = gutenberg_experimental_global_styles_merge_trees( $core, $theme, $user );
$core = gutenberg_experimental_global_styles_get_core();
$theme = gutenberg_experimental_global_styles_get_theme();
$user = gutenberg_experimental_global_styles_get_user();

return $merged;
return gutenberg_experimental_global_styles_merge_trees( $core, $theme, $user );
}

/**
Expand All @@ -578,7 +580,7 @@ function gutenberg_experimental_global_styles_get_merged_origins() {
*/
function gutenberg_experimental_global_styles_enqueue_assets() {
$merged = gutenberg_experimental_global_styles_get_merged_origins();
$stylesheet = gutenberg_experimental_global_styles_resolver( $merged );
$stylesheet = gutenberg_experimental_global_styles_get_stylesheet( $merged );
if ( empty( $stylesheet ) ) {
return;
}
Expand Down Expand Up @@ -644,33 +646,44 @@ function gutenberg_experimental_global_styles_get_editor_features( $config ) {
* @return array New block editor settings
*/
function gutenberg_experimental_global_styles_settings( $settings ) {
$merged = gutenberg_experimental_global_styles_get_merged_origins();

if ( gutenberg_experimental_global_styles_has_theme_json_support() ) {
$settings['__experimentalGlobalStylesUserEntityId'] = gutenberg_experimental_global_styles_get_user_cpt_id();

$global_styles = gutenberg_experimental_global_styles_merge_trees(
gutenberg_experimental_global_styles_get_core(),
gutenberg_experimental_global_styles_get_theme()
);

$settings['__experimentalGlobalStylesBase'] = $global_styles;
}

// Add the styles for the editor via the settings
// so they get processed as if they were added via add_editor_styles:
// they will get the editor wrapper class.
$merged = gutenberg_experimental_global_styles_get_merged_origins();
$stylesheet = gutenberg_experimental_global_styles_resolver( $merged );
$settings['styles'][] = array( 'css' => $stylesheet );

// STEP 1: ADD FEATURES
// These need to be added to settings always.
// We also need to unset the deprecated settings defined by core.
$settings['__experimentalFeatures'] = gutenberg_experimental_global_styles_get_editor_features( $merged );

// Unsetting deprecated settings defined by Core.
unset( $settings['disableCustomColors'] );
unset( $settings['disableCustomGradients'] );
unset( $settings['disableCustomFontSizes'] );
unset( $settings['enableCustomLineHeight'] );

// STEP 2 - IF EDIT-SITE, ADD DATA REQUIRED FOR GLOBAL STYLES SIDEBAR
// The client needs some information to be able to access/update the user styles.
// We only do this if the theme has support for theme.json, though,
// as an indicator that the theme will know how to combine this with its stylesheet.
$screen = get_current_screen();
if (
! empty( $screen ) &&
function_exists( 'gutenberg_is_edit_site_page' ) &&
gutenberg_is_edit_site_page( $screen->id ) &&
gutenberg_experimental_global_styles_has_theme_json_support()
) {
$settings['__experimentalGlobalStylesUserEntityId'] = gutenberg_experimental_global_styles_get_user_cpt_id();
$settings['__experimentalGlobalStylesContexts'] = gutenberg_experimental_global_styles_get_block_data();
$settings['__experimentalGlobalStylesBaseStyles'] = gutenberg_experimental_global_styles_merge_trees(
gutenberg_experimental_global_styles_get_core(),
gutenberg_experimental_global_styles_get_theme()
);
} else {
// STEP 3 - OTHERWISE, ADD STYLES
//
// If we are in a block editor context, but not in edit-site,
// we need to add the styles via the settings. This is because
// we want them processed as if they were added via add_editor_styles,
// which adds the editor wrapper class.
$settings['styles'][] = array( 'css' => gutenberg_experimental_global_styles_get_stylesheet( $merged ) );
}

return $settings;
}

Expand Down
96 changes: 96 additions & 0 deletions packages/edit-site/src/components/editor/global-styles-provider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/**
* External dependencies
*/
import { set, get } from 'lodash';

/**
* WordPress dependencies
*/
import {
createContext,
useContext,
useEffect,
useMemo,
} from '@wordpress/element';
import { useEntityProp } from '@wordpress/core-data';

/**
* Internal dependencies
*/
import getGlobalStyles from './global-styles-renderer';

const GlobalStylesContext = createContext( {
/* eslint-disable no-unused-vars */
getProperty: ( context, path ) => {},
setProperty: ( context, newValues ) => {},
globalContext: {},
/* eslint-enable no-unused-vars */
} );

export const useGlobalStylesContext = () => useContext( GlobalStylesContext );

export default ( { children, baseStyles, contexts } ) => {
const [ content, setContent ] = useEntityProp(
'postType',
'wp_global_styles',
'content'
);

const userStyles = useMemo(
() => ( content ? JSON.parse( content ) : {} ),
[ content ]
);

const nextValue = useMemo(
() => ( {
contexts,
getProperty: ( context, path ) =>
get( userStyles?.[ context ]?.styles, path ),
setProperty: ( context, newValues ) => {
const newContent = { ...userStyles };
Object.keys( newValues ).forEach( ( key ) => {
set(
newContent,
`${ context }.styles.${ key }`,
newValues[ key ]
);
} );
setContent( JSON.stringify( newContent ) );
},
} ),
[ contexts, content ]
);

useEffect( () => {
if (
typeof contexts !== 'object' ||
typeof baseStyles !== 'object' ||
typeof userStyles !== 'object'
) {
return;
}

const embeddedStylesheetId = 'global-styles-inline-css';
let styleNode = document.getElementById( embeddedStylesheetId );

if ( ! styleNode ) {
styleNode = document.createElement( 'style' );
styleNode.id = embeddedStylesheetId;
document
.getElementsByTagName( 'head' )[ 0 ]
.appendChild( styleNode );
}

styleNode.innerText = getGlobalStyles(
contexts,
baseStyles,
userStyles
);
}, [ contexts, baseStyles, content ] );

return (
<GlobalStylesContext.Provider value={ nextValue }>
{ children }
</GlobalStylesContext.Provider>
);
};
121 changes: 121 additions & 0 deletions packages/edit-site/src/components/editor/global-styles-renderer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/**
* External dependencies
*/
import { get } from 'lodash';

/**
* Internal dependencies
*/
import {
STYLE_PROPS,
PRESET_CATEGORIES,
LINK_COLOR_DECLARATION,
} from './utils';

const mergeTrees = ( baseData, userData ) => {
// Deep clone from base data.
//
// We don't use cloneDeep from lodash here
// because we know the data is JSON compatible,
// see https://github.com/lodash/lodash/issues/1984
const mergedTree = JSON.parse( JSON.stringify( baseData ) );

const styleKeys = [ 'typography', 'color' ];
Object.keys( userData ).forEach( ( context ) => {
styleKeys.forEach( ( key ) => {
// Normalize object shape: make sure the key exists under styles.
if ( ! mergedTree[ context ].styles?.[ key ] ) {
mergedTree[ context ].styles[ key ] = {};
}

// Merge data: base + user.
mergedTree[ context ].styles[ key ] = {
...mergedTree[ context ].styles[ key ],
...userData[ context ]?.styles?.[ key ],
};
} );
} );

return mergedTree;
};

export default ( blockData, baseTree, userTree ) => {
const styles = [];
// Can this be converted to a context, as the global context?
// See comment in the server.
styles.push( LINK_COLOR_DECLARATION );
const tree = mergeTrees( baseTree, userTree );

/**
* Transform given style tree into a set of style declarations.
*
* @param {Object} blockSupports What styles the block supports.
* @param {Object} blockStyles Block styles.
*
* @return {Array} An array of style declarations.
*/
const getBlockStylesDeclarations = ( blockSupports, blockStyles ) => {
const declarations = [];
Object.keys( STYLE_PROPS ).forEach( ( key ) => {
if (
blockSupports.includes( key ) &&
get( blockStyles, STYLE_PROPS[ key ], false )
) {
declarations.push(
`${ key }: ${ get( blockStyles, STYLE_PROPS[ key ] ) }`
);
}
} );

return declarations;
};

/**
* Transform given preset tree into a set of style declarations.
*
* @param {Object} blockPresets
*
* @return {Array} An array of style declarations.
*/
const getBlockPresetsDeclarations = ( blockPresets ) => {
const declarations = [];
PRESET_CATEGORIES.forEach( ( category ) => {
if ( blockPresets?.[ category ] ) {
blockPresets[ category ].forEach( ( { slug, value } ) =>
declarations.push(
`--wp--preset--${ category }--${ slug }: ${ value }`
)
);
}
} );
return declarations;
};

const getBlockSelector = ( selector ) => {
// Can we hook into the styles generation mechanism
// so we can avoid having to increase the class specificity here
// and remap :root?
if ( ':root' === selector ) {
selector = '';
}
return `.editor-styles-wrapper.editor-styles-wrapper ${ selector }`;
};

Object.keys( blockData ).forEach( ( context ) => {
const blockSelector = getBlockSelector( blockData[ context ].selector );
const blockDeclarations = [
...getBlockStylesDeclarations(
blockData[ context ].supports,
tree[ context ].styles
),
...getBlockPresetsDeclarations( tree[ context ].presets ),
];
if ( blockDeclarations.length > 0 ) {
styles.push(
`${ blockSelector } { ${ blockDeclarations.join( ';' ) } }`
);
}
} );

return styles.join( '' );
};
Loading

0 comments on commit 5eed5c5

Please sign in to comment.