Skip to content
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
54 changes: 42 additions & 12 deletions lib/block-supports/duotone.php
Original file line number Diff line number Diff line change
Expand Up @@ -311,9 +311,6 @@ function gutenberg_get_duotone_filter_id( $preset ) {
* @return string Duotone CSS filter property url value.
*/
function gutenberg_get_duotone_filter_property( $preset ) {
if ( isset( $preset['colors'] ) && 'unset' === $preset['colors'] ) {
return 'none';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I wish I would have used 'none' as the string instead of 'unset' so that string values could just be treated as CSS like the rest of the block supports. But for maintaining backwards compatibility, I don't think that's really an option anymore.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Just so you know this return value is preserved but I moved it outside of this function so it doesn't need colors in the "preset".

}
$filter_id = gutenberg_get_duotone_filter_id( $preset );
return "url('#" . $filter_id . "')";
}
Expand Down Expand Up @@ -445,15 +442,45 @@ function gutenberg_render_duotone_support( $block_content, $block ) {
return $block_content;
}

$colors = $block['attrs']['style']['color']['duotone'];
$filter_key = is_array( $colors ) ? implode( '-', $colors ) : $colors;
$filter_preset = array(
'slug' => wp_unique_id( sanitize_key( $filter_key . '-' ) ),
'colors' => $colors,
);
$filter_property = gutenberg_get_duotone_filter_property( $filter_preset );
$filter_id = gutenberg_get_duotone_filter_id( $filter_preset );
// Possible values for duotone attribute:
// 1. Array of colors - e.g. array('#000000', '#ffffff').
// 2. Slug of an existing Duotone preset - e.g. 'green-blue'.
// 3. The string 'unset' - indicates explicitly "no Duotone"..
$duotone_attr = $block['attrs']['style']['color']['duotone'];

$is_duotone_colors_array = is_array( $duotone_attr );
$is_duotone_unset = 'unset' === $duotone_attr;
$is_duotone_preset = ! $is_duotone_colors_array && ! $is_duotone_unset;

if ( $is_duotone_preset ) {
$filter_preset = array(
'slug' => $duotone_attr,
);

// Utilise existing CSS custom property.
$filter_property = "var(--wp--preset--duotone--$duotone_attr)";
} else {
// Handle when Duotone is either:
// - "unset"
// - an array of colors.

// Build a unique slug for the filter based on the array of colors.
$filter_key = $is_duotone_colors_array ? implode( '-', $duotone_attr ) : $duotone_attr;
$filter_preset = array(
'slug' => wp_unique_id( sanitize_key( $filter_key . '-' ) ),
'colors' => $duotone_attr, // required for building the SVG with gutenberg_get_duotone_filter_svg.
);

// Build a customised CSS filter property for unique slug.
$filter_property = $is_duotone_unset ? 'none' : gutenberg_get_duotone_filter_property( $filter_preset );
}

// - Applied as a class attribute to the block wrapper.
// - Used as a selector to apply the filter to the block.
$filter_id = gutenberg_get_duotone_filter_id( $filter_preset );

// Build the CSS selectors to which the filter will be applied.
// Todo - encapsulate this in a function.
$scope = '.' . $filter_id;
$selectors = explode( ',', $duotone_support );
$scoped = array();
Expand Down Expand Up @@ -483,8 +510,11 @@ function gutenberg_render_duotone_support( $block_content, $block ) {
)
);

