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

Add global styles sidebar at edit site screen #24250

Merged
merged 84 commits into from
Sep 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
4c50a5f
Typography panel: add line-height for the blocks that support it
oandregal Jun 22, 2020
c3d26b1
Show user data in controls
oandregal Jun 22, 2020
54c6116
Extract to own files
oandregal Jun 23, 2020
6ae71b6
Extract getters & setters to provider
oandregal Jun 23, 2020
4e96c1c
Edit entities when user interacts with controls
oandregal Jun 23, 2020
495be14
Pass the embedded stylesheet handle to the client
oandregal Jun 23, 2020
0d88467
Save proper value for font-size
oandregal Jun 23, 2020
6be29f1
Use edited data to feed controls & no overwrite existing data
oandregal Jun 23, 2020
0b952d7
Do not overwrite existing data from other styles
oandregal Jun 23, 2020
00c6126
Remove passing down handle as styles are processed via settings, not …
oandregal Jun 23, 2020
25cfb3a
Add effect hook to update styles
oandregal Jun 23, 2020
ac89dcf
Update stylesheet from user data
oandregal Jun 23, 2020
c0ef0b3
Account for blocks that do not define a particular property
oandregal Jun 23, 2020
4c1e497
Remove line breaks from stylesheet
oandregal Jun 23, 2020
62ded6b
Add skeleton for color panel
oandregal Jun 23, 2020
ac655f5
Remove base styles as we only need to generate user styles
oandregal Jun 23, 2020
0c4e8dc
Add colors
oandregal Jun 23, 2020
31a8b1a
Work only with published CPT for user data
oandregal Jun 23, 2020
f0b0293
Fix phpdocs
oandregal Jun 23, 2020
56850d5
Fix color names
oandregal Jun 23, 2020
f69f437
Compact API: expose only getProperty / setProperty
oandregal Jun 23, 2020
3c9b3a6
Create panels per block
oandregal Jun 23, 2020
7bc6cb7
Do not render heading block yet
oandregal Jun 23, 2020
3581e1f
Fix selector for core blocks
oandregal Jun 23, 2020
7b30c5d
PoC for global context
oandregal Jun 24, 2020
a57df47
Add typography panel
oandregal Jun 25, 2020
43fae0b
Use typography icon
oandregal Jun 25, 2020
a2c0860
Load global styles settings only in edit-site
oandregal Jun 25, 2020
c045d88
Generate stylesheet in the client - step 1
oandregal Jun 25, 2020
554a857
Generate stylesheet in the client - step 2
oandregal Jun 25, 2020
d7392b5
Add stylesheet for link color
oandregal Jun 25, 2020
3ad9f9b
Make order as front
oandregal Jun 25, 2020
8a4667b
Generate stylesheet in the client - step 3
oandregal Jun 25, 2020
91ede60
Disable font-size for the global level
oandregal Jun 25, 2020
db6c9dc
Make sure font-size is removed from the entity
oandregal Jun 25, 2020
072e5f4
Enable back font-size
oandregal Jun 25, 2020
7cd35cd
Fix rebase
oandregal Jul 21, 2020
f55fe3e
format-js
oandregal Jul 21, 2020
7d4389d
Show all blocks, including core/heading/*
oandregal Jul 21, 2020
e10e375
Show title per context
oandregal Jul 22, 2020
02af322
Destructure selector
oandregal Jul 22, 2020
33f20d4
Refactor to reduce the amount of data and extract global to constant
oandregal Jul 22, 2020
584c145
Inline function
oandregal Jul 23, 2020
a5bb681
Simplify code
oandregal Jul 23, 2020
279eadc
Simplify code for renderer
oandregal Jul 23, 2020
03fad42
Add prettier suggestion
oandregal Jul 23, 2020
cd55f33
Fix rebase
oandregal Aug 4, 2020
cdda0e2
Fix warnings
oandregal Aug 25, 2020
8ec4ca0
Add comment about not using cloneDeep
oandregal Aug 25, 2020
f16b7ac
Fix comment
oandregal Aug 25, 2020
f73e2bb
Compact code
oandregal Aug 25, 2020
3ceeac7
Make linter happy
oandregal Aug 25, 2020
ac1dadd
Make provider units agnostic
oandregal Aug 26, 2020
9d7abcf
Use EntityProvider to get/set the CPT
oandregal Aug 27, 2020
c97540c
Only process request if theme has theme.json support
oandregal Aug 27, 2020
8025a07
Do not try to generate stylesheet if we have no data
oandregal Aug 27, 2020
6a347b3
Do not show global styles sidebar if no proper data
oandregal Aug 27, 2020
ffce3e6
Extract TypographyPanel
oandregal Aug 27, 2020
2819d1b
Prettier changes
oandregal Aug 27, 2020
48ab467
Extract ColorPanel
oandregal Aug 27, 2020
36f55a3
Use ColorPanel in BlockPanel
oandregal Aug 27, 2020
43d4dab
Get rid of GlobalPanel & BlockPanel in favor of inlining the style co…
oandregal Aug 27, 2020
5d38eb0
Better variable name and comment
oandregal Aug 27, 2020
6a180f1
Reformat to make less diff changes
oandregal Aug 27, 2020
4f56221
Fix variable name
oandregal Aug 31, 2020
80b38a7
Use PanelColorGradientSettings
oandregal Aug 31, 2020
510181f
Add support for gradient colors
oandregal Sep 1, 2020
602920f
Simplify setProperty object merging
oandregal Sep 2, 2020
09dc3d9
Migrate setProperty to use path
oandregal Sep 2, 2020
c511af2
Migrate getProperty to use path
oandregal Sep 2, 2020
30b8bcf
Pass object to setProperty to allow updating multiple entries at once
oandregal Sep 2, 2020
027db4d
prettify
oandregal Sep 2, 2020
dae9269
Make gradients work
oandregal Sep 2, 2020
6dfc7e5
Allow background without gradient
oandregal Sep 2, 2020
422a359
Revert adding wrong check
oandregal Sep 3, 2020
43a6f12
Clarify gutenberg_experimental_global_styles_settings
oandregal Sep 3, 2020
63dc5f1
Remove incorrect comment
oandregal Sep 3, 2020
16ed391
Refactor GlobalStylesProvider
oandregal Sep 3, 2020
fbbac37
Fix function arguments
oandregal Sep 3, 2020
3491e92
Fix style effect
oandregal Sep 4, 2020
0248802
Disable custom gradients
oandregal Sep 7, 2020
b90617e
Prettify
oandregal Sep 7, 2020
76a68c3
Update close button label
oandregal Sep 7, 2020
20fcaaa
gutenberg_is_edit_site_page is only load when the experiment is active
oandregal Sep 8, 2020
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
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';
oandregal marked this conversation as resolved.
Show resolved Hide resolved
let styleNode = document.getElementById( embeddedStylesheetId );

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

styleNode.innerText = getGlobalStyles(
Copy link
Member

Choose a reason for hiding this comment

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

Not a blocker but I think when we start having many styles rendering every rule on each global style attribute change is going to be very costly. I wonder if we can avoid rerendering the rules that don't have any attribute changed. It is essentially what react does only render things on the dom when in fact the vdom of that node changed. I wonder if we can use react itself to do that for us. Maybe using multiple style elements that are rendered with react?
Anyway, it is just an idea to explore later.

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 ) );
oandregal marked this conversation as resolved.
Show resolved Hide resolved

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 );
oandregal marked this conversation as resolved.
Show resolved Hide resolved
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