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 ability to unset duotone in themes with default duotone set #39564

Merged
merged 12 commits into from
Jun 20, 2022
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
53 changes: 32 additions & 21 deletions lib/block-supports/duotone.php
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,9 @@ 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';
}
$filter_id = gutenberg_get_duotone_filter_id( $preset );
return "url('#" . $filter_id . "')";
}
Expand Down Expand Up @@ -442,13 +445,14 @@ 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( implode( '-', $block['attrs']['style']['color']['duotone'] ) . '-' ) ),
'colors' => $block['attrs']['style']['color']['duotone'],
'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 );
$filter_svg = gutenberg_get_duotone_filter_svg( $filter_preset );

$scope = '.' . $filter_id;
$selectors = explode( ',', $duotone_support );
Expand All @@ -468,25 +472,32 @@ function gutenberg_render_duotone_support( $block_content, $block ) {
wp_add_inline_style( $filter_id, $filter_style );
wp_enqueue_style( $filter_id );

add_action(
'wp_footer',
static function () use ( $filter_svg, $selector ) {
echo $filter_svg;

// Safari renders elements incorrectly on first paint when the SVG
// filter comes after the content that it is filtering, so we force
// a repaint with a WebKit hack which solves the issue.
global $is_safari;
if ( $is_safari ) {
printf(
// Simply accessing el.offsetHeight flushes layout and style
// changes in WebKit without having to wait for setTimeout.
'<script>( function() { var el = document.querySelector( %s ); var display = el.style.display; el.style.display = "none"; el.offsetHeight; el.style.display = display; } )();</script>',
wp_json_encode( $selector )
);
if ( 'unset' !== $colors ) {
$filter_svg = gutenberg_get_duotone_filter_svg( $filter_preset );
add_action(
'wp_footer',
static function () use ( $filter_svg, $selector ) {
echo $filter_svg;

/*
* Safari renders elements incorrectly on first paint when the
* SVG filter comes after the content that it is filtering, so
* we force a repaint with a WebKit hack which solves the issue.
*/
global $is_safari;
if ( $is_safari ) {
/*
* Simply accessing el.offsetHeight flushes layout and style
* changes in WebKit without having to wait for setTimeout.
*/
printf(
'<script>( function() { var el = document.querySelector( %s ); var display = el.style.display; el.style.display = "none"; el.offsetHeight; el.style.display = display; } )();</script>',
wp_json_encode( $selector )
);
}
}
}
);
);
}

// Like the layout hook, this assumes the hook only applies to blocks with a single wrapper.
return preg_replace(
Expand Down
19 changes: 12 additions & 7 deletions packages/block-editor/src/components/duotone-control/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* WordPress dependencies
*/
import {
ColorIndicator,
Dropdown,
DuotonePicker,
DuotoneSwatch,
Expand All @@ -20,6 +21,16 @@ function DuotoneControl( {
value,
onChange,
} ) {
let toolbarIcon;
if ( value === 'unset' ) {
toolbarIcon = (
<ColorIndicator className="block-editor-duotone-control__unset-indicator" />
);
} else if ( value ) {
toolbarIcon = <DuotoneSwatch values={ value } />;
} else {
toolbarIcon = <Icon icon={ filter } />;
}
return (
<Dropdown
popoverProps={ {
Expand All @@ -42,13 +53,7 @@ function DuotoneControl( {
aria-expanded={ isOpen }
onKeyDown={ openOnArrowDown }
label={ __( 'Apply duotone filter' ) }
icon={
value ? (
<DuotoneSwatch values={ value } />
) : (
<Icon icon={ filter } />
)
}
icon={ toolbarIcon }
/>
);
} }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,8 @@ $swatch-columns: math.floor(math.div($popover-width + $swatch-gap - 2 * $popover
margin: $grid-unit-20 0;
font-size: $helptext-font-size;
}

.block-editor-duotone-control__unset-indicator {
// Show a diagonal line (crossed out) for empty swatches.
background: linear-gradient(-45deg, transparent 48%, $gray-300 48%, $gray-300 52%, transparent 52%);
}
68 changes: 40 additions & 28 deletions packages/block-editor/src/hooks/duotone.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,43 +50,51 @@ export function getValuesFromColors( colors = [] ) {
}

/**
* Values for the SVG `feComponentTransfer`.
* SVG and stylesheet needed for rendering the duotone filter.
*
* @param {Object} props Duotone props.
* @param {string} props.selector Selector to apply the filter to.
* @param {string} props.id Unique id for this duotone filter.
*
* @typedef Values {Object}
* @property {number[]} r Red values.
* @property {number[]} g Green values.
* @property {number[]} b Blue values.
* @property {number[]} a Alpha values.
* @return {WPElement} Duotone element.
*/
function DuotoneStylesheet( { selector, id } ) {
const css = `
${ selector } {
filter: url( #${ id } );
}
`;
return <style>{ css }</style>;
}

/**
* Stylesheet for rendering the duotone filter.
* Stylesheet for disabling a global styles duotone filter.
*
* @param {Object} props Duotone props.
* @param {string} props.selector Selector to apply the filter to.
* @param {string} props.id Unique id for this duotone filter.
* @param {Object} props Duotone props.
* @param {string} props.selector Selector to disable the filter for.
*
* @return {WPElement} Duotone element.
* @return {WPElement} Filter none style element.
*/
function DuotoneStylesheet( { selector, id } ) {
function DuotoneUnsetStylesheet( { selector } ) {
const css = `
${ selector } {
filter: url( #${ id } );
filter: none;
}
`;
return <style>{ css }</style>;
}

/**
* SVG for rendering the duotone filter.
* The SVG part of the duotone filter.
*
* @param {Object} props Duotone props.
* @param {string} props.id Unique id for this duotone filter.
* @param {Values} props.values R, G, B, and A values to filter with.
* @param {Object} props Duotone props.
* @param {string} props.id Unique id for this duotone filter.
* @param {string[]} props.colors Color strings from dark to light.
*
* @return {WPElement} Duotone element.
* @return {WPElement} Duotone SVG.
*/
function DuotoneFilter( { id, values } ) {
function DuotoneFilter( { id, colors } ) {
const values = getValuesFromColors( colors );
return (
<SVG
xmlnsXlink="http://www.w3.org/1999/xlink"
Expand Down Expand Up @@ -151,17 +159,21 @@ function DuotoneFilter( { id, values } ) {
/**
* SVG and stylesheet needed for rendering the duotone filter.
*
* @param {Object} props Duotone props.
* @param {Object} props Duotone props.
* @param {string} props.selector Selector to apply the filter to.
* @param {string} props.id Unique id for this duotone filter.
* @param {Values} props.values R, G, B, and A values to filter with.
* @param {string} props.id Unique id for this duotone filter.
* @param {string[]|"unset"} props.colors Array of RGB color strings ordered from dark to light.
*
* @return {WPElement} Duotone element.
*/
function InlineDuotone( { selector, id, values } ) {
function InlineDuotone( { selector, id, colors } ) {
if ( colors === 'unset' ) {
return <DuotoneUnsetStylesheet selector={ selector } />;
}

return (
<>
<DuotoneFilter id={ id } values={ values } />
<DuotoneFilter id={ id } colors={ colors } />
<DuotoneStylesheet id={ id } selector={ selector } />
</>
);
Expand Down Expand Up @@ -324,9 +336,9 @@ const withDuotoneStyles = createHigherOrderComponent(
props.name,
'color.__experimentalDuotone'
);
const values = props?.attributes?.style?.color?.duotone;
const colors = props?.attributes?.style?.color?.duotone;

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

Expand All @@ -351,7 +363,7 @@ const withDuotoneStyles = createHigherOrderComponent(
<InlineDuotone
selector={ selectorsGroup }
id={ id }
values={ getValuesFromColors( values ) }
colors={ colors }
/>,
element
) }
Expand All @@ -366,7 +378,7 @@ export function PresetDuotoneFilter( { preset } ) {
return (
<DuotoneFilter
id={ `wp-duotone-${ preset.slug }` }
values={ getValuesFromColors( preset.colors ) }
colors={ preset.colors }
/>
);
}
Expand Down
95 changes: 58 additions & 37 deletions packages/components/src/duotone-picker/duotone-picker.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { getDefaultColors, getGradientFromCSSColors } from './utils';

function DuotonePicker( {
clearable = true,
unsetable = true,
colorPalette,
duotonePalette,
disableCustomColors,
Expand All @@ -32,43 +33,60 @@ function DuotonePicker( {
[ colorPalette ]
);

const isUnset = value === 'unset';

const unsetOption = (
<CircularOptionPicker.Option
key="unset"
value="unset"
isSelected={ isUnset }
tooltipText={ __( 'Unset' ) }
className="components-duotone-picker__color-indicator"
onClick={ () => {
onChange( isUnset ? undefined : 'unset' );
} }
/>
);

const options = duotonePalette.map( ( { colors, slug, name } ) => {
const style = {
background: getGradientFromCSSColors( colors, '135deg' ),
color: 'transparent',
};
const tooltipText =
name ??
sprintf(
// translators: %s: duotone code e.g: "dark-grayscale" or "7f7f7f-ffffff".
__( 'Duotone code: %s' ),
slug
);
const label = name
? sprintf(
// translators: %s: The name of the option e.g: "Dark grayscale".
__( 'Duotone: %s' ),
name
)
: tooltipText;
const isSelected = isEqual( colors, value );

return (
<CircularOptionPicker.Option
key={ slug }
value={ colors }
isSelected={ isSelected }
aria-label={ label }
tooltipText={ tooltipText }
style={ style }
onClick={ () => {
onChange( isSelected ? undefined : colors );
} }
/>
);
} );

return (
<CircularOptionPicker
options={ duotonePalette.map( ( { colors, slug, name } ) => {
const style = {
background: getGradientFromCSSColors( colors, '135deg' ),
color: 'transparent',
};
const tooltipText =
name ??
sprintf(
// translators: %s: duotone code e.g: "dark-grayscale" or "7f7f7f-ffffff".
__( 'Duotone code: %s' ),
slug
);
const label = name
? sprintf(
// translators: %s: The name of the option e.g: "Dark grayscale".
__( 'Duotone: %s' ),
name
)
: tooltipText;
const isSelected = isEqual( colors, value );

return (
<CircularOptionPicker.Option
key={ slug }
value={ colors }
isSelected={ isSelected }
aria-label={ label }
tooltipText={ tooltipText }
style={ style }
onClick={ () => {
onChange( isSelected ? undefined : colors );
} }
/>
);
} ) }
options={ unsetable ? [ unsetOption, ...options ] : options }
actions={
!! clearable && (
<CircularOptionPicker.ButtonAction
Expand All @@ -80,13 +98,16 @@ function DuotonePicker( {
}
>
{ ! disableCustomColors && ! disableCustomDuotone && (
<CustomDuotoneBar value={ value } onChange={ onChange } />
<CustomDuotoneBar
value={ isUnset ? undefined : value }
onChange={ onChange }
/>
) }
{ ! disableCustomDuotone && (
<ColorListPicker
labels={ [ __( 'Shadows' ), __( 'Highlights' ) ] }
colors={ colorPalette }
value={ value }
value={ isUnset ? undefined : value }
Copy link
Contributor

Choose a reason for hiding this comment

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

We do this 3 times. Should we make it a variable?

Copy link
Contributor Author

@ajlende ajlende Jun 15, 2022

Choose a reason for hiding this comment

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

It's only used in two places—ColorListPicker and CustomDuotoneBar—and I can't think of a good name for what to call it that would make the code any more readable. Do you have a suggestion?

disableCustomColors={ disableCustomColors }
enableAlpha
onChange={ ( newColors ) => {
Expand Down
19 changes: 19 additions & 0 deletions packages/components/src/duotone-picker/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.components-duotone-picker__color-indicator::before {
background: transparent;
}

.components-duotone-picker__color-indicator > .components-button {
// Show a diagonal line (crossed out) for empty swatches.
background: linear-gradient(-45deg, transparent 48%, $gray-300 48%, $gray-300 52%, transparent 52%);
color: transparent;

&.is-pressed:hover:not(:disabled) {
// Hover state has to be overridden too.
background: linear-gradient(-45deg, transparent 48%, $gray-300 48%, $gray-300 52%, transparent 52%);
color: transparent;
}

&:not([aria-disabled="true"]):active {
color: transparent;
}
}
Loading