if ( 'unset' !== $colors ) {
// For *non*-presets then generate an SVG for the filter.
// Note: duotone presets are already pre-generated so no need to do this again.
if ( $is_duotone_colors_array ) {
$filter_svg = gutenberg_get_duotone_filter_svg( $filter_preset );

add_action(
'wp_footer',
static function () use ( $filter_svg, $selector ) {
Expand Down
59 changes: 53 additions & 6 deletions packages/block-editor/src/hooks/duotone.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,34 @@ function useMultiOriginPresets( { presetSetting, defaultSetting } ) {
);
}

export function getColorsFromDuotonePreset( duotone, duotonePalette ) {
if ( ! duotone ) {
return;
}
const preset = duotonePalette?.find( ( { slug } ) => {
return slug === duotone;
} );

return preset ? preset.colors : undefined;
}

export function getDuotonePresetFromColors( colors, duotonePalette ) {
if ( ! colors || ! Array.isArray( colors ) ) {
return;
}

const preset = duotonePalette?.find( ( duotonePreset ) => {
return duotonePreset?.colors?.every(
( val, index ) => val === colors[ index ]
);
} );

return preset ? preset.slug : undefined;
}

function DuotonePanel( { attributes, setAttributes } ) {
const style = attributes?.style;
const duotone = style?.color?.duotone;
const duotoneStyle = style?.color?.duotone;

const duotonePalette = useMultiOriginPresets( {
presetSetting: 'color.duotone',
Expand All @@ -96,20 +121,29 @@ function DuotonePanel( { attributes, setAttributes } ) {
return null;
}

const duotonePresetOrColors = ! Array.isArray( duotoneStyle )
? getColorsFromDuotonePreset( duotoneStyle, duotonePalette )
: duotoneStyle;

return (
<BlockControls group="block" __experimentalShareWithChildBlocks>
<DuotoneControl
duotonePalette={ duotonePalette }
colorPalette={ colorPalette }
disableCustomDuotone={ disableCustomDuotone }
disableCustomColors={ disableCustomColors }
value={ duotone }
value={ duotonePresetOrColors }
onChange={ ( newDuotone ) => {
const maybePreset = getDuotonePresetFromColors(
newDuotone,
duotonePalette
);

const newStyle = {
...style,
color: {
...style?.color,
duotone: newDuotone,
duotone: maybePreset ?? newDuotone, // use preset or fallback to custom colors.
},
};
setAttributes( { style: newStyle } );
Expand Down Expand Up @@ -224,14 +258,27 @@ const withDuotoneStyles = createHigherOrderComponent(
props.name,
'color.__experimentalDuotone'
);
const colors = props?.attributes?.style?.color?.duotone;
const duotonePalette = useMultiOriginPresets( {
presetSetting: 'color.duotone',
defaultSetting: 'color.defaultDuotone',
} );

const id = `wp-duotone-${ useInstanceId( BlockListBlock ) }`;

let colors = props?.attributes?.style?.color?.duotone;

if ( ! Array.isArray( colors ) ) {
const duotone = duotonePalette.find( ( dt ) => dt.slug === colors );

if ( duotone ) {
colors = duotone.colors;
}
}

if ( ! duotoneSupport || ! colors ) {
return <BlockListBlock { ...props } />;
}

const id = `wp-duotone-${ useInstanceId( BlockListBlock ) }`;

// Extra .editor-styles-wrapper specificity is needed in the editor
// since we're not using inline styles to apply the filter. We need to
// override duotone applied by global styles and theme.json.
Expand Down
99 changes: 99 additions & 0 deletions packages/block-editor/src/hooks/test/duotone.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/**
* Internal dependencies
*/
import {
getColorsFromDuotonePreset,
getDuotonePresetFromColors,
} from '../duotone';

describe( 'Duotone utilities', () => {
const duotonePalette = [
{
name: 'Dark grayscale',
colors: [ '#000000', '#7f7f7f' ],
slug: 'dark-grayscale',
},
{
name: 'Grayscale',
colors: [ '#000000', '#ffffff' ],
slug: 'grayscale',
},
{
name: 'Purple and yellow',
colors: [ '#8c00b7', '#fcff41' ],
slug: 'purple-yellow',
},
];
describe( 'getColorsFromDuotonePreset', () => {
it( 'should return undefined if no arguments are provided', () => {
expect( getColorsFromDuotonePreset() ).toBeUndefined();
} );

it( 'should return undefined if no duotone preset is provided', () => {
expect(
getColorsFromDuotonePreset( undefined, duotonePalette )
).toBeUndefined();
} );

it( 'should return undefined if a non-existent preset is provided', () => {
expect(
getColorsFromDuotonePreset( 'does-not-exist', duotonePalette )
).toBeUndefined();
} );

it( 'should return the colors from the preset if found', () => {
expect(
getColorsFromDuotonePreset(
duotonePalette[ 2 ].slug,
duotonePalette
)
).toEqual( duotonePalette[ 2 ].colors );
} );
} );

describe( 'getDuotonePresetFromColors', () => {
it( 'should return undefined if no arguments are provided', () => {
expect( getDuotonePresetFromColors() ).toBeUndefined();
} );

it( 'should return undefined if no colors are provided', () => {
expect(
getDuotonePresetFromColors( undefined, duotonePalette )
).toBeUndefined();
} );

it( 'should return undefined if provided colors is not of valid type', () => {
const notAnArrayOfColorStrings = 'purple-yellow';
expect(
getDuotonePresetFromColors(
notAnArrayOfColorStrings,
duotonePalette
)
).toBeUndefined();
} );

it( 'should return undefined if no duotone palette is provided', () => {
expect(
getDuotonePresetFromColors( [ '#8c00b7', '#fcff41' ] )
).toBeUndefined();
} );

it( 'should return undefined if the provided colors do not match any preset', () => {
expect(
getDuotonePresetFromColors(
[ '#000000', '#000000' ],
duotonePalette
)
).toBeUndefined();
} );

it( 'should return the slug of the preset if found', () => {
expect(
getDuotonePresetFromColors(
duotonePalette[ 2 ].colors,
duotonePalette
)
).toEqual( duotonePalette[ 2 ].slug );
} );
} );
} );