diff --git a/packages/block-editor/src/components/block-popover/index.js b/packages/block-editor/src/components/block-popover/index.js
index 2bff678f977b6..ae493fb9cf5b5 100644
--- a/packages/block-editor/src/components/block-popover/index.js
+++ b/packages/block-editor/src/components/block-popover/index.js
@@ -8,6 +8,7 @@ import classnames from 'classnames';
*/
import { Popover } from '@wordpress/components';
import { getScrollContainer } from '@wordpress/dom';
+import { useMemo } from '@wordpress/element';
/**
* Internal dependencies
@@ -19,6 +20,8 @@ export default function BlockPopover( {
clientId,
bottomClientId,
children,
+ __unstableRefreshSize,
+ __unstableCoverTarget = false,
__unstablePopoverSlot,
__unstableContentRef,
...props
@@ -26,6 +29,17 @@ export default function BlockPopover( {
const selectedElement = useBlockElement( clientId );
const lastSelectedElement = useBlockElement( bottomClientId ?? clientId );
const popoverScrollRef = usePopoverScroll( __unstableContentRef );
+ const style = useMemo( () => {
+ if ( ! selectedElement || lastSelectedElement !== selectedElement ) {
+ return {};
+ }
+
+ return {
+ position: 'absolute',
+ width: selectedElement.offsetWidth,
+ height: selectedElement.offsetHeight,
+ };
+ }, [ selectedElement, lastSelectedElement, __unstableRefreshSize ] );
if ( ! selectedElement || ( bottomClientId && ! lastSelectedElement ) ) {
return null;
@@ -50,7 +64,9 @@ export default function BlockPopover( {
position="top right left"
focusOnMount={ false }
anchorRef={ anchorRef }
- __unstableStickyBoundaryElement={ stickyBoundaryElement }
+ __unstableStickyBoundaryElement={
+ __unstableCoverTarget ? undefined : stickyBoundaryElement
+ }
// Render in the old slot if needed for backward compatibility,
// otherwise render in place (not in the the default popover slot).
__unstableSlotName={ __unstablePopoverSlot || null }
@@ -61,13 +77,15 @@ export default function BlockPopover( {
// Used to safeguard sticky position behavior against cases where it would permanently
// obscure specific sections of a block.
__unstableEditorCanvasWrapper={ __unstableContentRef?.current }
+ __unstableForcePosition={ __unstableCoverTarget }
{ ...props }
className={ classnames(
'block-editor-block-popover',
props.className
) }
>
- { children }
+ { __unstableCoverTarget &&
{ children }
}
+ { ! __unstableCoverTarget && children }
);
}
diff --git a/packages/block-editor/src/components/block-popover/style.scss b/packages/block-editor/src/components/block-popover/style.scss
index 86d11ed94007f..c120f05faa2e8 100644
--- a/packages/block-editor/src/components/block-popover/style.scss
+++ b/packages/block-editor/src/components/block-popover/style.scss
@@ -2,6 +2,9 @@
.components-popover.block-editor-block-popover {
z-index: z-index(".block-editor-block-popover");
position: absolute;
+ // Shouldn't be needed but it looks
+ // like the popover is impacted by the block gap margin.
+ margin: 0 !important;
.components-popover__content {
margin: 0 !important;
diff --git a/packages/block-editor/src/hooks/dimensions.js b/packages/block-editor/src/hooks/dimensions.js
index ecfb0fa164c34..9252844d8b576 100644
--- a/packages/block-editor/src/hooks/dimensions.js
+++ b/packages/block-editor/src/hooks/dimensions.js
@@ -19,6 +19,7 @@ import {
} from './gap';
import {
MarginEdit,
+ MarginVisualizer,
hasMarginSupport,
hasMarginValue,
resetMargin,
@@ -26,6 +27,7 @@ import {
} from './margin';
import {
PaddingEdit,
+ PaddingVisualizer,
hasPaddingSupport,
hasPaddingValue,
resetPadding,
@@ -71,44 +73,48 @@ export function DimensionsPanel( props ) {
} );
return (
-
- { ! isPaddingDisabled && (
- hasPaddingValue( props ) }
- label={ __( 'Padding' ) }
- onDeselect={ () => resetPadding( props ) }
- resetAllFilter={ createResetAllFilter( 'padding' ) }
- isShownByDefault={ defaultSpacingControls?.padding }
- panelId={ props.clientId }
- >
-
-
- ) }
- { ! isMarginDisabled && (
- hasMarginValue( props ) }
- label={ __( 'Margin' ) }
- onDeselect={ () => resetMargin( props ) }
- resetAllFilter={ createResetAllFilter( 'margin' ) }
- isShownByDefault={ defaultSpacingControls?.margin }
- panelId={ props.clientId }
- >
-
-
- ) }
- { ! isGapDisabled && (
- hasGapValue( props ) }
- label={ __( 'Block spacing' ) }
- onDeselect={ () => resetGap( props ) }
- resetAllFilter={ createResetAllFilter( 'blockGap' ) }
- isShownByDefault={ defaultSpacingControls?.blockGap }
- panelId={ props.clientId }
- >
-
-
- ) }
-
+ <>
+
+ { ! isPaddingDisabled && (
+ hasPaddingValue( props ) }
+ label={ __( 'Padding' ) }
+ onDeselect={ () => resetPadding( props ) }
+ resetAllFilter={ createResetAllFilter( 'padding' ) }
+ isShownByDefault={ defaultSpacingControls?.padding }
+ panelId={ props.clientId }
+ >
+
+
+ ) }
+ { ! isMarginDisabled && (
+ hasMarginValue( props ) }
+ label={ __( 'Margin' ) }
+ onDeselect={ () => resetMargin( props ) }
+ resetAllFilter={ createResetAllFilter( 'margin' ) }
+ isShownByDefault={ defaultSpacingControls?.margin }
+ panelId={ props.clientId }
+ >
+
+
+ ) }
+ { ! isGapDisabled && (
+ hasGapValue( props ) }
+ label={ __( 'Block spacing' ) }
+ onDeselect={ () => resetGap( props ) }
+ resetAllFilter={ createResetAllFilter( 'blockGap' ) }
+ isShownByDefault={ defaultSpacingControls?.blockGap }
+ panelId={ props.clientId }
+ >
+
+
+ ) }
+
+ { ! isPaddingDisabled && }
+ { ! isMarginDisabled && }
+ >
);
}
diff --git a/packages/block-editor/src/hooks/margin.js b/packages/block-editor/src/hooks/margin.js
index 8ae0a9d7d9083..06d86c85345fc 100644
--- a/packages/block-editor/src/hooks/margin.js
+++ b/packages/block-editor/src/hooks/margin.js
@@ -2,12 +2,19 @@
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
-import { Platform } from '@wordpress/element';
+import {
+ Platform,
+ useMemo,
+ useRef,
+ useState,
+ useEffect,
+} from '@wordpress/element';
import { getBlockSupport } from '@wordpress/blocks';
import {
__experimentalUseCustomUnits as useCustomUnits,
__experimentalBoxControl as BoxControl,
} from '@wordpress/components';
+import isShallowEqual from '@wordpress/is-shallow-equal';
/**
* Internal dependencies
@@ -20,6 +27,7 @@ import {
useIsDimensionsSupportValid,
} from './dimensions';
import { cleanEmptyObject } from './utils';
+import BlockPopover from '../components/block-popover';
/**
* Determines if there is margin support.
@@ -124,26 +132,12 @@ export function MarginEdit( props ) {
} );
};
- const onChangeShowVisualizer = ( next ) => {
- const newStyle = {
- ...style,
- visualizers: {
- margin: next,
- },
- };
-
- setAttributes( {
- style: cleanEmptyObject( newStyle ),
- } );
- };
-
return Platform.select( {
web: (
<>
{
+ return {
+ borderTopWidth: margin?.top ?? 0,
+ borderRightWidth: margin?.right ?? 0,
+ borderBottomWidth: margin?.bottom ?? 0,
+ borderLeftWidth: margin?.left ?? 0,
+ top: margin?.top ? `-${ margin.top }` : 0,
+ right: margin?.right ? `-${ margin.right }` : 0,
+ bottom: margin?.bottom ? `-${ margin.bottom }` : 0,
+ left: margin?.left ? `-${ margin.left }` : 0,
+ };
+ }, [ margin ] );
+
+ const [ isActive, setIsActive ] = useState( false );
+ const valueRef = useRef( margin );
+ const timeoutRef = useRef();
+
+ const clearTimer = () => {
+ if ( timeoutRef.current ) {
+ window.clearTimeout( timeoutRef.current );
+ }
+ };
+
+ useEffect( () => {
+ if ( ! isShallowEqual( margin, valueRef.current ) ) {
+ setIsActive( true );
+ valueRef.current = margin;
+
+ clearTimer();
+
+ timeoutRef.current = setTimeout( () => {
+ setIsActive( false );
+ }, 400 );
+ }
+
+ return () => clearTimer();
+ }, [ margin ] );
+
+ if ( ! isActive ) {
+ return null;
+ }
+
+ return (
+
+
+
+ );
+}
diff --git a/packages/block-editor/src/hooks/padding.js b/packages/block-editor/src/hooks/padding.js
index f01276baefdf1..02a371db0d6a3 100644
--- a/packages/block-editor/src/hooks/padding.js
+++ b/packages/block-editor/src/hooks/padding.js
@@ -2,12 +2,19 @@
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
-import { Platform } from '@wordpress/element';
+import {
+ Platform,
+ useState,
+ useRef,
+ useEffect,
+ useMemo,
+} from '@wordpress/element';
import { getBlockSupport } from '@wordpress/blocks';
import {
__experimentalUseCustomUnits as useCustomUnits,
__experimentalBoxControl as BoxControl,
} from '@wordpress/components';
+import isShallowEqual from '@wordpress/is-shallow-equal';
/**
* Internal dependencies
@@ -20,6 +27,7 @@ import {
useIsDimensionsSupportValid,
} from './dimensions';
import { cleanEmptyObject } from './utils';
+import BlockPopover from '../components/block-popover';
/**
* Determines if there is padding support.
@@ -124,26 +132,12 @@ export function PaddingEdit( props ) {
} );
};
- const onChangeShowVisualizer = ( next ) => {
- const newStyle = {
- ...style,
- visualizers: {
- padding: next,
- },
- };
-
- setAttributes( {
- style: cleanEmptyObject( newStyle ),
- } );
- };
-
return Platform.select( {
web: (
<>
{
+ return {
+ borderTopWidth: padding?.top ?? 0,
+ borderRightWidth: padding?.right ?? 0,
+ borderBottomWidth: padding?.bottom ?? 0,
+ borderLeftWidth: padding?.left ?? 0,
+ };
+ }, [ padding ] );
+
+ const [ isActive, setIsActive ] = useState( false );
+ const valueRef = useRef( padding );
+ const timeoutRef = useRef();
+
+ const clearTimer = () => {
+ if ( timeoutRef.current ) {
+ window.clearTimeout( timeoutRef.current );
+ }
+ };
+
+ useEffect( () => {
+ if ( ! isShallowEqual( padding, valueRef.current ) ) {
+ setIsActive( true );
+ valueRef.current = padding;
+
+ clearTimer();
+
+ timeoutRef.current = setTimeout( () => {
+ setIsActive( false );
+ }, 400 );
+ }
+
+ return () => clearTimer();
+ }, [ padding ] );
+
+ if ( ! isActive ) {
+ return null;
+ }
+
+ return (
+
+
+
+ );
+}
diff --git a/packages/block-editor/src/hooks/padding.scss b/packages/block-editor/src/hooks/padding.scss
new file mode 100644
index 0000000000000..dbb9991988189
--- /dev/null
+++ b/packages/block-editor/src/hooks/padding.scss
@@ -0,0 +1,12 @@
+.block-editor__padding-visualizer {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ opacity: 0.5;
+ border-color: var(--wp-admin-theme-color);
+ border-style: solid;
+ pointer-events: none;
+ box-sizing: border-box;
+}
diff --git a/packages/block-editor/src/style.scss b/packages/block-editor/src/style.scss
index b2228762f12db..cd7e64118ff44 100644
--- a/packages/block-editor/src/style.scss
+++ b/packages/block-editor/src/style.scss
@@ -59,6 +59,7 @@
@import "./hooks/dimensions.scss";
@import "./hooks/typography.scss";
@import "./hooks/color.scss";
+@import "./hooks/padding.scss";
@import "./components/block-toolbar/style.scss";
@import "./components/inserter/style.scss";
diff --git a/packages/block-library/src/cover/edit.js b/packages/block-library/src/cover/edit.js
index 45a090768f6a6..7481bf4640769 100644
--- a/packages/block-library/src/cover/edit.js
+++ b/packages/block-library/src/cover/edit.js
@@ -31,7 +31,6 @@ import {
ToggleControl,
ToolbarButton,
__experimentalUseCustomUnits as useCustomUnits,
- __experimentalBoxControl as BoxControl,
__experimentalToolsPanelItem as ToolsPanelItem,
__experimentalUnitControl as UnitControl,
__experimentalParseQuantityAndUnitFromRawValue as parseQuantityAndUnitFromRawValue,
@@ -77,8 +76,6 @@ import {
extend( [ namesPlugin ] );
-const { __Visualizer: BoxControlVisualizer } = BoxControl;
-
function getInnerBlocksTemplate( attributes ) {
return [
[
@@ -313,7 +310,6 @@ function CoverEdit( {
isRepeated,
minHeight,
minHeightUnit,
- style: styleAttribute,
alt,
allowedBlocks,
templateLock,
@@ -744,11 +740,6 @@ function CoverEdit( {
style={ { ...style, ...blockProps.style } }
data-url={ url }
>
-
{
diff --git a/packages/block-library/src/cover/style.scss b/packages/block-library/src/cover/style.scss
index db543cb731e5c..a9328ff17e52b 100644
--- a/packages/block-library/src/cover/style.scss
+++ b/packages/block-library/src/cover/style.scss
@@ -96,10 +96,6 @@
}
}
- .block-library-cover__padding-visualizer {
- z-index: z-index(".block-library-cover__padding-visualizer");
- }
-
// Apply max-width to floated items that have no intrinsic width
&.alignleft,
&.alignright {
diff --git a/packages/components/src/box-control/README.md b/packages/components/src/box-control/README.md
index 328bfb78c6772..8a704482cacd1 100644
--- a/packages/components/src/box-control/README.md
+++ b/packages/components/src/box-control/README.md
@@ -29,73 +29,6 @@ const Example = () => {
};
```
-### Visualizer
-
-BoxControl provides a companion component that visually renders value changes. Place the component you would like the sides visualized within the companion `` component.
-
-```jsx
-import { __experimentalBoxControl as BoxControl } from '@wordpress/components';
-import { useState } from '@wordpress/element';
-
-import MyComponent from './my-component';
-
-const { Visualizer } = BoxControl;
-
-const Example = () => {
- const [ values, setValues ] = useState( {
- top: '50px',
- left: '10%',
- right: '10%',
- bottom: '50px',
- } );
-
- return (
- <>
- setValues( nextValues ) }
- />
-
-
-
- >
- );
-};
-```
-
-Alternatively, the `` can be nested as a sibling to the component you would like visualized. Using `` in this manner will require the parent element having a `position` style.
-
-```jsx
-import { __experimentalBoxControl as BoxControl } from '@wordpress/components';
-import { useState } from '@wordpress/element';
-
-import MyComponent from './my-component';
-
-const { Visualizer } = BoxControl;
-
-const Example = () => {
- const [ values, setValues ] = useState( {
- top: '50px',
- left: '10%',
- right: '10%',
- bottom: '50px',
- } );
-
- return (
- <>
- setValues( nextValues ) }
- />
-
-
-
-
- >
- );
-};
-```
-
## Props
### allowReset
@@ -135,13 +68,6 @@ 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
-
### resetValues
The `top`, `right`, `bottom`, and `left` box dimension values to use when the control is reset.
diff --git a/packages/components/src/box-control/index.js b/packages/components/src/box-control/index.js
index 54377010e5df2..cbd11e1995435 100644
--- a/packages/components/src/box-control/index.js
+++ b/packages/components/src/box-control/index.js
@@ -21,7 +21,6 @@ import AxialInputControls from './axial-input-controls';
import BoxControlIcon from './icon';
import { Text } from '../text';
import LinkedButton from './linked-button';
-import Visualizer from './visualizer';
import {
Root,
Header,
@@ -30,7 +29,6 @@ import {
import { parseQuantityAndUnitFromRawValue } from '../unit-control/utils';
import {
DEFAULT_VALUES,
- DEFAULT_VISUALIZER_VALUES,
getInitialSide,
isValuesMixed,
isValuesDefined,
@@ -50,7 +48,6 @@ export default function BoxControl( {
id: idProp,
inputProps = defaultInputProps,
onChange = noop,
- onChangeShowVisualizer = noop,
label = __( 'Box Control' ),
values: valuesProp,
units,
@@ -103,14 +100,6 @@ export default function BoxControl( {
setIsDirty( true );
};
- const handleOnHoverOn = ( next = {} ) => {
- onChangeShowVisualizer( { ...DEFAULT_VISUALIZER_VALUES, ...next } );
- };
-
- const handleOnHoverOff = ( next = {} ) => {
- onChangeShowVisualizer( { ...DEFAULT_VISUALIZER_VALUES, ...next } );
- };
-
const handleOnReset = () => {
onChange( resetValues );
setValues( resetValues );
@@ -122,8 +111,6 @@ export default function BoxControl( {
...inputProps,
onChange: handleOnChange,
onFocus: handleOnFocus,
- onHoverOn: handleOnHoverOn,
- onHoverOff: handleOnHoverOff,
isLinked,
units,
selectedUnits,
@@ -189,5 +176,3 @@ export default function BoxControl( {
);
}
-
-BoxControl.__Visualizer = Visualizer;
diff --git a/packages/components/src/box-control/stories/index.js b/packages/components/src/box-control/stories/index.js
index c05263adefb79..f0bab8e21481d 100644
--- a/packages/components/src/box-control/stories/index.js
+++ b/packages/components/src/box-control/stories/index.js
@@ -11,7 +11,6 @@ import { useState } from '@wordpress/element';
* Internal dependencies
*/
import BoxControl from '../';
-import BoxControlVisualizer from '../visualizer';
import { Flex, FlexBlock } from '../../flex';
export default {
@@ -36,7 +35,6 @@ function DemoExample( {
splitOnAxis = false,
} ) {
const [ values, setValues ] = useState( defaultValues );
- const [ showVisualizer, setShowVisualizer ] = useState( {} );
return (
@@ -47,31 +45,14 @@ function DemoExample( {
values={ values }
sides={ sides }
onChange={ setValues }
- onChangeShowVisualizer={ setShowVisualizer }
splitOnAxis={ splitOnAxis }
/>
-
-
-
-
-
-
-
-
-
);
}
-export const visualizer = () => {
- return ;
-};
-
export const arbitrarySides = () => {
return (
-
- { children }
-
- );
-}
-
-function Sides( { showValues = DEFAULT_VISUALIZER_VALUES, values } ) {
- const { top, right, bottom, left } = values;
-
- return (
- <>
-
-
-
-
- >
- );
-}
-
-function Top( { isVisible = false, value } ) {
- const height = value;
- const animationProps = useSideAnimation( height );
- const isActive = animationProps.isActive || isVisible;
-
- return ;
-}
-
-function Right( { isVisible = false, value } ) {
- const width = value;
- const animationProps = useSideAnimation( width );
- const isActive = animationProps.isActive || isVisible;
-
- return ;
-}
-
-function Bottom( { isVisible = false, value } ) {
- const height = value;
- const animationProps = useSideAnimation( height );
- const isActive = animationProps.isActive || isVisible;
-
- return ;
-}
-
-function Left( { isVisible = false, value } ) {
- const width = value;
- const animationProps = useSideAnimation( width );
- const isActive = animationProps.isActive || isVisible;
-
- return ;
-}
-
-/**
- * Custom hook that renders the "flash" animation whenever the value changes.
- *
- * @param {string} value Value of (box) side.
- */
-function useSideAnimation( value ) {
- const [ isActive, setIsActive ] = useState( false );
- const valueRef = useRef( value );
- const timeoutRef = useRef();
-
- const clearTimer = () => {
- if ( timeoutRef.current ) {
- window.clearTimeout( timeoutRef.current );
- }
- };
-
- useEffect( () => {
- if ( value !== valueRef.current ) {
- setIsActive( true );
- valueRef.current = value;
-
- clearTimer();
-
- timeoutRef.current = setTimeout( () => {
- setIsActive( false );
- }, 400 );
- }
-
- return () => clearTimer();
- }, [ value ] );
-
- return {
- isActive,
- };
-}