diff --git a/packages/block-editor/src/components/background-color-control/index.js b/packages/block-editor/src/components/background-color-control/index.js new file mode 100644 index 00000000000000..47fee780843aae --- /dev/null +++ b/packages/block-editor/src/components/background-color-control/index.js @@ -0,0 +1,265 @@ +/** + * External dependencies + */ +import clsx from 'clsx'; + +/** + * WordPress dependencies + */ +import { + Flex, + FlexItem, + __experimentalItem as Item, + __experimentalHStack as HStack, + __experimentalZStack as ZStack, + Dropdown, + Button, + ColorIndicator, + __experimentalDropdownContentWrapper as DropdownContentWrapper, + privateApis as componentsPrivateApis, +} from '@wordpress/components'; +import { __, sprintf } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import ColorGradientControl from '../colors-gradients/control'; +import { unlock } from '../../lock-unlock'; +import { + useColorsPerOrigin, + useGradientsPerOrigin, +} from '../global-styles/hooks'; +import { getValueFromVariable } from '../global-styles/utils'; +import { setImmutably } from '../../utils/object'; + +const BACKGROUND_POPOVER_PROPS = { + placement: 'left-start', + offset: 36, + shift: true, + className: 'block-editor-global-styles-background-panel__popover', +}; + +function ColorPanelTab( { + isGradient, + inheritedValue, + userValue, + setValue, + colorGradientControlSettings, +} ) { + return ( + + ); +} + +const LabeledColorIndicators = ( { indicators, label } ) => ( + + + + { indicators.map( ( indicator, index ) => ( + + + + ) ) } + + + { label } + + + +); + +const { Tabs } = unlock( componentsPrivateApis ); + +export default function BackgroundColorPanel( { + value, + onChange, + inheritedValue = value, + settings, + label = __( 'Color' ), +} ) { + const colors = useColorsPerOrigin( settings ); + const gradients = useGradientsPerOrigin( settings ); + const areCustomSolidsEnabled = settings?.color?.custom; + const areCustomGradientsEnabled = settings?.color?.customGradient; + + const decodeValue = ( rawValue ) => + getValueFromVariable( { settings }, '', rawValue ); + + const backgroundColor = decodeValue( inheritedValue?.color?.background ); + const backgroundGradient = decodeValue( inheritedValue?.color?.gradient ); + + const userBackgroundColor = decodeValue( value?.color?.background ); + const userGradient = decodeValue( value?.color?.gradient ); + + const setBackgroundColor = ( newColor ) => { + const newValue = setImmutably( + value, + [ 'color', 'background' ], + encodeColorValue( newColor ) + ); + newValue.color.gradient = undefined; + onChange( newValue ); + }; + + const setGradient = ( newGradient ) => { + const newValue = setImmutably( + value, + [ 'color', 'gradient' ], + encodeGradientValue( newGradient ) + ); + newValue.color.background = undefined; + onChange( newValue ); + }; + + const encodeColorValue = ( colorValue ) => { + const allColors = colors.flatMap( + ( { colors: originColors } ) => originColors + ); + const colorObject = allColors.find( + ( { color } ) => color === colorValue + ); + return colorObject + ? 'var:preset|color|' + colorObject.slug + : colorValue; + }; + + const encodeGradientValue = ( gradientValue ) => { + const allGradients = gradients.flatMap( + ( { gradients: originGradients } ) => originGradients + ); + const gradientObject = allGradients.find( + ( { gradient } ) => gradient === gradientValue + ); + return gradientObject + ? 'var:preset|gradient|' + gradientObject.slug + : gradientValue; + }; + + const hasSolidColors = colors.length > 0 || areCustomSolidsEnabled; + const hasGradientColors = gradients.length > 0 || areCustomGradientsEnabled; + + const tabs = [ + hasSolidColors && { + key: 'background', + label: __( 'Background' ), + inheritedValue: backgroundColor, + setValue: setBackgroundColor, + userValue: userBackgroundColor, + }, + hasGradientColors && { + key: 'gradient', + label: __( 'Gradient' ), + inheritedValue: backgroundGradient, + setValue: setGradient, + userValue: userGradient, + isGradient: true, + }, + ].filter( Boolean ); + + const colorGradientControlSettings = { + colors, + disableCustomColors: ! areCustomSolidsEnabled, + gradients, + disableCustomGradients: ! areCustomGradientsEnabled, + }; + + const indicators = [ backgroundGradient ?? backgroundColor ]; + const currentTab = tabs.find( ( tab ) => tab.userValue !== undefined ); + const { key: firstTabKey, ...firstTab } = tabs[ 0 ] ?? {}; + + return ( + + { + const toggleProps = { + onClick: onToggle, + className: clsx( + 'block-editor-panel-color-gradient-settings__dropdown', + { 'is-open': isOpen } + ), + 'aria-expanded': isOpen, + 'aria-label': sprintf( + /* translators: %s is the type of color property, e.g., "background" */ + __( 'Color %s styles' ), + label + ), + }; + + return ( + + ); + } } + renderContent={ () => ( + +
+ { tabs.length === 1 && ( + + ) } + { tabs.length > 1 && ( + + + { tabs.map( ( tab ) => ( + + { tab.label } + + ) ) } + + + { tabs.map( ( tab ) => { + const { key: tabKey, ...restTabProps } = + tab; + return ( + + + + ); + } ) } + + ) } +
+
+ ) } + /> +
+ ); +} diff --git a/packages/block-editor/src/components/background-color-control/style.scss b/packages/block-editor/src/components/background-color-control/style.scss new file mode 100644 index 00000000000000..82c0b2c3e7a3e4 --- /dev/null +++ b/packages/block-editor/src/components/background-color-control/style.scss @@ -0,0 +1,16 @@ + +.block-editor-global-styles-background-panel__inspector-media-replace-container { + .block-editor-global-styles-background-panel__color-tools-panel-item { + padding: 0; + + button { + padding: 0; + } + } +} + +.block-editor-global-styles-background-panel__color-tools-panel-item { + button { + padding: 0; + } +} diff --git a/packages/block-editor/src/components/background-image-control/index.js b/packages/block-editor/src/components/background-image-control/index.js index 2703aa3988d64e..7af1888d603c39 100644 --- a/packages/block-editor/src/components/background-image-control/index.js +++ b/packages/block-editor/src/components/background-image-control/index.js @@ -18,17 +18,19 @@ import { MenuItem, VisuallyHidden, __experimentalItemGroup as ItemGroup, + __experimentalItem as Item, __experimentalHStack as HStack, __experimentalTruncate as Truncate, Dropdown, Placeholder, Spinner, + ColorIndicator, __experimentalDropdownContentWrapper as DropdownContentWrapper, } from '@wordpress/components'; import { __, _x, sprintf } from '@wordpress/i18n'; import { store as noticesStore } from '@wordpress/notices'; import { getFilename } from '@wordpress/url'; -import { useRef, useState, useEffect, useMemo } from '@wordpress/element'; +import { useRef, useState, useMemo } from '@wordpress/element'; import { useDispatch, useSelect } from '@wordpress/data'; import { focus } from '@wordpress/dom'; import { isBlobURL } from '@wordpress/blob'; @@ -116,13 +118,7 @@ function InspectorImagePreviewItem( { filename, label, className, - onToggleCallback = noop, } ) { - useEffect( () => { - if ( typeof toggleProps?.isOpen !== 'undefined' ) { - onToggleCallback( toggleProps?.isOpen ); - } - }, [ toggleProps?.isOpen, onToggleCallback ] ); return ( - { imgUrl && ( - - - - ) } + { const toggleProps = { onClick: onToggle, - className: + className: clsx( 'block-editor-global-styles-background-panel__dropdown-toggle', + { + 'is-open': isOpen, + } + ), 'aria-expanded': isOpen, 'aria-label': __( 'Background size, position and repeat options.' @@ -201,7 +196,6 @@ function BackgroundControlsPanel( { label={ imgLabel } toggleProps={ toggleProps } as="button" - onToggleCallback={ onToggleCallback } /> ); } } @@ -348,11 +342,10 @@ function BackgroundImageControls( { } ) ); const canRemove = ! hasValue && hasBackgroundImageValue( inheritedValue ); - const imgLabel = - title || getFilename( url ) || __( 'Add background image' ); + const imgLabel = title || getFilename( url ) || __( 'Image' ); return ( -
@@ -400,7 +393,7 @@ function BackgroundImageControls( { onFilesDrop={ onFilesDrop } label={ __( 'Drop to upload' ) } /> -
+ ); } @@ -683,23 +676,13 @@ export default function BackgroundImagePanel( { settings?.background?.backgroundPosition || settings?.background?.backgroundRepeat ); - const [ isDropDownOpen, setIsDropDownOpen ] = useState( false ); - return ( -
+ <> { shouldShowBackgroundImageControls ? ( @@ -709,10 +692,8 @@ export default function BackgroundImagePanel( { inheritedValue={ resolvedInheritedValue } displayInPanel onResetImage={ () => { - setIsDropDownOpen( false ); resetBackground(); } } - onRemoveImage={ () => setIsDropDownOpen( false ) } defaultValues={ defaultValues } /> { - setIsDropDownOpen( false ); resetBackground(); } } - onRemoveImage={ () => setIsDropDownOpen( false ) } /> ) } -
+ ); } diff --git a/packages/block-editor/src/components/background-image-control/style.scss b/packages/block-editor/src/components/background-image-control/style.scss index cde8044c24c121..971c877805dba7 100644 --- a/packages/block-editor/src/components/background-image-control/style.scss +++ b/packages/block-editor/src/components/background-image-control/style.scss @@ -11,6 +11,7 @@ .block-editor-global-styles-background-panel__image-tools-panel-item { flex-grow: 1; border: 0; + padding: 0; .components-dropdown { display: block; @@ -55,10 +56,6 @@ &:hover { color: var(--wp-admin-theme-color); } - - &:focus { - box-shadow: inset 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color); - } } .block-editor-global-styles-background-panel__loading { @@ -79,6 +76,15 @@ height: 100%; width: 100%; padding-left: $grid-unit-15; + + &.is-open { + background-color: $gray-100; + } + + &:focus { + box-shadow: 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color); + outline: none; + } } .block-editor-global-styles-background-panel__dropdown-toggle { @@ -94,7 +100,6 @@ // Without this, the ellipsis can sometimes be partially hidden by the Button padding. text-align: start; - text-align-last: center; } .block-editor-global-styles-background-panel__inspector-preview-inner { @@ -107,25 +112,11 @@ .block-editor-global-styles-background-panel__inspector-image-indicator { background-size: cover; - border-radius: $radius-round; - width: 20px; - height: 20px; - display: block; - position: relative; -} + border-radius: $radius-small; -.block-editor-global-styles-background-panel__inspector-image-indicator::after { - content: ""; - position: absolute; - top: -1px; - left: -1px; - bottom: -1px; - right: -1px; - border-radius: $radius-round; - box-shadow: inset 0 0 0 $border-width rgba(0, 0, 0, 0.2); - // Show a thin outline in Windows high contrast mode, otherwise the button is invisible. - border: 1px solid transparent; - box-sizing: inherit; + &.has-image { + background-image: var(--image-url); + } } .block-editor-global-styles-background-panel__dropdown-content-wrapper { @@ -154,6 +145,12 @@ z-index: z-index(".block-editor-global-styles-background-panel__popover"); } +.block-editor-global-styles-background-panel__popover { + .block-editor-global-styles-background-panel__image-tools-panel-item { + padding: 0; + } +} + .block-editor-global-styles-background-panel__media-replace-popover { .components-popover__content { // width of block-editor-global-styles-background-panel__dropdown-content-wrapper minus padding. diff --git a/packages/block-editor/src/components/global-styles/background-panel.js b/packages/block-editor/src/components/global-styles/background-panel.js index c66ea01bce5498..5f486bc8ef728b 100644 --- a/packages/block-editor/src/components/global-styles/background-panel.js +++ b/packages/block-editor/src/components/global-styles/background-panel.js @@ -4,18 +4,21 @@ import { __experimentalToolsPanel as ToolsPanel, __experimentalToolsPanelItem as ToolsPanelItem, + __experimentalItemGroup as ItemGroup, } from '@wordpress/components'; import { useCallback, Platform } from '@wordpress/element'; /** * Internal dependencies */ import BackgroundImageControl from '../background-image-control'; +import BackgroundColorControl from '../background-color-control'; import { useToolsPanelDropdownMenuProps } from './utils'; import { setImmutably } from '../../utils/object'; import { __ } from '@wordpress/i18n'; const DEFAULT_CONTROLS = { backgroundImage: true, + backgroundColor: true, }; /** @@ -96,7 +99,8 @@ export default function BackgroundImagePanel( { panelId, defaultControls = DEFAULT_CONTROLS, defaultValues = {}, - headerLabel = __( 'Background image' ), + headerLabel = __( 'Elements' ), + showColorControl = false, } ) { const showBackgroundImageControl = useHasBackgroundPanel( settings ); const resetBackground = () => @@ -105,9 +109,20 @@ export default function BackgroundImagePanel( { return { ...previousValue, background: {}, + color: undefined, }; }, [] ); + const shouldShowBackgroundColorControls = useHasBackgroundPanel( settings ); + const resetBackgroundColor = () => { + const newValue = setImmutably( + value, + [ 'color', 'background' ], + undefined + ); + newValue.color.gradient = undefined; + onChange( newValue ); + }; return ( - { showBackgroundImageControl && ( - !! value?.background } - label={ __( 'Image' ) } - onDeselect={ resetBackground } - isShownByDefault={ defaultControls.backgroundImage } - panelId={ panelId } - > - - - ) } + + { showBackgroundImageControl && ( + !! value?.background } + label={ __( 'Image' ) } + onDeselect={ resetBackground } + isShownByDefault={ defaultControls.backgroundImage } + panelId={ panelId } + > + + + ) } + { shouldShowBackgroundColorControls && showColorControl && ( + !! value?.color } + label={ __( 'Color' ) } + onDeselect={ resetBackgroundColor } + isShownByDefault={ defaultControls.backgroundColor } + panelId={ panelId } + > + + + ) } + ); } diff --git a/packages/block-editor/src/style.scss b/packages/block-editor/src/style.scss index 512169351fe1fb..31ca4c777498f9 100644 --- a/packages/block-editor/src/style.scss +++ b/packages/block-editor/src/style.scss @@ -1,5 +1,6 @@ @import "./autocompleters/style.scss"; @import "./components/background-image-control/style.scss"; +@import "./components/background-color-control/style.scss"; @import "./components/block-alignment-control/style.scss"; @import "./components/block-canvas/style.scss"; @import "./components/block-icon/style.scss"; diff --git a/packages/edit-site/src/components/global-styles/background-panel.js b/packages/edit-site/src/components/global-styles/background-panel.js index e185079d8cee04..553e2e67c0598a 100644 --- a/packages/edit-site/src/components/global-styles/background-panel.js +++ b/packages/edit-site/src/components/global-styles/background-panel.js @@ -50,6 +50,7 @@ export default function BackgroundPanel() { onChange={ setStyle } settings={ settings } defaultValues={ BACKGROUND_DEFAULT_VALUES } + showColorControl /> ); }