diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md index 60147a5bac7fd8..875af65f62ef90 100644 --- a/docs/reference-guides/core-blocks.md +++ b/docs/reference-guides/core-blocks.md @@ -50,7 +50,7 @@ Prompt visitors to take action with a button-style link. ([Source](https://githu - **Name:** core/button - **Category:** design -- **Supports:** anchor, color (background, gradients, text), spacing (padding), typography (fontSize, lineHeight), ~~alignWide~~, ~~align~~, ~~reusable~~ +- **Supports:** anchor, color (background, gradients, text), shadow, spacing (padding), typography (fontSize, lineHeight), ~~alignWide~~, ~~align~~, ~~reusable~~ - **Attributes:** backgroundColor, gradient, linkTarget, placeholder, rel, text, textAlign, textColor, title, url, width ## Buttons diff --git a/lib/theme.json b/lib/theme.json index d280f1bea45c46..8e60c945456b1b 100644 --- a/lib/theme.json +++ b/lib/theme.json @@ -187,16 +187,32 @@ "text": true }, "shadow": { + "defaultPresets": true, "presets": [ { "name": "Natural", "slug": "natural", - "shadow": "0 .2rem .3rem 0 rgba(0,0,0, 0.3), 0 .5rem .6rem 0 rgba(0,0,0, 0.4)" + "shadow": "6px 6px 9px rgba(0, 0, 0, 0.2)" + }, + { + "name": "Deep", + "slug": "deep", + "shadow": "12px 12px 50px rgba(0, 0, 0, 0.4)" }, { "name": "Sharp", "slug": "sharp", - "shadow": ".5rem .5rem 0 0 rgba(0,0,0, 0.4)" + "shadow": "6px 6px 0px rgba(0, 0, 0, 0.2)" + }, + { + "name": "Outlined", + "slug": "outlined", + "shadow": "6px 6px 0px -3px rgba(255, 255, 255, 1), 6px 6px rgba(0, 0, 0, 1)" + }, + { + "name": "Crisp", + "slug": "crisp", + "shadow": "6px 6px 0px rgba(0, 0, 0, 1)" } ] }, diff --git a/packages/block-editor/src/components/global-styles/utils.js b/packages/block-editor/src/components/global-styles/utils.js index e3c3bf47d4a5a3..4e0dbedfcafecc 100644 --- a/packages/block-editor/src/components/global-styles/utils.js +++ b/packages/block-editor/src/components/global-styles/utils.js @@ -61,6 +61,12 @@ export const PRESET_METADATA = [ valueFunc: ( { slug } ) => `url( '#wp-duotone-${ slug }' )`, classes: [], }, + { + path: [ 'shadow', 'presets' ], + valueKey: 'shadow', + cssVarInfix: 'shadow', + classes: [], + }, { path: [ 'typography', 'fontSizes' ], valueFunc: ( preset, { typography: typographySettings } ) => @@ -127,6 +133,7 @@ export const STYLE_PATH_TO_CSS_VAR_INFIX = { 'elements.h6.typography.fontFamily': 'font-family', 'elements.h6.color.gradient': 'gradient', 'color.gradient': 'gradient', + shadow: 'shadow', 'typography.fontSize': 'font-size', 'typography.fontFamily': 'font-family', }; diff --git a/packages/block-library/src/button/block.json b/packages/block-library/src/button/block.json index f34437f74b573e..cc9921953abf66 100644 --- a/packages/block-library/src/button/block.json +++ b/packages/block-library/src/button/block.json @@ -83,6 +83,7 @@ } }, "reusable": false, + "shadow": true, "spacing": { "__experimentalSkipSerialization": true, "padding": [ "horizontal", "vertical" ], diff --git a/packages/edit-site/src/components/global-styles/context-menu.js b/packages/edit-site/src/components/global-styles/context-menu.js index ed56c3ffacc771..cedb17a41699b6 100644 --- a/packages/edit-site/src/components/global-styles/context-menu.js +++ b/packages/edit-site/src/components/global-styles/context-menu.js @@ -85,9 +85,9 @@ function ContextMenu( { name, parentMenu = '' } ) { - { __( 'Border' ) } + { __( 'Border & Shadow' ) } ) } { hasLayoutPanel && ( diff --git a/packages/edit-site/src/components/global-styles/hooks.js b/packages/edit-site/src/components/global-styles/hooks.js index 0dadd18c7827dc..dd85c8896c6a5c 100644 --- a/packages/edit-site/src/components/global-styles/hooks.js +++ b/packages/edit-site/src/components/global-styles/hooks.js @@ -71,6 +71,11 @@ export function getSupportedGlobalStylesPanels( name ) { supportKeys.push( 'blockGap' ); } + // check for shadow support + if ( blockType?.supports?.shadow ) { + supportKeys.push( 'shadow' ); + } + Object.keys( STYLE_PROPERTY ).forEach( ( styleName ) => { if ( ! STYLE_PROPERTY[ styleName ].support ) { return; diff --git a/packages/edit-site/src/components/global-styles/screen-border.js b/packages/edit-site/src/components/global-styles/screen-border.js index 6a5578e20fcefa..b0d07638242675 100644 --- a/packages/edit-site/src/components/global-styles/screen-border.js +++ b/packages/edit-site/src/components/global-styles/screen-border.js @@ -10,17 +10,22 @@ import ScreenHeader from './header'; import BorderPanel, { useHasBorderPanel } from './border-panel'; import BlockPreviewPanel from './block-preview-panel'; import { getVariationClassName } from './utils'; +import ShadowPanel, { useHasShadowControl } from './shadow-panel'; function ScreenBorder( { name, variation = '' } ) { const hasBorderPanel = useHasBorderPanel( name ); const variationClassName = getVariationClassName( variation ); + const hasShadowPanel = useHasShadowControl( name ); return ( <> - + { hasBorderPanel && ( ) } + { hasShadowPanel && ( + + ) } ); } diff --git a/packages/edit-site/src/components/global-styles/shadow-panel.js b/packages/edit-site/src/components/global-styles/shadow-panel.js new file mode 100644 index 00000000000000..7d7b6a381564ba --- /dev/null +++ b/packages/edit-site/src/components/global-styles/shadow-panel.js @@ -0,0 +1,174 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +/** + * WordPress dependencies + */ +import { + __experimentalToolsPanel as ToolsPanel, + __experimentalToolsPanelItem as ToolsPanelItem, + __experimentalItemGroup as ItemGroup, + __experimentalHStack as HStack, + __experimentalVStack as VStack, + __experimentalGrid as Grid, + __experimentalHeading as Heading, + FlexItem, + Dropdown, + __experimentalDropdownContentWrapper as DropdownContentWrapper, + Button, +} from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; +import { shadow as shadowIcon, Icon, check } from '@wordpress/icons'; +import { useCallback } from '@wordpress/element'; +import { experiments as blockEditorExperiments } from '@wordpress/block-editor'; + +/** + * Internal dependencies + */ +import { getSupportedGlobalStylesPanels } from './hooks'; +import { IconWithCurrentColor } from './icon-with-current-color'; +import { unlock } from '../../experiments'; + +const { useGlobalSetting, useGlobalStyle } = unlock( blockEditorExperiments ); + +export function useHasShadowControl( name ) { + const supports = getSupportedGlobalStylesPanels( name ); + return supports.includes( 'shadow' ); +} + +export default function ShadowPanel( { name, variation = '' } ) { + const prefix = variation ? `variations.${ variation }.` : ''; + const [ shadow, setShadow ] = useGlobalStyle( `${ prefix }shadow`, name ); + const [ userShadow ] = useGlobalStyle( `${ prefix }shadow`, name, 'user' ); + const hasShadow = () => !! userShadow; + + const resetShadow = () => setShadow( undefined ); + const resetAll = useCallback( + () => resetShadow( undefined ), + [ resetShadow ] + ); + + return ( + + + + + + + + ); +} + +const ShadowPopover = ( { shadow, onShadowChange } ) => { + const popoverProps = { + placement: 'left-start', + offset: 36, + shift: true, + }; + + return ( + ( + + + + ) } + /> + ); +}; + +function renderShadowToggle() { + return ( { onToggle, isOpen } ) => { + const toggleProps = { + onClick: onToggle, + className: classnames( { 'is-open': isOpen } ), + 'aria-expanded': isOpen, + }; + + return ( + + ); + }; +} + +function ShadowPopoverContainer( { shadow, onShadowChange } ) { + const [ defaultShadows ] = useGlobalSetting( 'shadow.presets.default' ); + const [ themeShadows ] = useGlobalSetting( 'shadow.presets.theme' ); + const [ defaultPresetsEnabled ] = useGlobalSetting( + 'shadow.defaultPresets' + ); + + const shadows = [ + ...( defaultPresetsEnabled ? defaultShadows : [] ), + ...( themeShadows || [] ), + ]; + + return ( +
+ + { __( 'Shadows' ) } + + +
+ ); +} + +function ShadowPresets( { presets, activeShadow, onSelect } ) { + return ! presets ? null : ( + + { presets.map( ( { name, shadow }, i ) => ( + + onSelect( shadow === activeShadow ? undefined : shadow ) + } + shadow={ shadow } + /> + ) ) } + + ); +} + +function ShadowIndicator( { label, isActive, onSelect, shadow } ) { + return ( +
+ +
+ ); +} diff --git a/packages/edit-site/src/components/global-styles/style.scss b/packages/edit-site/src/components/global-styles/style.scss index 268536ff0433cc..f21dd1f612b77d 100644 --- a/packages/edit-site/src/components/global-styles/style.scss +++ b/packages/edit-site/src/components/global-styles/style.scss @@ -190,3 +190,42 @@ $block-preview-height: 150px; display: flex; flex-direction: column; } + +.edit-site-global-styles__shadow-panel { + width: 230px; +} + +.edit-site-global-styles__shadow-dropdown { + display: block; + padding: 0; + + > button { + width: 100%; + padding: $grid-unit-10; + + &.is-open { + background-color: $gray-100; + } + } +} + +// wrapper to clip the shadow beyond 6px +.edit-site-global-styles__shadow-indicator-wrapper { + padding: 6px; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; +} + +// These styles are similar to the color palette. +.edit-site-global-styles__shadow-indicator { + color: $gray-800; + border: $gray-200 $border-width solid; + border-radius: $radius-block-ui; + cursor: pointer; + padding: 0; + + height: 24px; + width: 24px; +}