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

Duotone: add block controls on the inspector #49838

Merged
merged 10 commits into from
Apr 20, 2023
114 changes: 99 additions & 15 deletions packages/block-editor/src/components/global-styles/filters-panel.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
/**
* External dependencies
*/
import classnames from 'classnames';

/**
* WordPress dependencies
*/
import {
__experimentalToolsPanel as ToolsPanel,
__experimentalToolsPanelItem as ToolsPanelItem,
__experimentalItemGroup as ItemGroup,
__experimentalHStack as HStack,
__experimentalZStack as ZStack,
__experimentalVStack as VStack,
__experimentalDropdownContentWrapper as DropdownContentWrapper,
Button,
ColorIndicator,
DuotonePicker,
DuotoneSwatch,
Dropdown,
Flex,
FlexItem,
} from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { useCallback, useMemo } from '@wordpress/element';
Expand Down Expand Up @@ -75,6 +90,37 @@ const DEFAULT_CONTROLS = {
duotone: true,
};

const popoverProps = {
placement: 'left-start',
offset: 36,
shift: true,
className: 'block-editor-duotone-control__popover',
Copy link
Contributor

Choose a reason for hiding this comment

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

It's not great when we use "classnames" as reusable components. In the ideal scenario, the styles should be defined in the same place where the classname is used.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes! this I think can be refactored when we work on using the filter panel for the toolbar controls, I will make a note to do just that

headerTitle: __( 'Duotone' ),
variant: 'toolbar',
};

const LabeledColorIndicators = ( { indicators, label } ) => (
MaggieCabrera marked this conversation as resolved.
Show resolved Hide resolved
<HStack justify="flex-start">
<ZStack isLayered={ false } offset={ -8 }>
{ indicators.map( ( indicator, index ) => (
<Flex key={ index } expanded={ false }>
{ indicator === 'unset' || ! indicator ? (
<ColorIndicator className="block-editor-duotone-control__unset-indicator" />
Copy link
Contributor

Choose a reason for hiding this comment

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

I saw this check and this class in a couple places, It seems to me that the DuotonePicker already understands the unset value, so it's not far fetched to say that the DuotoneSwatch should also understand the unset value and do the right thing internally instead of duplicating the styles and the classname every time.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will tackle this one when refactoring the DuotoneControl component too, good catch

) : (
<DuotoneSwatch values={ indicator } />
) }
</Flex>
) ) }
</ZStack>
<FlexItem
className="block-editor-panel-color-gradient-settings__color-name"
MaggieCabrera marked this conversation as resolved.
Show resolved Hide resolved
title={ label }
>
{ label }
</FlexItem>
</HStack>
);

