: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
// Safari does not always update the duotone filter when the duotone colors
// are changed. This browser check is later used to force a re-render of the block
// element to ensure the duotone filter is updated. The check is included at the
// root of this file as it only needs to be run once per page load.
const isSafari = window?.navigator.userAgent && window.navigator.userAgent.includes('Safari') && !window.navigator.userAgent.includes('Chrome') && !window.navigator.userAgent.includes('Chromium');
function useMultiOriginPresets({
const [enableDefault, userPresets, themePresets, defaultPresets] = use_settings_useSettings(defaultSetting, `${presetSetting}.custom`, `${presetSetting}.theme`, `${presetSetting}.default`);
return (0,external_wp_element_namespaceObject.useMemo)(() => [...(userPresets || duotone_EMPTY_ARRAY), ...(themePresets || duotone_EMPTY_ARRAY), ...(enableDefault && defaultPresets || duotone_EMPTY_ARRAY)], [enableDefault, userPresets, themePresets, defaultPresets]);
function getColorsFromDuotonePreset(duotone, duotonePalette) {
const preset = duotonePalette?.find(({
return duotone === `var:preset|duotone|${slug}`;
return preset ? preset.colors : undefined;
function getDuotonePresetFromColors(colors, duotonePalette) {
if (!colors || !Array.isArray(colors)) {
const preset = duotonePalette?.find(duotonePreset => {
return duotonePreset?.colors?.every((val, index) => val === colors[index]);
return preset ? `var:preset|duotone|${preset.slug}` : undefined;
function DuotonePanelPure({
const duotoneStyle = style?.color?.duotone;
const settings = useBlockSettings(name);
const blockEditingMode = useBlockEditingMode();
const duotonePalette = useMultiOriginPresets({
presetSetting: 'color.duotone',
defaultSetting: 'color.defaultDuotone'
const colorPalette = useMultiOriginPresets({
presetSetting: 'color.palette',
defaultSetting: 'color.defaultPalette'
const [enableCustomColors, enableCustomDuotone] = use_settings_useSettings('color.custom', 'color.customDuotone');
const disableCustomColors = !enableCustomColors;
const disableCustomDuotone = !enableCustomDuotone || colorPalette?.length === 0 && disableCustomColors;
if (duotonePalette?.length === 0 && disableCustomDuotone) {
if (blockEditingMode !== 'default') {
const duotonePresetOrColors = !Array.isArray(duotoneStyle) ? getColorsFromDuotonePreset(duotoneStyle, duotonePalette) : duotoneStyle;
return /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsxs)(external_ReactJSXRuntime_namespaceObject.Fragment, {
children: [/*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)(inspector_controls, {
children: /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)(FiltersPanel, {
duotone: duotonePresetOrColors
onChange: newDuotone => {
}), /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)(block_controls, {
__experimentalShareWithChildBlocks: true,
children: /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)(duotone_control, {
duotonePalette: duotonePalette,
colorPalette: colorPalette,
disableCustomDuotone: disableCustomDuotone,
disableCustomColors: disableCustomColors,
value: duotonePresetOrColors,
onChange: newDuotone => {
const maybePreset = getDuotonePresetFromColors(newDuotone, duotonePalette);
duotone: maybePreset !== null && maybePreset !== void 0 ? maybePreset : newDuotone // use preset or fallback to custom colors.
/* harmony default export */ const duotone = ({
shareWithChildBlocks: true,
useBlockProps: duotone_useBlockProps,
attributeKeys: ['style'],
return (0,external_wp_blocks_namespaceObject.hasBlockSupport)(name, 'filter.duotone');
* Filters registered block settings, extending attributes to include
* the `duotone` attribute.
* @param {Object} settings Original block settings.
* @return {Object} Filtered block settings.
function addDuotoneAttributes(settings) {
// Previous `color.__experimentalDuotone` support flag is migrated via
// block_type_metadata_settings filter in `lib/block-supports/duotone.php`.
if (!(0,external_wp_blocks_namespaceObject.hasBlockSupport)(settings, 'filter.duotone')) {
// Allow blocks to specify their own attribute definition with default
if (!settings.attributes.style) {
Object.assign(settings.attributes, {
function useDuotoneStyles({
selector: duotoneSelector,
const duotonePalette = useMultiOriginPresets({
presetSetting: 'color.duotone',
defaultSetting: 'color.defaultDuotone'
// Possible values for duotone attribute:
// 1. Array of colors - e.g. ['#000000', '#ffffff'].
// 2. Variable for an existing Duotone preset - e.g. 'var:preset|duotone|green-blue' or 'var(--wp--preset--duotone--green-blue)''
// 3. A CSS string - e.g. 'unset' to remove globally applied duotone.
const isCustom = Array.isArray(duotoneAttr);
const duotonePreset = isCustom ? undefined : getColorsFromDuotonePreset(duotoneAttr, duotonePalette);
const isPreset = typeof duotoneAttr === 'string' && duotonePreset;
const isCSS = typeof duotoneAttr === 'string' && !isPreset;
// Match the structure of WP_Duotone_Gutenberg::render_duotone_support() in PHP.
// CSS filter property string (e.g. 'unset').
// Build the CSS selectors to which the filter will be applied.
const selectors = duotoneSelector.split(',');
const selectorsScoped = selectors.map(selectorPart => {
// 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.
// Assuming the selector part is a subclass selector (not a tag name)
// so we can prepend the filter id class. If we want to support elements
// such as `img` or namespaces, we'll need to add a case for that here.
return `.${filterId}${selectorPart.trim()}`;
const selector = selectorsScoped.join(', ');
const isValidFilter = Array.isArray(colors) || colors === 'unset';
useStyleOverride(isValidFilter ? {
css: colors !== 'unset' ? getDuotoneStylesheet(selector, filterId) : getDuotoneUnsetStylesheet(selector),
__unstableType: 'presets'
useStyleOverride(isValidFilter ? {
assets: colors !== 'unset' ? getDuotoneFilter(filterId, colors) : '',
const blockElement = useBlockElement(clientId);
(0,external_wp_element_namespaceObject.useEffect)(() => {
// Safari does not always update the duotone filter when the duotone
// colors are changed. When using Safari, force the block element to be
// repainted by the browser to ensure any changes are reflected
// visually. This logic matches that used on the site frontend in
// `block-supports/duotone.php`.
if (blockElement && isSafari) {
const display = blockElement.style.display;
// Switch to `inline-block` to force a repaint. In the editor,
// `inline-block` is used instead of `none` to ensure that scroll
// position is not affected, as `none` results in the editor
// scrolling to the top of the block.
blockElement.style.display = 'inline-block';
// Simply accessing el.offsetHeight flushes layout and style changes
// in WebKit without having to wait for setTimeout.
// eslint-disable-next-line no-unused-expressions
blockElement.offsetHeight;
blockElement.style.display = display;
// `colors` must be a dependency so this effect runs when the colors
}, [isValidFilter, blockElement, colors]);
function duotone_useBlockProps({
const id = (0,external_wp_compose_namespaceObject.useInstanceId)(duotone_useBlockProps);
const selector = (0,external_wp_element_namespaceObject.useMemo)(() => {
const blockType = (0,external_wp_blocks_namespaceObject.getBlockType)(name);
// Backwards compatibility for `supports.color.__experimentalDuotone`
// is provided via the `block_type_metadata_settings` filter. If
// `supports.filter.duotone` has not been set and the
// experimental property has been, the experimental property
// value is copied into `supports.filter.duotone`.
const duotoneSupport = (0,external_wp_blocks_namespaceObject.getBlockSupport)(blockType, 'filter.duotone', false);
// If the experimental duotone support was set, that value is
// to be treated as a selector and requires scoping.
const experimentalDuotone = (0,external_wp_blocks_namespaceObject.getBlockSupport)(blockType, 'color.__experimentalDuotone', false);
if (experimentalDuotone) {
const rootSelector = getBlockCSSSelector(blockType);
return typeof experimentalDuotone === 'string' ? scopeSelector(rootSelector, experimentalDuotone) : rootSelector;
// Regular filter.duotone support uses filter.duotone selectors with fallbacks.
return getBlockCSSSelector(blockType, 'filter.duotone', {
const attribute = style?.color?.duotone;
const filterClass = `wp-duotone-${id}`;
const shouldRender = selector && attribute;
className: shouldRender ? filterClass : ''
(0,external_wp_hooks_namespaceObject.addFilter)('blocks.registerBlockType', 'core/editor/duotone/add-attributes', addDuotoneAttributes);
;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/use-block-display-information/index.js
/** @typedef {import('@wordpress/blocks').WPIcon} WPIcon */
* Contains basic block's information for display reasons.
* @typedef {Object} WPBlockDisplayInformation
* @property {boolean} isSynced True if is a reusable block or template part
* @property {string} title Human-readable block type label.
* @property {WPIcon} icon Block type icon.
* @property {string} description A detailed block type description.
* @property {string} anchor HTML anchor.
* @property {name} name A custom, human readable name for the block.
* Get the display label for a block's position type.
* @param {Object} attributes Block attributes.
* @return {string} The position type label.
function getPositionTypeLabel(attributes) {
const positionType = attributes?.style?.position?.type;
if (positionType === 'sticky') {
return (0,external_wp_i18n_namespaceObject.__)('Sticky');
if (positionType === 'fixed') {
return (0,external_wp_i18n_namespaceObject.__)('Fixed');
* Hook used to try to find a matching block variation and return
* the appropriate information for display reasons. In order to
* to try to find a match we need to things:
* 1. Block's client id to extract it's current attributes.
* 2. A block variation should have set `isActive` prop to a proper function.
* If for any reason a block variation match cannot be found,
* the returned information come from the Block Type.
* If no blockType is found with the provided clientId, returns null.
* @param {string} clientId Block's client id.
* @return {?WPBlockDisplayInformation} Block's display information, or `null` when the block or its type not found.
function useBlockDisplayInformation(clientId) {
return (0,external_wp_data_namespaceObject.useSelect)(select => {
} = select(external_wp_blocks_namespaceObject.store);
const blockName = getBlockName(clientId);
const blockType = getBlockType(blockName);
const attributes = getBlockAttributes(clientId);
const match = getActiveBlockVariation(blockName, attributes);
const isSynced = (0,external_wp_blocks_namespaceObject.isReusableBlock)(blockType) || (0,external_wp_blocks_namespaceObject.isTemplatePart)(blockType);
const syncedTitle = isSynced ? (0,external_wp_blocks_namespaceObject.__experimentalGetBlockLabel)(blockType, attributes) : undefined;
const title = syncedTitle || blockType.title;
const positionLabel = getPositionTypeLabel(attributes);
description: blockType.description,
anchor: attributes?.anchor,
positionType: attributes?.style?.position?.type,
name: attributes?.metadata?.name
title: match.title || blockType.title,
icon: match.icon || blockType.icon,
description: match.description || blockType.description,
anchor: attributes?.anchor,
positionType: attributes?.style?.position?.type,
name: attributes?.metadata?.name
;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/hooks/position.js
} = unlock(external_wp_components_namespaceObject.privateApis);
const POSITION_SUPPORT_KEY = 'position';
const OPTION_CLASSNAME = 'block-editor-hooks__position-selection__select-control__option';
name: (0,external_wp_i18n_namespaceObject.__)('Default'),
className: OPTION_CLASSNAME
name: (0,external_wp_i18n_namespaceObject._x)('Sticky', 'Name for the value of the CSS position property'),
className: OPTION_CLASSNAME,
__experimentalHint: (0,external_wp_i18n_namespaceObject.__)('The block will stick to the top of the window instead of scrolling.')
name: (0,external_wp_i18n_namespaceObject._x)('Fixed', 'Name for the value of the CSS position property'),
className: OPTION_CLASSNAME,
__experimentalHint: (0,external_wp_i18n_namespaceObject.__)('The block will not move when the page is scrolled.')
const POSITION_SIDES = ['top', 'right', 'bottom', 'left'];
const VALID_POSITION_TYPES = ['sticky', 'fixed'];
* Get calculated position CSS.
* @param {Object} props Component props.
* @param {string} props.selector Selector to use.
* @param {Object} props.style Style object.
* @return {string} The generated CSS rules.
function getPositionCSS({
} = style?.position || {};
if (!VALID_POSITION_TYPES.includes(positionType)) {
output += `${selector} {`;
output += `position: ${positionType};`;
POSITION_SIDES.forEach(side => {
if (style?.position?.[side] !== undefined) {
output += `${side}: ${style.position[side]};`;
if (positionType === 'sticky' || positionType === 'fixed') {
// TODO: Replace hard-coded z-index value with a z-index preset approach in theme.json.
* Determines if there is sticky position support.
* @param {string|Object} blockType Block name or Block Type object.
* @return {boolean} Whether there is support.
function hasStickyPositionSupport(blockType) {
const support = (0,external_wp_blocks_namespaceObject.getBlockSupport)(blockType, POSITION_SUPPORT_KEY);
return !!(true === support || support?.sticky);
* Determines if there is fixed position support.
* @param {string|Object} blockType Block name or Block Type object.
* @return {boolean} Whether there is support.
function hasFixedPositionSupport(blockType) {
const support = (0,external_wp_blocks_namespaceObject.getBlockSupport)(blockType, POSITION_SUPPORT_KEY);
return !!(true === support || support?.fixed);
* Determines if there is position support.