Skip to content

Commit

Permalink
Block Editor: Implement new colors hook. (#16781)
Browse files Browse the repository at this point in the history
* Block Editor: Implement new colors hook.

* Block Library: Swap usage of the colors HOC with the colors hook in the heading edit component.

* Use Colors: Add 'has-x-color' class names.

* Use Colors: Avoid memory leaks by making caches limited in size, and tied to hook instances.

* Use Colors: Support children and optional contrast checking in the color panel.

* Use Colors: Expose colors panel without inspector slot/fill wrapper.

* Use Colors: Mark hook as experimental.

* Use Colors: Support custom colors.

* Block Edit: Remove extra context values and use selectors/actions instead.

* Heading: Remove unnecessary color class and set text color on save.

* Use Colors: Add custom/preset color logic.

* Use Colors: Fix panel bugs.

* Heading Block: Detect actual background color for contrast checking.

* Block Edit: Add new export to native file.

* Use Colors: Change CSS "attribute" to "property".
  • Loading branch information
epiqueras authored and hypest committed Nov 4, 2019
1 parent 8d0e5aa commit 9781a02
Show file tree
Hide file tree
Showing 8 changed files with 270 additions and 66 deletions.
4 changes: 4 additions & 0 deletions packages/block-editor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,10 @@ _Related_

- <https://github.com/WordPress/gutenberg/blob/master/packages/block-editor/src/components/url-popover/README.md>

<a name="useBlockEditContext" href="#useBlockEditContext">#</a> **useBlockEditContext**

Undocumented declaration.

<a name="Warning" href="#Warning">#</a> **Warning**

Undocumented declaration.
Expand Down
8 changes: 6 additions & 2 deletions packages/block-editor/src/components/block-edit/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,22 @@ import { noop } from 'lodash';
/**
* WordPress dependencies
*/
import { createContext } from '@wordpress/element';
import { createContext, useContext } from '@wordpress/element';
import { createHigherOrderComponent } from '@wordpress/compose';

const { Consumer, Provider } = createContext( {
const Context = createContext( {
name: '',
isSelected: false,
focusedElement: null,
setFocusedElement: noop,
clientId: null,
} );
const { Provider, Consumer } = Context;

export { Provider as BlockEditContextProvider };
export function useBlockEditContext() {
return useContext( Context );
}

/**
* A Higher Order Component used to inject BlockEdit context to the
Expand Down
3 changes: 2 additions & 1 deletion packages/block-editor/src/components/block-edit/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { Component } from '@wordpress/element';
* Internal dependencies
*/
import Edit from './edit';
import { BlockEditContextProvider } from './context';
import { BlockEditContextProvider, useBlockEditContext } from './context';

class BlockEdit extends Component {
constructor() {
Expand Down Expand Up @@ -44,3 +44,4 @@ class BlockEdit extends Component {
}

export default BlockEdit;
export { useBlockEditContext };
1 change: 1 addition & 0 deletions packages/block-editor/src/components/colors/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export {
createCustomColorsHOC,
default as withColors,
} from './with-colors';
export { default as __experimentalUseColors } from './use-colors';
207 changes: 207 additions & 0 deletions packages/block-editor/src/components/colors/use-colors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
/**
* External dependencies
*/
import memoize from 'memize';
import { kebabCase, camelCase, startCase } from 'lodash';

/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { useSelect, useDispatch } from '@wordpress/data';
import {
useCallback,
useMemo,
Children,
cloneElement,
} from '@wordpress/element';

/**
* Internal dependencies
*/
import PanelColorSettings from '../panel-color-settings';
import ContrastChecker from '../contrast-checker';
import InspectorControls from '../inspector-controls';
import { useBlockEditContext } from '../block-edit';

const ColorPanel = ( {
title,
colorSettings,
colorPanelProps,
contrastCheckerProps,
components,
panelChildren,
} ) => (
<PanelColorSettings
title={ title }
initialOpen={ false }
colorSettings={ colorSettings }
{ ...colorPanelProps }
>
{ contrastCheckerProps &&
components.map( ( Component, index ) => (
<ContrastChecker
key={ Component.displayName }
textColor={ colorSettings[ index ].value }
{ ...contrastCheckerProps }
/>
) ) }
{ typeof panelChildren === 'function' ?
panelChildren( components ) :
panelChildren }
</PanelColorSettings>
);
const InspectorControlsColorPanel = ( props ) => (
<InspectorControls>
<ColorPanel { ...props } />
</InspectorControls>
);

export default function __experimentalUseColors(
colorConfigs,
{
panelTitle = __( 'Color Settings' ),
colorPanelProps,
contrastCheckerProps,
panelChildren,
} = {
panelTitle: __( 'Color Settings' ),
},
deps = []
) {
const { clientId } = useBlockEditContext();
const { attributes, settingsColors } = useSelect(
( select ) => {
const { getBlockAttributes, getSettings } = select( 'core/block-editor' );
return {
attributes: getBlockAttributes( clientId ),
settingsColors: getSettings().colors,
};
},
[ clientId ]
);
const { updateBlockAttributes } = useDispatch( 'core/block-editor' );
const setAttributes = useCallback(
( newAttributes ) => updateBlockAttributes( clientId, newAttributes ),
[ updateBlockAttributes, clientId ]
);

const createComponent = useMemo(
() =>
memoize(
( property, color, colorValue, customColor ) => ( { children } ) =>
// Clone children, setting the style property from the color configuration,
// if not already set explicitly through props.
Children.map( children, ( child ) => {
let className = child.props.className;
let style = child.props.style;
if ( color ) {
className = `${ child.props.className } has-${ kebabCase(
color
) }-${ kebabCase( property ) }`;
style = { [ property ]: colorValue, ...child.props.style };
} else if ( customColor ) {
className = `${ child.props.className } has-${ kebabCase( property ) }`;
style = { [ property ]: customColor, ...child.props.style };
}
return cloneElement( child, {
className,
style,
} );
} ),
{ maxSize: colorConfigs.length }
),
[ colorConfigs.length ]
);
const createSetColor = useMemo(
() =>
memoize(
( name, colors ) => ( newColor ) => {
const color = colors.find( ( _color ) => _color.color === newColor );
setAttributes( {
[ color ? camelCase( `custom ${ name }` ) : name ]: undefined,
} );
setAttributes( {
[ color ? name : camelCase( `custom ${ name }` ) ]: color ?
color.slug :
newColor,
} );
},
{
maxSize: colorConfigs.length,
}
),
[ setAttributes, colorConfigs.length ]
);

return useMemo( () => {
const colorSettings = [];

const components = colorConfigs.reduce( ( acc, colorConfig ) => {
if ( typeof colorConfig === 'string' ) {
colorConfig = { name: colorConfig };
}
const {
name, // E.g. 'backgroundColor'.
property = name, // E.g. 'backgroundColor'.

panelLabel = startCase( name ), // E.g. 'Background Color'.
componentName = panelLabel.replace( /\s/g, '' ), // E.g. 'BackgroundColor'.

color = colorConfig.color,
colors = settingsColors,
} = {
...colorConfig,
color: attributes[ colorConfig.name ],
};

// We memoize the non-primitives to avoid unnecessary updates
// when they are used as props for other components.
const _color = colors.find( ( __color ) => __color.slug === color );
acc[ componentName ] = createComponent(
property,
color,
_color && _color.color,
attributes[ camelCase( `custom ${ name }` ) ]
);
acc[ componentName ].displayName = componentName;
acc[ componentName ].color = color;
acc[ componentName ].setColor = createSetColor( name, colors );

const newSettingIndex =
colorSettings.push( {
value: _color ?
_color.color :
attributes[ camelCase( `custom ${ name }` ) ],
onChange: acc[ componentName ].setColor,
label: panelLabel,
colors,
} ) - 1;
// These settings will be spread over the `colors` in
// `colorPanelProps`, so we need to unset the key here,
// if not set to an actual value, to avoid overwriting
// an actual value in `colorPanelProps`.
if ( ! colors ) {
delete colorSettings[ newSettingIndex ].colors;
}

return acc;
}, {} );

const wrappedColorPanelProps = {
title: panelTitle,
colorSettings,
colorPanelProps,
contrastCheckerProps,
components: Object.values( components ),
panelChildren,
};
return {
...components,
ColorPanel: <ColorPanel { ...wrappedColorPanelProps } />,
InspectorControlsColorPanel: (
<InspectorControlsColorPanel { ...wrappedColorPanelProps } />
),
};
}, [ attributes, setAttributes, ...deps ] );
}
2 changes: 1 addition & 1 deletion packages/block-editor/src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export { default as Autocomplete } from './autocomplete';
export { default as BlockAlignmentToolbar } from './block-alignment-toolbar';
export { default as BlockBreadcrumb } from './block-breadcrumb';
export { default as BlockControls } from './block-controls';
export { default as BlockEdit } from './block-edit';
export { default as BlockEdit, useBlockEditContext } from './block-edit';
export { default as BlockFormatControls } from './block-format-controls';
export { default as BlockIcon } from './block-icon';
export { default as BlockNavigationDropdown } from './block-navigation/dropdown';
Expand Down
2 changes: 1 addition & 1 deletion packages/block-editor/src/components/index.native.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Block Creation Components
export { default as BlockAlignmentToolbar } from './block-alignment-toolbar';
export { default as BlockControls } from './block-controls';
export { default as BlockEdit } from './block-edit';
export { default as BlockEdit, useBlockEditContext } from './block-edit';
export { default as BlockFormatControls } from './block-format-controls';
export { default as BlockIcon } from './block-icon';
export { default as BlockVerticalAlignmentToolbar } from './block-vertical-alignment-toolbar';
Expand Down
Loading

0 comments on commit 9781a02

Please sign in to comment.