export default function FiltersPanel( {
as: Wrapper = FiltersToolsPanel,
value,
Expand Down Expand Up @@ -110,6 +156,11 @@ export default function FiltersPanel( {
const hasDuotone = () => !! value?.filter?.duotone;
const resetDuotone = () => setDuotone( undefined );

const disableCustomColors = ! settings?.color?.custom;
const disableCustomDuotone =
! settings?.color?.customDuotone ||
( colorPalette?.length === 0 && disableCustomColors );

const resetAllFilter = useCallback( ( previousValue ) => {
return {
...previousValue,
Expand All @@ -135,21 +186,54 @@ export default function FiltersPanel( {
isShownByDefault={ defaultControls.duotone }
panelId={ panelId }
>
<VStack>
<p>
{ __(
'Create a two-tone color effect without losing your original image.'
) }
</p>
<DuotonePicker
colorPalette={ colorPalette }
duotonePalette={ duotonePalette }
disableCustomColors={ true }
disableCustomDuotone={ true }
value={ duotone }
onChange={ setDuotone }
/>
</VStack>
<Dropdown
popoverProps={ popoverProps }
className="block-editor-tools-panel-color-gradient-settings__dropdown"
MaggieCabrera marked this conversation as resolved.
Show resolved Hide resolved
renderToggle={ ( { onToggle, isOpen } ) => {
const toggleProps = {
onClick: onToggle,
className: classnames(
'block-editor-panel-color-gradient-settings__dropdown',
{ 'is-open': isOpen }
),
'aria-expanded': isOpen,
};

return (
<ItemGroup isBordered isSeparated>
<Button { ...toggleProps }>
<LabeledColorIndicators
indicators={ [ duotone ] }
label={ __( 'Duotone' ) }
/>
</Button>
</ItemGroup>
);
} }
renderContent={ () => (
<DropdownContentWrapper paddingSize="medium">
<VStack>
<p>
{ __(
'Create a two-tone color effect without losing your original image.'
) }
</p>
<DuotonePicker
colorPalette={ colorPalette }
duotonePalette={ duotonePalette }
disableCustomColors={
disableCustomColors
}
disableCustomDuotone={
disableCustomDuotone
}
value={ duotone }
onChange={ setDuotone }
/>
</VStack>
</DropdownContentWrapper>
) }
/>
</ToolsPanelItem>
) }
</Wrapper>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const StylesTab = ( { blockName, clientId, hasBlockStyles } ) => {
label={ __( 'Color' ) }
className="color-block-support-panel__inner-wrapper"
/>
<InspectorControls.Slot group="filter" />
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jasmussen is this a proper order for the filters? I put it after color because it's what made sense to me

<InspectorControls.Slot
group="typography"
label={ __( 'Typography' ) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const InspectorControlsDefault = createSlotFill( 'InspectorControls' );
const InspectorControlsAdvanced = createSlotFill( 'InspectorAdvancedControls' );
const InspectorControlsBorder = createSlotFill( 'InspectorControlsBorder' );
const InspectorControlsColor = createSlotFill( 'InspectorControlsColor' );
const InspectorControlsFilter = createSlotFill( 'InspectorControlsFilter' );
const InspectorControlsDimensions = createSlotFill(
'InspectorControlsDimensions'
);
Expand All @@ -22,6 +23,7 @@ const groups = {
advanced: InspectorControlsAdvanced,
border: InspectorControlsBorder,
color: InspectorControlsColor,
filter: InspectorControlsFilter,
dimensions: InspectorControlsDimensions,
list: InspectorControlsListView,
settings: InspectorControlsDefault, // Alias for default.
Expand Down
71 changes: 46 additions & 25 deletions packages/block-editor/src/hooks/duotone.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { useSelect } from '@wordpress/data';
*/
import {
BlockControls,
InspectorControls,
__experimentalDuotoneControl as DuotoneControl,
useSetting,
} from '../components';
Expand All @@ -34,7 +35,9 @@ import {
} from '../components/duotone';
import { getBlockCSSSelector } from '../components/global-styles/get-block-css-selector';
import { scopeSelector } from '../components/global-styles/utils';
import { useBlockSettings } from './utils';
import { store as blockEditorStore } from '../store';
import { default as StylesFiltersPanel } from '../components/global-styles/filters-panel';

const EMPTY_ARRAY = [];

Expand Down Expand Up @@ -106,9 +109,10 @@ export function getDuotonePresetFromColors( colors, duotonePalette ) {
return preset ? `var:preset|duotone|${ preset.slug }` : undefined;
}

function DuotonePanel( { attributes, setAttributes } ) {
function DuotonePanel( { attributes, setAttributes, name } ) {
const style = attributes?.style;
const duotoneStyle = style?.color?.duotone;
const settings = useBlockSettings( name );

const duotonePalette = useMultiOriginPresets( {
presetSetting: 'color.duotone',
Expand All @@ -132,30 +136,47 @@ function DuotonePanel( { attributes, setAttributes } ) {
: duotoneStyle;

return (
<BlockControls group="block" __experimentalShareWithChildBlocks>
<DuotoneControl
duotonePalette={ duotonePalette }
colorPalette={ colorPalette }
disableCustomDuotone={ disableCustomDuotone }
disableCustomColors={ disableCustomColors }
value={ duotonePresetOrColors }
onChange={ ( newDuotone ) => {
const maybePreset = getDuotonePresetFromColors(
newDuotone,
duotonePalette
);

const newStyle = {
...style,
color: {
...style?.color,
duotone: maybePreset ?? newDuotone, // use preset or fallback to custom colors.
},
};
setAttributes( { style: newStyle } );
} }
/>
</BlockControls>
<>
<InspectorControls group="filter">
<StylesFiltersPanel
value={ { filter: { duotone: duotonePresetOrColors } } }
onChange={ ( newDuotone ) => {
const newStyle = {
color: {
...newDuotone?.filter,
},
};
setAttributes( { style: newStyle } );
} }
settings={ settings }
/>
</InspectorControls>
<BlockControls group="block" __experimentalShareWithChildBlocks>
<DuotoneControl
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@youknowriad we tried to use the StylesFiltersPanel here too but it doesn't work outside of the inspector context, so we decided to leave it as it is right now.

Copy link
Contributor

Choose a reason for hiding this comment

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

yes, this is a new use case (block toolbar). Maybe there's a way to make these controllers agnostic here too by providing a specific Wrapper prop, did we try that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes, we did that, and the ToolsPanelItem would not render, isShown is false in that context

Copy link
Contributor Author

Choose a reason for hiding this comment

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

/cc @ajlende

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess it's because of a missing ToolsPanel container within the "wrapper", that component seems mandatory. We can probably include it in the block toolbar too though

duotonePalette={ duotonePalette }
colorPalette={ colorPalette }
disableCustomDuotone={ disableCustomDuotone }
disableCustomColors={ disableCustomColors }
value={ duotonePresetOrColors }
onChange={ ( newDuotone ) => {
const maybePreset = getDuotonePresetFromColors(
newDuotone,
duotonePalette
);

const newStyle = {
...style,
color: {
...style?.color,
duotone: maybePreset ?? newDuotone, // use preset or fallback to custom colors.
},
};
setAttributes( { style: newStyle } );
} }
settings={ settings }
/>
</BlockControls>
</>
);
}

Expand Down
17 changes: 17 additions & 0 deletions packages/block-editor/src/hooks/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,14 @@ export function useBlockSettings( name, parentLayout ) {
const borderWidth = useSetting( 'border.width' );
const customColorsEnabled = useSetting( 'color.custom' );
const customColors = useSetting( 'color.palette.custom' );
const customDuotone = useSetting( 'color.customDuotone' );
const themeColors = useSetting( 'color.palette.theme' );
const defaultColors = useSetting( 'color.palette.default' );
const defaultPalette = useSetting( 'color.defaultPalette' );
const defaultDuotone = useSetting( 'color.defaultDuotone' );
const userDuotonePalette = useSetting( 'color.duotone.custom' );
const themeDuotonePalette = useSetting( 'color.duotone.theme' );
const defaultDuotonePalette = useSetting( 'color.duotone.default' );
const userGradientPalette = useSetting( 'color.gradients.custom' );
const themeGradientPalette = useSetting( 'color.gradients.theme' );
const defaultGradientPalette = useSetting( 'color.gradients.default' );
Expand All @@ -175,10 +180,17 @@ export function useBlockSettings( name, parentLayout ) {
theme: themeGradientPalette,
default: defaultGradientPalette,
},
duotone: {
custom: userDuotonePalette,
theme: themeDuotonePalette,
default: defaultDuotonePalette,
},
defaultGradients,
defaultPalette,
defaultDuotone,
custom: customColorsEnabled,
customGradient: areCustomGradientsEnabled,
customDuotone,
background: isBackgroundEnabled,
link: isLinkEnabled,
text: isTextEnabled,
Expand Down Expand Up @@ -245,9 +257,14 @@ export function useBlockSettings( name, parentLayout ) {
borderWidth,
customColorsEnabled,
customColors,
customDuotone,
themeColors,
defaultColors,
defaultPalette,
defaultDuotone,
userDuotonePalette,
themeDuotonePalette,
defaultDuotonePalette,
userGradientPalette,
themeGradientPalette,
defaultGradientPalette,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@ export default function FiltersPanel( { name } ) {
inheritedValue={ inheritedStyle }
value={ style }
onChange={ setStyle }
settings={ settings }
settings={ {
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if we should memoize this object (depends on how it's used internally)

...settings,
color: {
...settings.color,
customDuotone: false, //TO FIX: Custom duotone only works on the block level right now
},
} }
/>
);
}