Skip to content

Commit

Permalink
Fix duotone rendering in site editor (#37727)
Browse files Browse the repository at this point in the history
Co-authored-by: Alex Lende <ajlende@gmail.com>
Co-authored-by: Ben Dwyer <ben@scruffian.com>
Co-authored-by: André <583546+oandregal@users.noreply.github.com>
  • Loading branch information
4 people authored and adamziel committed Apr 25, 2022
1 parent 0f8b6dd commit fbe64ec
Show file tree
Hide file tree
Showing 8 changed files with 201 additions and 76 deletions.
160 changes: 98 additions & 62 deletions packages/block-editor/src/hooks/duotone.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,82 +60,109 @@ export function getValuesFromColors( colors = [] ) {
*/

/**
* SVG and stylesheet needed for rendering the duotone filter.
* Stylesheet 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.
* @param {Values} props.values R, G, B, and A values to filter with.
*
* @return {WPElement} Duotone element.
*/
function DuotoneFilter( { selector, id, values } ) {
const stylesheet = `
function DuotoneStylesheet( { selector, id } ) {
const css = `
${ selector } {
filter: url( #${ id } );
}
`;
return <style>{ css }</style>;
}

/**
* SVG for rendering 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.
*
* @return {WPElement} Duotone element.
*/
function DuotoneFilter( { id, values } ) {
return (
<>
<SVG
xmlnsXlink="http://www.w3.org/1999/xlink"
viewBox="0 0 0 0"
width="0"
height="0"
focusable="false"
role="none"
style={ {
visibility: 'hidden',
position: 'absolute',
left: '-9999px',
overflow: 'hidden',
} }
>
<defs>
<filter id={ id }>
<feColorMatrix
// Use sRGB instead of linearRGB so transparency looks correct.
colorInterpolationFilters="sRGB"
type="matrix"
// Use perceptual brightness to convert to grayscale.
values="
.299 .587 .114 0 0
.299 .587 .114 0 0
.299 .587 .114 0 0
.299 .587 .114 0 0
"
<SVG
xmlnsXlink="http://www.w3.org/1999/xlink"
viewBox="0 0 0 0"
width="0"
height="0"
focusable="false"
role="none"
style={ {
visibility: 'hidden',
position: 'absolute',
left: '-9999px',
overflow: 'hidden',
} }
>
<defs>
<filter id={ id }>
<feColorMatrix
// Use sRGB instead of linearRGB so transparency looks correct.
colorInterpolationFilters="sRGB"
type="matrix"
// Use perceptual brightness to convert to grayscale.
values="
.299 .587 .114 0 0
.299 .587 .114 0 0
.299 .587 .114 0 0
.299 .587 .114 0 0
"
/>
<feComponentTransfer
// Use sRGB instead of linearRGB to be consistent with how CSS gradients work.
colorInterpolationFilters="sRGB"
>
<feFuncR
type="table"
tableValues={ values.r.join( ' ' ) }
/>
<feFuncG
type="table"
tableValues={ values.g.join( ' ' ) }
/>
<feFuncB
type="table"
tableValues={ values.b.join( ' ' ) }
/>
<feComponentTransfer
// Use sRGB instead of linearRGB to be consistent with how CSS gradients work.
colorInterpolationFilters="sRGB"
>
<feFuncR
type="table"
tableValues={ values.r.join( ' ' ) }
/>
<feFuncG
type="table"
tableValues={ values.g.join( ' ' ) }
/>
<feFuncB
type="table"
tableValues={ values.b.join( ' ' ) }
/>
<feFuncA
type="table"
tableValues={ values.a.join( ' ' ) }
/>
</feComponentTransfer>
<feComposite
// Re-mask the image with the original transparency since the feColorMatrix above loses that information.
in2="SourceGraphic"
operator="in"
<feFuncA
type="table"
tableValues={ values.a.join( ' ' ) }
/>
</filter>
</defs>
</SVG>
<style dangerouslySetInnerHTML={ { __html: stylesheet } } />
</feComponentTransfer>
<feComposite
// Re-mask the image with the original transparency since the feColorMatrix above loses that information.
in2="SourceGraphic"
operator="in"
/>
</filter>
</defs>
</SVG>
);
}

/**
* 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.
* @param {Values} props.values R, G, B, and A values to filter with.
*
* @return {WPElement} Duotone element.
*/
function InlineDuotone( { selector, id, values } ) {
return (
<>
<DuotoneFilter id={ id } values={ values } />
<DuotoneStylesheet id={ id } selector={ selector } />
</>
);
}
Expand Down Expand Up @@ -321,7 +348,7 @@ const withDuotoneStyles = createHigherOrderComponent(
<>
{ element &&
createPortal(
<DuotoneFilter
<InlineDuotone
selector={ selectorsGroup }
id={ id }
values={ getValuesFromColors( values ) }
Expand All @@ -335,6 +362,15 @@ const withDuotoneStyles = createHigherOrderComponent(
'withDuotoneStyles'
);

export function PresetDuotoneFilter( { preset } ) {
return (
<DuotoneFilter
id={ `wp-duotone-${ preset.slug }` }
values={ getValuesFromColors( preset.colors ) }
/>
);
}

addFilter(
'blocks.registerBlockType',
'core/editor/duotone/add-attributes',
Expand Down
1 change: 1 addition & 0 deletions packages/block-editor/src/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ export { getBorderClassesAndStyles, useBorderProps } from './use-border-props';
export { getColorClassesAndStyles, useColorProps } from './use-color-props';
export { getSpacingClassesAndStyles } from './use-spacing-props';
export { useCachedTruthy } from './use-cached-truthy';
export { PresetDuotoneFilter } from './duotone';
1 change: 1 addition & 0 deletions packages/block-editor/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/
import './hooks';
export {
PresetDuotoneFilter as __unstablePresetDuotoneFilter,
getBorderClassesAndStyles as __experimentalGetBorderClassesAndStyles,
useBorderProps as __experimentalUseBorderProps,
getColorClassesAndStyles as __experimentalGetColorClassesAndStyles,
Expand Down
4 changes: 4 additions & 0 deletions packages/blocks/src/api/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ export const __EXPERIMENTAL_STYLE_PROPERTY = {
support: [ 'color', 'text' ],
requiresOptOut: true,
},
filter: {
value: [ 'filter', 'duotone' ],
support: [ 'color', '__experimentalDuotone' ],
},
linkColor: {
value: [ 'elements', 'link', 'color', 'text' ],
support: [ 'color', 'link' ],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const HANDLE_STYLES_OVERRIDE = {
left: undefined,
};

function ResizableEditor( { enableResizing, settings, ...props } ) {
function ResizableEditor( { enableResizing, settings, children, ...props } ) {
const deviceType = useSelect(
( select ) =>
select( editSiteStore ).__experimentalGetPreviewDeviceType(),
Expand Down Expand Up @@ -182,7 +182,11 @@ function ResizableEditor( { enableResizing, settings, ...props } ) {
name="editor-canvas"
className="edit-site-visual-editor__editor-canvas"
{ ...props }
/>
>
{ /* Filters need to be rendered before children to avoid Safari rendering issues. */ }
{ settings.svgFilters }
{ children }
</Iframe>
</ResizableBox>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { store as editSiteStore } from '../../store';
import { useGlobalStylesOutput } from '../global-styles/use-global-styles-output';

function useGlobalStylesRenderer() {
const [ styles, settings ] = useGlobalStylesOutput();
const [ styles, settings, svgFilters ] = useGlobalStylesOutput();
const { getSettings } = useSelect( editSiteStore );
const { updateSettings } = useDispatch( editSiteStore );

Expand All @@ -37,6 +37,7 @@ function useGlobalStylesRenderer() {
updateSettings( {
...currentStoreSettings,
styles: [ ...nonGlobalStyles, ...styles ],
svgFilters,
__experimentalFeatures: settings,
} );
}, [ styles, settings ] );
Expand Down
Loading

0 comments on commit fbe64ec

Please sign in to comment.