Skip to content

Commit

Permalink
Rendering duotone presets in pattern preview (WordPress#41249)
Browse files Browse the repository at this point in the history
  • Loading branch information
matiasbenedetto authored Jun 22, 2022
1 parent 4ccf192 commit 4315382
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 143 deletions.
18 changes: 16 additions & 2 deletions packages/block-editor/src/components/block-preview/auto.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { useMemo } from '@wordpress/element';
import BlockList from '../block-list';
import Iframe from '../iframe';
import EditorStyles from '../editor-styles';
import { __unstablePresetDuotoneFilter as PresetDuotoneFilter } from '../../components/duotone';
import { store } from '../../store';

// This is used to avoid rendering the block list if the sizes change.
Expand All @@ -28,11 +29,12 @@ function AutoBlockPreview( {
useResizeObserver();
const [ contentResizeListener, { height: contentHeight } ] =
useResizeObserver();
const { styles, assets } = useSelect( ( select ) => {
const { styles, assets, duotone } = useSelect( ( select ) => {
const settings = select( store ).getSettings();
return {
styles: settings.styles,
assets: settings.__unstableResolvedAssets,
duotone: settings.__experimentalFeatures?.color?.duotone,
};
}, [] );

Expand All @@ -51,11 +53,14 @@ function AutoBlockPreview( {
return styles;
}, [ styles ] );

const svgFilters = useMemo( () => {
return [ ...( duotone?.default ?? [] ), ...( duotone?.theme ?? [] ) ];
}, [ duotone ] );

// Initialize on render instead of module top level, to avoid circular dependency issues.
MemoizedBlockList = MemoizedBlockList || pure( BlockList );

const scale = containerWidth / viewportWidth;

return (
<div className="block-editor-block-preview__container">
{ containerResizeListener }
Expand Down Expand Up @@ -106,6 +111,15 @@ function AutoBlockPreview( {
} }
>
{ contentResizeListener }
{
/* Filters need to be rendered before children to avoid Safari rendering issues. */
svgFilters.map( ( preset ) => (
<PresetDuotoneFilter
preset={ preset }
key={ preset.slug }
/>
) )
}
<MemoizedBlockList renderAppender={ false } />
</Iframe>
</Disabled>
Expand Down
133 changes: 133 additions & 0 deletions packages/block-editor/src/components/duotone/components.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/**
* WordPress dependencies
*/
import { SVG } from '@wordpress/components';

/**
* Internal dependencies
*/
import { __unstableGetValuesFromColors as getValuesFromColors } from './index';

/**
* 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.
*
* @return {WPElement} Duotone element.
*/
export function DuotoneStylesheet( { selector, id } ) {
const css = `
${ selector } {
filter: url( #${ id } );
}
`;
return <style>{ css }</style>;
}

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

/**
* The SVG part of the duotone filter.
*
* @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 SVG.
*/
export function DuotoneFilter( { id, colors } ) {
const values = getValuesFromColors( colors );
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
"
/>
<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"
/>
</filter>
</defs>
</SVG>
);
}

/**
* SVG from a duotone preset
*
* @param {Object} props Duotone props.
* @param {Object} props.preset Duotone preset settings.
*
* @return {WPElement} Duotone element.
*/
export function PresetDuotoneFilter( { preset } ) {
return (
<DuotoneFilter
id={ `wp-duotone-${ preset.slug }` }
colors={ preset.colors }
/>
);
}
7 changes: 7 additions & 0 deletions packages/block-editor/src/components/duotone/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export { getValuesFromColors as __unstableGetValuesFromColors } from './utils';
export {
DuotoneFilter as __unstableDuotoneFilter,
PresetDuotoneFilter as __unstablePresetDuotoneFilter,
DuotoneStylesheet as __unstableDuotoneStylesheet,
DuotoneUnsetStylesheet as __unstableDuotoneUnsetStylesheet,
} from './components';
25 changes: 25 additions & 0 deletions packages/block-editor/src/components/duotone/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* External dependencies
*/
import { colord } from 'colord';

/**
* Convert a list of colors to an object of R, G, and B values.
*
* @param {string[]} colors Array of RBG color strings.
*
* @return {Object} R, G, and B values.
*/
export function getValuesFromColors( colors = [] ) {
const values = { r: [], g: [], b: [], a: [] };

colors.forEach( ( color ) => {
const rgbColor = colord( color ).toRgb();
values.r.push( rgbColor.r / 255 );
values.g.push( rgbColor.g / 255 );
values.b.push( rgbColor.b / 255 );
values.a.push( rgbColor.a );
} );

return values;
}
1 change: 1 addition & 0 deletions packages/block-editor/src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
export * from './colors';
export * from './gradients';
export * from './font-sizes';
export * from './duotone';
export { AlignmentControl, AlignmentToolbar } from './alignment-control';
export { default as Autocomplete } from './autocomplete';
export {
Expand Down
Loading

0 comments on commit 4315382

Please sign in to comment.