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
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