Skip to content

Commit

Permalink
Add effects/box shadow tools to block inspector (#57654)
Browse files Browse the repository at this point in the history
* wip

* wip

* Add effects panel to block inspector

@todo: for some reason, the Effects Panel is rendering, but the Shadow
controls are not.

* add shadow classes for block settings in the editor

* add shadow attribute to template from the UI

* fix typo

* define support key in the supports hook

* remove internal dependencies block

* remove effects panel from multi-selection inspector

* read/write shadow from style attribute

* make sure path and value computed correctly

* fix shadow being applied to block stead of inner element for button

* revert generation of class names for shadows

* revert support for shadow atribute.

* don't use useMemo

* skip serialization for shadow when the support is enabled

* add entry to components changelog

---------

Co-authored-by: madhusudhand <madhusudhan.dollu@gmail.com>
  • Loading branch information
vcanales and madhusudhand committed Jan 23, 2024
1 parent 614e035 commit 43ac2af
Show file tree
Hide file tree
Showing 20 changed files with 194 additions and 6 deletions.
5 changes: 2 additions & 3 deletions lib/block-supports/shadow.php
Expand Up @@ -53,9 +53,8 @@ function gutenberg_apply_shadow_support( $block_type, $block_attributes ) {

$shadow_block_styles = array();

$preset_shadow = array_key_exists( 'shadow', $block_attributes ) ? "var:preset|shadow|{$block_attributes['shadow']}" : null;
$custom_shadow = isset( $block_attributes['style']['shadow'] ) ? $block_attributes['style']['shadow'] : null;
$shadow_block_styles['shadow'] = $preset_shadow ? $preset_shadow : $custom_shadow;
$custom_shadow = $block_attributes['style']['shadow'] ?? null;
$shadow_block_styles['shadow'] = $custom_shadow;

$attributes = array();
$styles = gutenberg_style_engine_get_styles( $shadow_block_styles );
Expand Down
4 changes: 4 additions & 0 deletions packages/block-editor/src/components/block-inspector/index.js
Expand Up @@ -307,6 +307,10 @@ const BlockInspectorSingleBlock = ( { clientId, blockName } ) => {
label={ __( 'Background' ) }
/>
<PositionControls />
<InspectorControls.Slot
group="effects"
label={ __( 'Effects' ) }
/>
<div>
<AdvancedControls />
</div>
Expand Down
Expand Up @@ -26,6 +26,7 @@ import { shadow as shadowIcon, Icon, check } from '@wordpress/icons';
/**
* Internal dependencies
*/
import { mergeOrigins } from '../use-settings';
import { getValueFromVariable, TOOLSPANEL_DROPDOWNMENU_PROPS } from './utils';
import { setImmutably } from '../../utils/object';

Expand Down Expand Up @@ -81,8 +82,22 @@ export default function EffectsPanel( {
// Shadow
const hasShadowEnabled = useHasShadowControl( settings );
const shadow = decodeValue( inheritedValue?.shadow );
const shadowPresets = settings?.shadow?.presets;
const mergedShadowPresets = shadowPresets
? mergeOrigins( shadowPresets )
: [];
const setShadow = ( newValue ) => {
onChange( setImmutably( value, [ 'shadow' ], newValue ) );
const slug = mergedShadowPresets?.find(
( { shadow: shadowName } ) => shadowName === newValue
)?.slug;

onChange(
setImmutably(
value,
[ 'shadow' ],
slug ? `var:preset|shadow|${ slug }` : newValue || undefined
)
);
};
const hasShadow = () => !! value?.shadow;
const resetShadow = () => setShadow( undefined );
Expand Down
Expand Up @@ -46,6 +46,7 @@ const StylesTab = ( { blockName, clientId, hasBlockStyles } ) => {
label={ __( 'Dimensions' ) }
/>
<InspectorControls.Slot group="border" label={ __( 'Border' ) } />
<InspectorControls.Slot group="effects" label={ __( 'Effects' ) } />
<InspectorControls.Slot group="styles" />
</>
);
Expand Down
Expand Up @@ -40,6 +40,7 @@ export default function useInspectorControlsTabs( blockName ) {
position: positionGroup,
styles: stylesGroup,
typography: typographyGroup,
effects: effectsGroup,
} = InspectorControlsGroups;

// List View Tab: If there are any fills for the list group add that tab.
Expand All @@ -55,6 +56,7 @@ export default function useInspectorControlsTabs( blockName ) {
...( useSlotFills( dimensionsGroup.Slot.__unstableName ) || [] ),
...( useSlotFills( stylesGroup.Slot.__unstableName ) || [] ),
...( useSlotFills( typographyGroup.Slot.__unstableName ) || [] ),
...( useSlotFills( effectsGroup.Slot.__unstableName ) || [] ),
];
const hasStyleFills = styleFills.length;

Expand Down
Expand Up @@ -20,6 +20,7 @@ const InspectorControlsTypography = createSlotFill(
);
const InspectorControlsListView = createSlotFill( 'InspectorControlsListView' );
const InspectorControlsStyles = createSlotFill( 'InspectorControlsStyles' );
const InspectorControlsEffects = createSlotFill( 'InspectorControlsEffects' );

const groups = {
default: InspectorControlsDefault,
Expand All @@ -28,6 +29,7 @@ const groups = {
border: InspectorControlsBorder,
color: InspectorControlsColor,
dimensions: InspectorControlsDimensions,
effects: InspectorControlsEffects,
filter: InspectorControlsFilter,
list: InspectorControlsListView,
position: InspectorControlsPosition,
Expand Down
59 changes: 59 additions & 0 deletions packages/block-editor/src/hooks/effects.js
@@ -0,0 +1,59 @@
/**
* WordPress dependencies
*/
import { hasBlockSupport } from '@wordpress/blocks';
import { useSelect } from '@wordpress/data';
/**
* Internal dependencies
*/
import StylesEffectsPanel, {
useHasEffectsPanel,
} from '../components/global-styles/effects-panel';
import { InspectorControls } from '../components';
import { store as blockEditorStore } from '../store';

export const SHADOW_SUPPORT_KEY = 'shadow';
export const EFFECTS_SUPPORT_KEYS = [ SHADOW_SUPPORT_KEY ];

export function hasEffectsSupport( blockName ) {
return EFFECTS_SUPPORT_KEYS.some( ( key ) =>
hasBlockSupport( blockName, key )
);
}

function EffectsInspectorControl( { children, resetAllFilter } ) {
return (
<InspectorControls group="effects" resetAllFilter={ resetAllFilter }>
{ children }
</InspectorControls>
);
}
export function EffectsPanel( { clientId, setAttributes, settings } ) {
const isEnabled = useHasEffectsPanel( settings );
const blockAttributes = useSelect(
( select ) => select( blockEditorStore ).getBlockAttributes( clientId ),
[ clientId ]
);
const shadow = blockAttributes?.style?.shadow;
const value = { shadow };

const onChange = ( newValue ) => {
setAttributes( {
style: { ...blockAttributes.style, shadow: newValue.shadow },
} );
};

if ( ! isEnabled ) {
return null;
}

return (
<StylesEffectsPanel
as={ EffectsInspectorControl }
panelId={ clientId }
settings={ settings }
value={ value }
onChange={ onChange }
/>
);
}
1 change: 1 addition & 0 deletions packages/block-editor/src/hooks/index.js
Expand Up @@ -68,6 +68,7 @@ createBlockSaveFilter( [
export { useCustomSides } from './dimensions';
export { useLayoutClasses, useLayoutStyles } from './layout';
export { getBorderClassesAndStyles, useBorderProps } from './use-border-props';
export { getShadowClassesAndStyles, useShadowProps } from './use-shadow-props';
export { getColorClassesAndStyles, useColorProps } from './use-color-props';
export { getSpacingClassesAndStyles } from './use-spacing-props';
export { getTypographyClassesAndStyles } from './use-typography-props';
Expand Down
1 change: 1 addition & 0 deletions packages/block-editor/src/hooks/index.native.js
Expand Up @@ -28,6 +28,7 @@ createBlockSaveFilter( [
] );

export { getBorderClassesAndStyles, useBorderProps } from './use-border-props';
export { getShadowClassesAndStyles, useShadowProps } from './use-shadow-props';
export { getColorClassesAndStyles, useColorProps } from './use-color-props';
export { getSpacingClassesAndStyles } from './use-spacing-props';
export { useCachedTruthy } from './use-cached-truthy';
Expand Down
8 changes: 8 additions & 0 deletions packages/block-editor/src/hooks/style.js
Expand Up @@ -27,6 +27,11 @@ import {
SPACING_SUPPORT_KEY,
DimensionsPanel,
} from './dimensions';
import {
EFFECTS_SUPPORT_KEYS,
SHADOW_SUPPORT_KEY,
EffectsPanel,
} from './effects';
import {
shouldSkipSerialization,
useStyleOverride,
Expand All @@ -37,6 +42,7 @@ import { useBlockEditingMode } from '../components/block-editing-mode';

const styleSupportKeys = [
...TYPOGRAPHY_SUPPORT_KEYS,
...EFFECTS_SUPPORT_KEYS,
BORDER_SUPPORT_KEY,
COLOR_SUPPORT_KEY,
DIMENSIONS_SUPPORT_KEY,
Expand Down Expand Up @@ -110,6 +116,7 @@ const skipSerializationPathsEdit = {
[ `${ SPACING_SUPPORT_KEY }.__experimentalSkipSerialization` ]: [
SPACING_SUPPORT_KEY,
],
[ `${ SHADOW_SUPPORT_KEY }` ]: [ SHADOW_SUPPORT_KEY ],
};

/**
Expand Down Expand Up @@ -336,6 +343,7 @@ function BlockStyleControls( {
<TypographyPanel { ...passedProps } />
<BorderPanel { ...passedProps } />
<DimensionsPanel { ...passedProps } />
<EffectsPanel { ...passedProps } />
</>
);
}
Expand Down
2 changes: 2 additions & 0 deletions packages/block-editor/src/hooks/supports.js
Expand Up @@ -59,8 +59,10 @@ const TYPOGRAPHY_SUPPORT_KEYS = [
WRITING_MODE_SUPPORT_KEY,
LETTER_SPACING_SUPPORT_KEY,
];
const EFFECTS_SUPPORT_KEYS = [ 'shadow' ];
const SPACING_SUPPORT_KEY = 'spacing';
const styleSupportKeys = [
...EFFECTS_SUPPORT_KEYS,
...TYPOGRAPHY_SUPPORT_KEYS,
BORDER_SUPPORT_KEY,
COLOR_SUPPORT_KEY,
Expand Down
39 changes: 39 additions & 0 deletions packages/block-editor/src/hooks/test/effects.js
@@ -0,0 +1,39 @@
/**
* Internal dependencies
*/
import { hasEffectsSupport } from '../effects';

describe( 'effects', () => {
describe( 'hasEffectsSupport', () => {
it( 'should return false if the block does not support effects', () => {
const settings = {
supports: {
shadow: false,
},
};

expect( hasEffectsSupport( settings ) ).toBe( false );
} );

it( 'should return true if the block supports effects', () => {
const settings = {
supports: {
shadow: true,
},
};

expect( hasEffectsSupport( settings ) ).toBe( true );
} );

it( 'should return true if the block supports effects and other features', () => {
const settings = {
supports: {
shadow: true,
align: true,
},
};

expect( hasEffectsSupport( settings ) ).toBe( true );
} );
} );
} );
37 changes: 37 additions & 0 deletions packages/block-editor/src/hooks/use-shadow-props.js
@@ -0,0 +1,37 @@
/**
* Internal dependencies
*/
import { getInlineStyles } from './style';

// This utility is intended to assist where the serialization of the shadow
// block support is being skipped for a block but the shadow related CSS classes
// & styles still need to be generated so they can be applied to inner elements.

/**
* Provides the CSS class names and inline styles for a block's shadow support
* attributes.
*
* @param {Object} attributes Block attributes.
* @return {Object} Shadow block support derived CSS classes & styles.
*/
export function getShadowClassesAndStyles( attributes ) {
const shadow = attributes.style?.shadow || '';

return {
className: undefined,
style: getInlineStyles( { shadow } ),
};
}

/**
* Derives the shadow related props for a block from its shadow block support
* attributes.
*
* @param {Object} attributes Block attributes.
*
* @return {Object} ClassName & style props from shadow block support.
*/
export function useShadowProps( attributes ) {
const shadowProps = getShadowClassesAndStyles( attributes );
return shadowProps;
}
6 changes: 5 additions & 1 deletion packages/block-editor/src/hooks/utils.js
Expand Up @@ -221,6 +221,7 @@ export function useBlockSettings( name, parentLayout ) {
isTextEnabled,
isHeadingEnabled,
isButtonEnabled,
shadow,
] = useSettings(
'background.backgroundImage',
'background.backgroundSize',
Expand Down Expand Up @@ -268,7 +269,8 @@ export function useBlockSettings( name, parentLayout ) {
'color.link',
'color.text',
'color.heading',
'color.button'
'color.button',
'shadow'
);

const rawSettings = useMemo( () => {
Expand Down Expand Up @@ -345,6 +347,7 @@ export function useBlockSettings( name, parentLayout ) {
},
layout,
parentLayout,
shadow,
};
}, [
backgroundImage,
Expand Down Expand Up @@ -395,6 +398,7 @@ export function useBlockSettings( name, parentLayout ) {
isTextEnabled,
isHeadingEnabled,
isButtonEnabled,
shadow,
] );

return useSettingsForBlockElement( rawSettings, name );
Expand Down
2 changes: 2 additions & 0 deletions packages/block-editor/src/index.js
Expand Up @@ -11,6 +11,8 @@ export {
useCustomSides as __experimentalUseCustomSides,
getSpacingClassesAndStyles as __experimentalGetSpacingClassesAndStyles,
getGapCSSValue as __experimentalGetGapCSSValue,
getShadowClassesAndStyles as __experimentalGetShadowClassesAndStyles,
useShadowProps as __experimentalUseShadowProps,
useCachedTruthy,
} from './hooks';
export * from './components';
Expand Down
3 changes: 3 additions & 0 deletions packages/block-library/src/button/edit.js
Expand Up @@ -32,6 +32,7 @@ import {
__experimentalUseBorderProps as useBorderProps,
__experimentalUseColorProps as useColorProps,
__experimentalGetSpacingClassesAndStyles as useSpacingProps,
__experimentalUseShadowProps as useShadowProps,
__experimentalLinkControl as LinkControl,
__experimentalGetElementClassName,
store as blockEditorStore,
Expand Down Expand Up @@ -184,6 +185,7 @@ function ButtonEdit( props ) {
const borderProps = useBorderProps( attributes );
const colorProps = useColorProps( attributes );
const spacingProps = useSpacingProps( attributes );
const shadowProps = useShadowProps( attributes );
const ref = useRef();
const richTextRef = useRef();
const blockProps = useBlockProps( {
Expand Down Expand Up @@ -266,6 +268,7 @@ function ButtonEdit( props ) {
...borderProps.style,
...colorProps.style,
...spacingProps.style,
...shadowProps.style,
} }
onSplit={ ( value ) =>
createBlock( 'core/button', {
Expand Down
3 changes: 3 additions & 0 deletions packages/block-library/src/button/save.js
Expand Up @@ -12,6 +12,7 @@ import {
__experimentalGetBorderClassesAndStyles as getBorderClassesAndStyles,
__experimentalGetColorClassesAndStyles as getColorClassesAndStyles,
__experimentalGetSpacingClassesAndStyles as getSpacingClassesAndStyles,
__experimentalGetShadowClassesAndStyles as getShadowClassesAndStyles,
__experimentalGetElementClassName,
} from '@wordpress/block-editor';

Expand Down Expand Up @@ -40,6 +41,7 @@ export default function save( { attributes, className } ) {
const borderProps = getBorderClassesAndStyles( attributes );
const colorProps = getColorClassesAndStyles( attributes );
const spacingProps = getSpacingClassesAndStyles( attributes );
const shadowProps = getShadowClassesAndStyles( attributes );
const buttonClasses = classnames(
'wp-block-button__link',
colorProps.className,
Expand All @@ -56,6 +58,7 @@ export default function save( { attributes, className } ) {
...borderProps.style,
...colorProps.style,
...spacingProps.style,
...shadowProps.style,
};

// The use of a `title` attribute here is soft-deprecated, but still applied
Expand Down
1 change: 1 addition & 0 deletions packages/components/CHANGELOG.md
Expand Up @@ -10,6 +10,7 @@
- `PaletteEdit` and `CircularOptionPicker`: improve unit tests ([#57809](https://github.com/WordPress/gutenberg/pull/57809)).
- `Tooltip`: no-op when nested inside other `Tooltip` components ([#57202](https://github.com/WordPress/gutenberg/pull/57202)).
- `Tooltip` and `Button`: tidy up unit tests ([#57975](https://github.com/WordPress/gutenberg/pull/57975)).
- `SlotFill`: fix typo in use-slot-fills return docs ([#57654](https://github.com/WordPress/gutenberg/pull/57654))

### Bug Fix

Expand Down

0 comments on commit 43ac2af

Please sign in to comment.