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

Use Colors: Enhance contrast checking API. #18237

Merged
Merged
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
134 changes: 102 additions & 32 deletions packages/block-editor/src/components/colors/use-colors.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,7 @@
*/
import memoize from 'memize';
import classnames from 'classnames';
import {
camelCase,
kebabCase,
map,
startCase,
} from 'lodash';
import { map, kebabCase, camelCase, startCase } from 'lodash';

/**
* WordPress dependencies
Expand All @@ -20,7 +15,9 @@ import {
useMemo,
Children,
cloneElement,
useRef,
} from '@wordpress/element';
import { withFallbackStyles } from '@wordpress/components';

/**
* Internal dependencies
Expand All @@ -30,13 +27,28 @@ import ContrastChecker from '../contrast-checker';
import InspectorControls from '../inspector-controls';
import { useBlockEditContext } from '../block-edit';

/**
* Browser dependencies
*/
const { getComputedStyle } = window;

const DEFAULT_COLORS = [];

const resolveContrastCheckerColor = ( color, colorSettings, detectedColor ) => {
if ( typeof color === 'function' ) {
return color( colorSettings );
} else if ( color === true ) {
return detectedColor;
}
return color;
};

const ColorPanel = ( {
title,
colorSettings,
colorPanelProps,
contrastCheckerProps,
components,
contrastCheckers,
detectedBackgroundColor,
panelChildren,
} ) => (
<PanelColorSettings
Expand All @@ -45,16 +57,46 @@ const ColorPanel = ( {
colorSettings={ Object.values( colorSettings ) }
{ ...colorPanelProps }
>
{ contrastCheckerProps &&
map( components, ( ( Component, key ) => (
<ContrastChecker
key={ key }
textColor={ colorSettings[ key ].value }
{ ...contrastCheckerProps }
/>
) ) ) }
{ contrastCheckers &&
( Array.isArray( contrastCheckers ) ?
jorgefilipecosta marked this conversation as resolved.
Show resolved Hide resolved
contrastCheckers.map( ( { backgroundColor, textColor, ...rest } ) => {
backgroundColor = resolveContrastCheckerColor(
backgroundColor,
colorSettings,
detectedBackgroundColor
);
textColor = resolveContrastCheckerColor( textColor, colorSettings );
return (
<ContrastChecker
key={ `${ backgroundColor }-${ textColor }` }
backgroundColor={ backgroundColor }
textColor={ textColor }
{ ...rest }
/>
);
} ) :
map( colorSettings, ( { value } ) => {
let { backgroundColor, textColor } = contrastCheckers;
backgroundColor = resolveContrastCheckerColor(
backgroundColor || value,
colorSettings,
detectedBackgroundColor
);
textColor = resolveContrastCheckerColor(
textColor || value,
colorSettings
);
return (
<ContrastChecker
{ ...contrastCheckers }
key={ `${ backgroundColor }-${ textColor }` }
backgroundColor={ backgroundColor }
textColor={ textColor }
/>
);
} ) ) }
{ typeof panelChildren === 'function' ?
panelChildren( components ) :
panelChildren( colorSettings ) :
panelChildren }
</PanelColorSettings>
);
Expand All @@ -69,7 +111,7 @@ export default function __experimentalUseColors(
{
panelTitle = __( 'Color Settings' ),
colorPanelProps,
contrastCheckerProps,
contrastCheckers,
panelChildren,
} = {
panelTitle: __( 'Color Settings' ),
Expand Down Expand Up @@ -113,14 +155,10 @@ export default function __experimentalUseColors(
}

return cloneElement( child, {
className: classnames(
componentClassName,
child.props.className,
{
[ `has-${ kebabCase( color ) }-${ kebabCase( property ) }` ]: color,
[ className || `has-${ kebabCase( name ) }` ]: color || customColor,
}
),
className: classnames( componentClassName, child.props.className, {
[ `has-${ kebabCase( color ) }-${ kebabCase( property ) }` ]: color,
[ className || `has-${ kebabCase( name ) }` ]: color || customColor,
} ),
style: {
...colorStyle,
...componentStyle,
Expand Down Expand Up @@ -153,6 +191,39 @@ export default function __experimentalUseColors(
[ setAttributes, colorConfigs.length ]
);

const detectedBackgroundColorRef = useRef();
const BackgroundColorDetector = useMemo(
Copy link
Member

Choose a reason for hiding this comment

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

We are calling withFallbackStyles, even if the block does not use the detection functionality or the contrast functionality at all. Given that the reference is undefined, the withFallbackStyles will not do much. But, I still wonder, could we make these calls only if the background detection functionality is used?
We would an auxiliary component given than hooks always need to be called in the same order.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not in any straightforward way, because we want to return a component, not an element so that it can take props easily.

I made it so that withFallbackStyles is only called when needed so that we avoid the potentially expensive part which is creating the component.

() =>
contrastCheckers &&
( Array.isArray( contrastCheckers ) ?
contrastCheckers.some(
( { backgroundColor } ) => backgroundColor === true
) :
contrastCheckers.backgroundColor === true ) &&
withFallbackStyles( ( node, { querySelector } ) => {
if ( querySelector ) {
node = node.parentNode.querySelector( querySelector );
}
let backgroundColor = getComputedStyle( node ).backgroundColor;
while ( backgroundColor === 'rgba(0, 0, 0, 0)' && node.parentNode ) {
node = node.parentNode;
backgroundColor = getComputedStyle( node ).backgroundColor;
}
detectedBackgroundColorRef.current = backgroundColor;
return { backgroundColor };
} )( () => <></> ),
[
colorConfigs.reduce(
( acc, colorConfig ) =>
`${ acc } | ${ attributes[ colorConfig.name ] } | ${
attributes[ camelCase( `custom ${ colorConfig.name }` ) ]
}`,
''
),
...deps,
]
);

return useMemo( () => {
const colorSettings = {};

Expand Down Expand Up @@ -191,9 +262,7 @@ export default function __experimentalUseColors(
acc[ componentName ].setColor = createSetColor( name, colors );

colorSettings[ componentName ] = {
value: _color ?
_color.color :
attributes[ camelCase( `custom ${ name }` ) ],
value: _color ? _color.color : attributes[ camelCase( `custom ${ name }` ) ],
onChange: acc[ componentName ].setColor,
label: panelLabel,
colors,
Expand All @@ -213,8 +282,8 @@ export default function __experimentalUseColors(
title: panelTitle,
colorSettings,
colorPanelProps,
contrastCheckerProps,
components,
contrastCheckers,
detectedBackgroundColor: detectedBackgroundColorRef.current,
panelChildren,
};
return {
Expand All @@ -223,6 +292,7 @@ export default function __experimentalUseColors(
InspectorControlsColorPanel: (
<InspectorControlsColorPanel { ...wrappedColorPanelProps } />
),
BackgroundColorDetector,
};
}, [ attributes, setAttributes, ...deps ] );
}, [ attributes, setAttributes, detectedBackgroundColorRef.current, ...deps ] );
}
22 changes: 5 additions & 17 deletions packages/block-library/src/heading/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import HeadingToolbar from './heading-toolbar';
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { PanelBody, withFallbackStyles } from '@wordpress/components';
import { PanelBody } from '@wordpress/components';
import { createBlock } from '@wordpress/blocks';
import {
AlignmentToolbar,
Expand All @@ -22,23 +22,17 @@ import {
__experimentalUseColors,
} from '@wordpress/block-editor';

/**
* Browser dependencies
*/
const { getComputedStyle } = window;

function HeadingEdit( {
backgroundColor,
attributes,
setAttributes,
mergeBlocks,
onReplace,
className,
} ) {
const { TextColor, InspectorControlsColorPanel } = __experimentalUseColors(
const { TextColor, InspectorControlsColorPanel, BackgroundColorDetector } = __experimentalUseColors(
[ { name: 'textColor', property: 'color' } ],
{
contrastCheckerProps: { backgroundColor, isLargeText: true },
contrastCheckers: { backgroundColor: true },
},
[]
);
Expand All @@ -62,6 +56,7 @@ function HeadingEdit( {
</InspectorControls>
{ InspectorControlsColorPanel }
<TextColor>
<BackgroundColorDetector querySelector='[contenteditable="true"]' />
<RichText
identifier="content"
tagName={ tagName }
Expand Down Expand Up @@ -90,11 +85,4 @@ function HeadingEdit( {
);
}

export default withFallbackStyles( ( node ) => {
let backgroundColor = getComputedStyle( node ).backgroundColor;
while ( backgroundColor === 'rgba(0, 0, 0, 0)' && node.parentNode ) {
node = node.parentNode;
backgroundColor = getComputedStyle( node ).backgroundColor;
}
return { backgroundColor };
} )( HeadingEdit );
export default HeadingEdit;