From 7873d0b2ee607b1dfd8fd04be64ef8b234c2dc03 Mon Sep 17 00:00:00 2001 From: Jon Q Date: Tue, 9 Jun 2020 14:55:50 -0400 Subject: [PATCH 1/4] Cover: Padding: Fix reset handling + add visualizer rendering interactions --- packages/block-editor/src/hooks/padding.js | 28 +++++++++++--- packages/block-editor/src/hooks/style.js | 14 +++++-- packages/block-library/src/cover/edit.js | 5 ++- .../src/box-control/all-input-control.js | 27 +++++++++++++- packages/components/src/box-control/index.js | 37 ++++++++++++++----- .../src/box-control/input-controls.js | 18 +++++++++ .../src/box-control/stories/index.js | 8 +++- .../src/box-control/unit-control.js | 18 ++++++++- packages/components/src/box-control/utils.js | 29 ++++++++++++--- .../components/src/box-control/visualizer.js | 35 ++++++++++-------- 10 files changed, 175 insertions(+), 44 deletions(-) diff --git a/packages/block-editor/src/hooks/padding.js b/packages/block-editor/src/hooks/padding.js index ce9316309e423..e042a2de83cac 100644 --- a/packages/block-editor/src/hooks/padding.js +++ b/packages/block-editor/src/hooks/padding.js @@ -37,7 +37,22 @@ export function PaddingEdit( props ) { const onChange = ( next ) => { const newStyle = { ...style, - padding: next, + spacing: { + padding: next, + }, + }; + + setAttributes( { + style: cleanEmptyObject( newStyle ), + } ); + }; + + const onChangeShowVisualizer = ( next ) => { + const newStyle = { + ...style, + visualizers: { + padding: next, + }, }; setAttributes( { @@ -49,8 +64,9 @@ export function PaddingEdit( props ) { web: ( <> @@ -61,8 +77,8 @@ export function PaddingEdit( props ) { } export const paddingStyleMappings = { - paddingTop: [ 'padding', 'top' ], - paddingRight: [ 'padding', 'right' ], - paddingBottom: [ 'padding', 'bottom' ], - paddingLeft: [ 'padding', 'left' ], + paddingTop: [ 'spacing', 'padding', 'top' ], + paddingRight: [ 'spacing', 'padding', 'right' ], + paddingBottom: [ 'spacing', 'padding', 'bottom' ], + paddingLeft: [ 'spacing', 'padding', 'left' ], }; diff --git a/packages/block-editor/src/hooks/style.js b/packages/block-editor/src/hooks/style.js index f8fbf30c6b6e3..a3b101b86790a 100644 --- a/packages/block-editor/src/hooks/style.js +++ b/packages/block-editor/src/hooks/style.js @@ -74,11 +74,17 @@ export function getInlineStyles( styles = {} ) { }; const output = {}; - Object.entries( mappings ).forEach( ( [ styleKey, objectKey ] ) => { - if ( has( styles, objectKey ) ) { - output[ styleKey ] = compileStyleValue( get( styles, objectKey ) ); + Object.entries( mappings ).forEach( + ( [ styleKey, ...otherObjectKeys ] ) => { + const [ objectKeys ] = otherObjectKeys; + + if ( has( styles, objectKeys ) ) { + output[ styleKey ] = compileStyleValue( + get( styles, objectKeys ) + ); + } } - } ); + ); return output; } diff --git a/packages/block-library/src/cover/edit.js b/packages/block-library/src/cover/edit.js index 076d0881cbe6e..6f2794da106b2 100644 --- a/packages/block-library/src/cover/edit.js +++ b/packages/block-library/src/cover/edit.js @@ -475,7 +475,10 @@ function CoverEdit( { <> { controls } - + { diff --git a/packages/components/src/box-control/all-input-control.js b/packages/components/src/box-control/all-input-control.js index 8a40dd47e6612..1f1260898505d 100644 --- a/packages/components/src/box-control/all-input-control.js +++ b/packages/components/src/box-control/all-input-control.js @@ -6,16 +6,19 @@ import { noop } from 'lodash'; * Internal dependencies */ import UnitControl from './unit-control'; -import { LABELS, getAllValue, isValuesMixed } from './utils'; +import { LABELS, getAllValue, isValuesMixed, isValuesDefined } from './utils'; export default function AllInputControl( { onChange = noop, onFocus = noop, + onHoverOn = noop, + onHoverOff = noop, values, ...props } ) { const allValue = getAllValue( values ); - const isMixed = isValuesMixed( values ); + const hasValues = isValuesDefined( values ); + const isMixed = hasValues && isValuesMixed( values ); const allPlaceholder = isMixed ? LABELS.mixed : null; @@ -34,6 +37,24 @@ export default function AllInputControl( { onChange( nextValues ); }; + const handleOnHoverOn = () => { + onHoverOn( { + top: true, + bottom: true, + left: true, + right: true, + } ); + }; + + const handleOnHoverOff = () => { + onHoverOff( { + top: false, + bottom: false, + left: false, + right: false, + } ); + }; + return ( ); diff --git a/packages/components/src/box-control/index.js b/packages/components/src/box-control/index.js index 387fd317563f3..f75a4aee4b77d 100644 --- a/packages/components/src/box-control/index.js +++ b/packages/components/src/box-control/index.js @@ -7,7 +7,7 @@ import { noop } from 'lodash'; * WordPress dependencies */ import { useInstanceId } from '@wordpress/compose'; -import { useState, useRef } from '@wordpress/element'; +import { useState } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; /** @@ -26,7 +26,12 @@ import { Header, HeaderControlWrapper, } from './styles/box-control-styles'; -import { DEFAULT_VALUES, isValuesMixed } from './utils'; +import { + DEFAULT_VALUES, + DEFAULT_VISUALIZER_VALUES, + isValuesMixed, + isValuesDefined, +} from './utils'; import { useControlledState } from '../utils/hooks'; const defaultInputProps = { @@ -43,17 +48,21 @@ export default function BoxControl( { id: idProp, inputProps = defaultInputProps, onChange = noop, + onChangeShowVisualizer = noop, label = __( 'Box Control' ), - values: valuesProp = DEFAULT_VALUES, + values: valuesProp, units, } ) { const [ values, setValues ] = useControlledState( valuesProp ); + const inputValues = values || DEFAULT_VALUES; + const hasInitialValue = isValuesDefined( valuesProp ); - const [ isDirty, setIsDirty ] = useState( false ); - const [ isLinked, setIsLinked ] = useState( ! isValuesMixed( values ) ); - const [ side, setSide ] = useState( isLinked ? 'all' : 'top' ); + const [ isDirty, setIsDirty ] = useState( hasInitialValue ); + const [ isLinked, setIsLinked ] = useState( + ! hasInitialValue || ! isValuesMixed( inputValues ) + ); - const initialValuesRef = useRef( values ); + const [ side, setSide ] = useState( isLinked ? 'all' : 'top' ); const id = useUniqueId( idProp ); const headingId = `${ id }-heading`; @@ -73,8 +82,16 @@ export default function BoxControl( { setIsDirty( true ); }; + const handleOnHoverOn = ( next = {} ) => { + onChangeShowVisualizer( { ...DEFAULT_VISUALIZER_VALUES, ...next } ); + }; + + const handleOnHoverOff = ( next = {} ) => { + onChangeShowVisualizer( { ...DEFAULT_VISUALIZER_VALUES, ...next } ); + }; + const handleOnReset = () => { - const initialValues = initialValuesRef.current; + const initialValues = DEFAULT_VALUES; onChange( initialValues ); setValues( initialValues ); @@ -85,9 +102,11 @@ export default function BoxControl( { ...inputProps, onChange: handleOnChange, onFocus: handleOnFocus, + onHoverOn: handleOnHoverOn, + onHoverOff: handleOnHoverOff, isLinked, units, - values, + values: inputValues, }; return ( diff --git a/packages/components/src/box-control/input-controls.js b/packages/components/src/box-control/input-controls.js index 36242e1e2a0f2..0177052151273 100644 --- a/packages/components/src/box-control/input-controls.js +++ b/packages/components/src/box-control/input-controls.js @@ -13,6 +13,8 @@ import { LayoutContainer, Layout } from './styles/box-control-styles'; export default function BoxInputControls( { onChange = noop, onFocus = noop, + onHoverOn = noop, + onHoverOff = noop, values, ...props } ) { @@ -20,6 +22,14 @@ export default function BoxInputControls( { onFocus( event, { side } ); }; + const createHandleOnHoverOn = ( side ) => () => { + onHoverOn( { [ side ]: true } ); + }; + + const createHandleOnHoverOff = ( side ) => () => { + onHoverOff( { [ side ]: false } ); + }; + const handleOnChange = ( nextValues ) => { onChange( nextValues ); }; @@ -69,6 +79,8 @@ export default function BoxInputControls( { value={ top } onChange={ createHandleOnChange( 'top' ) } onFocus={ createHandleOnFocus( 'top' ) } + onHoverOn={ createHandleOnHoverOn( 'top' ) } + onHoverOff={ createHandleOnHoverOff( 'top' ) } label={ LABELS.top } /> diff --git a/packages/components/src/box-control/stories/index.js b/packages/components/src/box-control/stories/index.js index 47e8520eeb9fb..60205d435fb94 100644 --- a/packages/components/src/box-control/stories/index.js +++ b/packages/components/src/box-control/stories/index.js @@ -28,6 +28,8 @@ function DemoExample() { left: '10px', } ); + const [ showVisualizer, setShowVisualizer ] = useState( {} ); + return ( @@ -36,13 +38,17 @@ function DemoExample() { label="Padding" values={ values } onChange={ setValues } + onChangeShowVisualizer={ setShowVisualizer } /> - + diff --git a/packages/components/src/box-control/unit-control.js b/packages/components/src/box-control/unit-control.js index d5842da216884..1211452f7d315 100644 --- a/packages/components/src/box-control/unit-control.js +++ b/packages/components/src/box-control/unit-control.js @@ -1,3 +1,9 @@ +/** + * External dependencies + */ +import { noop } from 'lodash'; +import { useHover } from 'react-use-gesture'; + /** * Internal dependencies */ @@ -8,12 +14,22 @@ export default function BoxUnitControl( { isFirst, isLast, isOnly, + onHoverOn = noop, + onHoverOff = noop, label, value, ...props } ) { + const bindHoverGesture = useHover( ( { event, ...state } ) => { + if ( state.hovering ) { + onHoverOn( event, state ); + } else { + onHoverOff( event, state ); + } + } ); + return ( - + ); } -function Sides( { values } ) { +function Sides( { showValues = DEFAULT_VISUALIZER_VALUES, values } ) { const { top, right, bottom, left } = values; return ( <> - - - - + + + + ); } -function Top( { value } ) { +function Top( { isVisible = false, value } ) { const height = value; const animationProps = useSideAnimation( height ); + const isActive = animationProps.isActive || isVisible; - return ; + return ; } -function Right( { value } ) { +function Right( { isVisible = false, value } ) { const width = value; const animationProps = useSideAnimation( width ); + const isActive = animationProps.isActive || isVisible; - return ; + return ; } -function Bottom( { value } ) { +function Bottom( { isVisible = false, value } ) { const height = value; const animationProps = useSideAnimation( height ); + const isActive = animationProps.isActive || isVisible; - return ; + return ; } -function Left( { value } ) { +function Left( { isVisible = false, value } ) { const width = value; const animationProps = useSideAnimation( width ); + const isActive = animationProps.isActive || isVisible; - return ; + return ; } /** From fc7e54af303e8cbfb779db340b4c4b4015a9b57a Mon Sep 17 00:00:00 2001 From: Jon Q Date: Tue, 9 Jun 2020 16:28:09 -0400 Subject: [PATCH 2/4] Update BoxControl README for new onChangeShowVisualizer callback prop --- packages/components/src/box-control/README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/components/src/box-control/README.md b/packages/components/src/box-control/README.md index e4105871042dc..47d398243297f 100644 --- a/packages/components/src/box-control/README.md +++ b/packages/components/src/box-control/README.md @@ -111,7 +111,14 @@ Heading label for BoxControl. ### onChange -A function that receives the values of the inputs. +A callback function when an input value changes. + +- Type: `Function` +- Required: Yes + +### onChangeShowVisualizer + +A callback function for visualizer changes, based on input hover interactions. - Type: `Function` - Required: Yes From 2f43cdb70e34a84e34bcdfa5247f15d0df074bde Mon Sep 17 00:00:00 2001 From: Jon Q Date: Tue, 9 Jun 2020 16:28:28 -0400 Subject: [PATCH 3/4] Add JSDocs for isValuesDefined util --- packages/components/src/box-control/utils.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/components/src/box-control/utils.js b/packages/components/src/box-control/utils.js index 5dcfa643c93ea..ef39000ead642 100644 --- a/packages/components/src/box-control/utils.js +++ b/packages/components/src/box-control/utils.js @@ -88,6 +88,13 @@ export function isValuesMixed( values = {} ) { return isMixed; } +/** + * Checks to determine if values are defined. + * + * @param {Object} values Box values. + * + * @return {boolean} Whether values are mixed. + */ export function isValuesDefined( values = {} ) { return ( values !== undefined && From 05b63db3dcf01be22f7e850277df3f322302d0f1 Mon Sep 17 00:00:00 2001 From: Jon Q Date: Tue, 9 Jun 2020 16:37:24 -0400 Subject: [PATCH 4/4] Add comments for isNumber usage in getAllValue --- packages/components/src/box-control/utils.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/components/src/box-control/utils.js b/packages/components/src/box-control/utils.js index ef39000ead642..63051c3ea0823 100644 --- a/packages/components/src/box-control/utils.js +++ b/packages/components/src/box-control/utils.js @@ -70,6 +70,15 @@ export function getAllValue( values = {} ) { : ''; const unit = mode( allUnits ); + /** + * The isNumber check is important. On reset actions, the incoming value + * may be null or an empty string. + * + * Also, the value may also be zero (0), which is considered a valid unit value. + * + * isNumber() is more specific for these cases, rather than relying on a + * simple truthy check. + */ const allValue = isNumber( value ) ? `${ value }${ unit }` : null; return allValue; @@ -95,7 +104,7 @@ export function isValuesMixed( values = {} ) { * * @return {boolean} Whether values are mixed. */ -export function isValuesDefined( values = {} ) { +export function isValuesDefined( values ) { return ( values !== undefined && ! isEmpty( Object.values( values ).filter( Boolean ) )