From a00b7a44859facca77aecf7322ed9a9b4e233571 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Tue, 18 Oct 2022 10:01:38 +0800 Subject: [PATCH 01/20] Add initial visualizer popover, sized to the full-width Show alignment steps Tidy up and add labels Use a resize observer to handle resizes Set a max width to avoid visualizer step appearing beyond screen edges Only show visualizer while resizing Use availableAlignments instead of hard coded array Refactor away from block popover Adjust sizing to parent/root block list Move into separate component file Move more code into effect for optimization Remove export Hack together a version that dynamically uses the parent block list layout Bring back proper styles Fix popover positioning Tidy up css and naming - Rename step to zone - Use the unstyled variant Use text color as the contrast color Allow popover to render inline by overriding slot Show nearest alignment zone label - Register zones into a slot when they mount - use `getDistanceToNearestEdge` to detect nearest zone to mouse pointer - Make Popover render behind the image and receive mouse events - Add CSS to only show highlighted zone Show nearest zone to mouse by hooking into resizeable box events Allow popover to render inline Add unstyled variant to wrapping popover Rename props Rename prop Take padding and margin of layout into account Lessen opacity of background Rename from blockList to layout Reduce need for margin/padding on container Throttle zone detection Ensure popovers that reference iframe are positioned correctly by not applying padding to iframe Fix zone highlighting when layout has padding Remove spacing variable Remove unused dep Add a basic fade-in animation Animate label Update label style Refactor - Move ResizableImageControls to block editor package and rename to ResizableAlignmentControls - Export ResizableAlignmentControls and remove export for BlockAlignmentVisualizer - Move hardcoded alignments to image block Use context for zones and switch to a map instead of a set for storing zone data Add basic snapping Fix incorrect prop name Move nearest zone detection to event handler Update re-resizeable Detect snapping Document code Speed up iframe rendering Avoid removing iframe head and body and reattaching them Make iframe title a prop Use custom snapping algorithm over resizable box algorithm Reintroduce basic snapping while dragging Set alignment on release of mouse Allow snapping from left/right alignments Fix snapping to content width for images that are larger than content width Increase snap threshold Refactor content width snapping fix to image block code Try allowing resize handles on wide and full aligned images Revert "Try allowing resize handles on wide and full aligned images" This reverts commit f8402c2c0269532cf4b0363c04145f302a742b6b. Revert "Update re-resizeable" This reverts commit 7d7e2316dc02fd0d6ef494b10df5dfe238e3695a. Add some really dodgy animation code Revert "Add some really dodgy animation code" This reverts commit a60d775204ca3edab15bd6e7554d77a8d3c633ff. Use a framer motion layout animation for snapping Refactor and rename things Remove unused resize observer Refactor - separate popover code from layout code Fix padding being applied to visualizer incorrectly Move styles to the content.scss Remove border from iframe Switch to using the shadow dom instead of an iframe to encapsulate styles Speed up animation Simplify guide styles Remove offset naming Adjust layout popopover to use correct element Try fixing layout block detection Remove non-existant stylesheet import Fix iframe offsetting Remove buggy padding on cover element Use a consistent value for no snapping Use package-lock from trunk Start implementation of new design Hide old guides Support content justifications for new visualization Simplify guides code, remove use of LayoutStyles Move snapping code into a hook Move getOffsetRect to dom package and rename Try: hide full and wide alignments at smaller container sizes Fix snapping Fix - show/hide block interface actions not usable outside dimensions panel Hide block toolbar when resizing Hide block border when using alignment visualizer --- .../guide-context.js | 90 ++++++++ .../block-alignment-visualizer/guides.js | 113 ++++++++++ .../block-alignment-visualizer/index.js | 80 +++++++ .../layout-popover.js | 124 +++++++++++ .../shadow-dom-container.js | 19 ++ .../block-alignment-visualizer/style.scss | 28 +++ .../visualization.js | 146 +++++++++++++ .../src/components/block-list/content.scss | 2 +- .../use-block-props/use-block-class-names.js | 6 +- packages/block-editor/src/components/index.js | 1 + .../resizable-alignment-controls/index.js | 205 ++++++++++++++++++ packages/block-editor/src/content.scss | 1 + packages/block-editor/src/hooks/dimensions.js | 11 +- packages/block-library/src/image/image.js | 94 ++++---- packages/components/src/popover/index.tsx | 13 +- packages/components/src/popover/types.ts | 6 + packages/dom/README.md | 14 ++ packages/dom/src/dom/get-screen-rect.js | 32 +++ packages/dom/src/dom/index.js | 1 + 19 files changed, 925 insertions(+), 61 deletions(-) create mode 100644 packages/block-editor/src/components/block-alignment-visualizer/guide-context.js create mode 100644 packages/block-editor/src/components/block-alignment-visualizer/guides.js create mode 100644 packages/block-editor/src/components/block-alignment-visualizer/index.js create mode 100644 packages/block-editor/src/components/block-alignment-visualizer/layout-popover.js create mode 100644 packages/block-editor/src/components/block-alignment-visualizer/shadow-dom-container.js create mode 100644 packages/block-editor/src/components/block-alignment-visualizer/style.scss create mode 100644 packages/block-editor/src/components/block-alignment-visualizer/visualization.js create mode 100644 packages/block-editor/src/components/resizable-alignment-controls/index.js create mode 100644 packages/dom/src/dom/get-screen-rect.js diff --git a/packages/block-editor/src/components/block-alignment-visualizer/guide-context.js b/packages/block-editor/src/components/block-alignment-visualizer/guide-context.js new file mode 100644 index 0000000000000..c9550f93eccde --- /dev/null +++ b/packages/block-editor/src/components/block-alignment-visualizer/guide-context.js @@ -0,0 +1,90 @@ +/** + * WordPress dependencies + */ +import { useThrottle } from '@wordpress/compose'; +import { getScreenRect } from '@wordpress/dom'; +import { createContext, useContext, useRef } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { getDistanceFromPointToEdge } from '../../utils/math'; + +const BlockAlignmentGuideContext = createContext( new Map() ); +export const useBlockAlignmentGuides = () => + useContext( BlockAlignmentGuideContext ); + +export function BlockAlignmentGuideContextProvider( { children } ) { + const guides = useRef( new Map() ); + + return ( + + { children } + + ); +} + +/** + * Detect whether the `element` is snapping to one of the alignment guide along its `snapEdge`. + * + * @param {Node} element The element to check for snapping. + * @param {'left'|'right'} snapEdge The edge that will snap. + * @param {Map} alignmentGuides A Map of alignment guide nodes. + * @param {number} snapGap The pixel threshold for snapping. + * + * @return {null|'none'|'wide'|'full'} The alignment guide or `null` if no snapping was detected. + */ +function detectSnapping( element, snapEdge, alignmentGuides, snapGap ) { + const elementRect = getScreenRect( element ); + + // Get a point on the resizable rect's edge for `getDistanceFromPointToEdge`. + // - Caveat: this assumes horizontal resizing. + const pointFromElementRect = { + x: elementRect[ snapEdge ], + y: elementRect.top, + }; + + let candidateGuide = null; + + // Loop through alignment guide nodes. + alignmentGuides?.forEach( ( guide, name ) => { + const guideRect = getScreenRect( guide ); + + // Calculate the distance from the resizeable element's edge to the + // alignment zone's edge. + const distance = getDistanceFromPointToEdge( + pointFromElementRect, + guideRect, + snapEdge + ); + + // If the distance is within snapping tolerance, we are snapping to this alignment. + if ( distance < snapGap ) { + candidateGuide = name; + } + } ); + + return candidateGuide; +} + +export function useDetectSnapping( snapGap ) { + const alignmentGuides = useBlockAlignmentGuides(); + return useThrottle( ( element, snapEdge ) => { + const snappedAlignment = detectSnapping( + element, + snapEdge, + alignmentGuides, + snapGap + ); + const guide = alignmentGuides.get( snappedAlignment ); + + if ( ! guide ) { + return null; + } + + return { + name: snappedAlignment, + rect: getScreenRect( guide ), + }; + }, 100 ); +} diff --git a/packages/block-editor/src/components/block-alignment-visualizer/guides.js b/packages/block-editor/src/components/block-alignment-visualizer/guides.js new file mode 100644 index 0000000000000..3744e1a3fa6f7 --- /dev/null +++ b/packages/block-editor/src/components/block-alignment-visualizer/guides.js @@ -0,0 +1,113 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +/** + * WordPress dependencies + */ +import { useRefEffect } from '@wordpress/compose'; + +/** + * Internal dependencies + */ +import { useBlockAlignmentGuides } from './guide-context'; + +/** + * Renders hidden guide elements that are used for calculating snapping. + * Each guide has the same rect as a block would at the given alignment. + * + * @param {Object} props + * @param {string} props.contentSize The CSS value for content size (e.g. 600px). + * @param {string} props.wideSize The CSS value for wide size (e.g. 80%). + * @param {'none'|'wide'|'full'[]} props.alignments An array of the alignments to render. + * @param {'left'|'right'|'center'} props.justification The justification. + */ +export default function Guides( { + contentSize, + wideSize, + alignments, + justification, +} ) { + return ( + <> + +
+ { alignments.map( ( { name } ) => ( + + ) ) } +
+ + ); +} + +function Guide( { alignment } ) { + const guides = useBlockAlignmentGuides(); + const updateGuideContext = useRefEffect( + ( node ) => { + guides?.set( alignment, node ); + return () => { + guides?.delete( alignment ); + }; + }, + [ alignment ] + ); + + return ( +
+ ); +} diff --git a/packages/block-editor/src/components/block-alignment-visualizer/index.js b/packages/block-editor/src/components/block-alignment-visualizer/index.js new file mode 100644 index 0000000000000..6b81a90534af8 --- /dev/null +++ b/packages/block-editor/src/components/block-alignment-visualizer/index.js @@ -0,0 +1,80 @@ +/** + * WordPress dependencies + */ +import { getBlockSupport, hasBlockSupport } from '@wordpress/blocks'; +import { useSelect } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import ShadowDOMContainer from './shadow-dom-container'; +import LayoutPopover from './layout-popover'; +import { useLayout } from '../block-list/layout'; +import useAvailableAlignments from '../block-alignment-control/use-available-alignments'; +import { store as blockEditorStore } from '../../store'; +import { getValidAlignments } from '../../hooks/align'; +import Visualization from './visualization'; +import Guides from './guides'; + +/** + * A component that displays block alignment guidelines. + * + * @param {Object} props + * @param {?string[]} props.allowedAlignments An optional array of alignments names. By default, the alignment support will be derived from the + * 'focused' block's block supports, but some blocks (image) have an ad-hoc alignment implementation. + * @param {string|null} props.layoutClientId The client id of the block that provides the layout. + * @param {string} props.focusedClientId The client id of the block to show the alignment guides for. + * @param {?string} props.highlightedAlignment The alignment name to show the label of. + */ +export default function BlockAlignmentVisualizer( { + allowedAlignments, + layoutClientId, + focusedClientId, + highlightedAlignment, +} ) { + const focusedBlockName = useSelect( + ( select ) => + select( blockEditorStore ).getBlockName( focusedClientId ), + [ focusedClientId ] + ); + + // Get the valid alignments of the focused block, or use the supplied `allowedAlignments`, + // which allows this to work for blocks like 'image' that don't use block supports. + const validAlignments = + allowedAlignments ?? + getValidAlignments( + getBlockSupport( focusedBlockName, 'align' ), + hasBlockSupport( focusedBlockName, 'alignWide', true ) + ); + const availableAlignments = useAvailableAlignments( validAlignments ); + const layout = useLayout(); + + if ( availableAlignments?.length === 0 ) { + return null; + } + + return ( + + + + + + + ); +} diff --git a/packages/block-editor/src/components/block-alignment-visualizer/layout-popover.js b/packages/block-editor/src/components/block-alignment-visualizer/layout-popover.js new file mode 100644 index 0000000000000..a485676bd40dc --- /dev/null +++ b/packages/block-editor/src/components/block-alignment-visualizer/layout-popover.js @@ -0,0 +1,124 @@ +/** + * WordPress dependencies + */ +import { Popover } from '@wordpress/components'; +import { useContext, useEffect, useState } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { BlockList } from '../'; +import { __unstableUseBlockElement as useBlockElement } from '../block-list/use-block-props/use-block-refs'; + +function getComputedCSS( element, property ) { + return element.ownerDocument.defaultView + .getComputedStyle( element ) + .getPropertyValue( property ); +} + +export default function LayoutPopover( { + className, + coverClassName, + layoutClientId, + focusedClientId, + isConstrained, + children, +} ) { + const [ popoverAnchor, setPopoverAnchor ] = useState( null ); + const [ coverElementStyle, setCoverElementStyle ] = useState( null ); + const focusedBlockElement = useBlockElement( focusedClientId ); + const layoutBlockElement = useBlockElement( layoutClientId ); + + // useBlockElement is unable to return the document's root block list. + // __unstableElementContext seems to provide this. + const rootBlockListElement = useContext( + BlockList.__unstableElementContext + ); + + useEffect( () => { + const resolvedLayoutElement = + isConstrained && layoutBlockElement + ? layoutBlockElement + : rootBlockListElement; + if ( ! focusedBlockElement || ! resolvedLayoutElement ) { + return; + } + + const { ownerDocument } = focusedBlockElement; + const { defaultView } = ownerDocument; + + const update = () => { + // The popover is positioned to the top of the block list that provides the layout + // and left of the 'focused' block. + setPopoverAnchor( { + ownerDocument, + getBoundingClientRect() { + const layoutRect = + resolvedLayoutElement.getBoundingClientRect(); + const focusedBlockRect = + focusedBlockElement.getBoundingClientRect(); + + return new defaultView.DOMRect( + layoutRect.x, + focusedBlockRect.y, + layoutRect.width, + focusedBlockRect.height + ); + }, + } ); + + // The cover element is an inner element within the popover. It has the width of the layout + // and height of the focused block, and also matches any padding of the layout. + // const paddingLeft = getComputedCSS( + // resolvedLayoutElement, + // 'padding-left' + // ); + // const paddingRight = getComputedCSS( + // resolvedLayoutElement, + // 'padding-right' + // ); + setCoverElementStyle( { + position: 'absolute', + width: resolvedLayoutElement.offsetWidth, + height: focusedBlockElement.offsetHeight, + // paddingLeft, + // paddingRight, + } ); + }; + + // Observe any resizes of both the layout and focused elements. + const resizeObserver = defaultView.ResizeObserver + ? new defaultView.ResizeObserver( update ) + : undefined; + resizeObserver?.observe( resolvedLayoutElement ); + resizeObserver?.observe( focusedBlockElement ); + update(); + + return () => { + resizeObserver?.disconnect(); + }; + }, [ + focusedBlockElement, + layoutBlockElement, + rootBlockListElement, + isConstrained, + ] ); + + return ( + +
+ { children } +
+
+ ); +} diff --git a/packages/block-editor/src/components/block-alignment-visualizer/shadow-dom-container.js b/packages/block-editor/src/components/block-alignment-visualizer/shadow-dom-container.js new file mode 100644 index 0000000000000..2e37497dbfd4e --- /dev/null +++ b/packages/block-editor/src/components/block-alignment-visualizer/shadow-dom-container.js @@ -0,0 +1,19 @@ +/** + * WordPress dependencies + */ +import { useRefEffect } from '@wordpress/compose'; +import { createPortal, useState } from '@wordpress/element'; + +export default function ShadowDOMContainer( { children } ) { + const [ shadowRoot, setShadowRoot ] = useState( null ); + const ref = useRefEffect( ( node ) => { + setShadowRoot( node.attachShadow( { mode: 'open' } ) ); + return () => setShadowRoot( null ); + }, [] ); + + return ( +
+ { shadowRoot && createPortal( children, shadowRoot ) } +
+ ); +} diff --git a/packages/block-editor/src/components/block-alignment-visualizer/style.scss b/packages/block-editor/src/components/block-alignment-visualizer/style.scss new file mode 100644 index 0000000000000..594a88b8cfaf1 --- /dev/null +++ b/packages/block-editor/src/components/block-alignment-visualizer/style.scss @@ -0,0 +1,28 @@ + +.block-editor-alignment-visualizer { + z-index: 0; +} +.block-editor-alignment-visualizer__cover-element { + z-index: 0; + + &::before { + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + background-color: var(--contrast-color); + opacity: 0.05; + + } +} + +.block-editor__alignment-visualizer-guide__label { + // Values match capitalized label style. + font-size: 11px; + font-weight: 500; + text-transform: uppercase; + white-space: nowrap; + margin: $grid-unit-05 0; +} diff --git a/packages/block-editor/src/components/block-alignment-visualizer/visualization.js b/packages/block-editor/src/components/block-alignment-visualizer/visualization.js new file mode 100644 index 0000000000000..d2c4ad9af61d2 --- /dev/null +++ b/packages/block-editor/src/components/block-alignment-visualizer/visualization.js @@ -0,0 +1,146 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +/** + * Renders a visualization of block alignments. + * + * @param {Object} props + * @param {string} props.contentSize The CSS value for content size (e.g. 600px). + * @param {string} props.wideSize The CSS value for wide size (e.g. 80%). + * @param {'none'|'wide'|'full'[]} props.alignments An array of the alignments to render. + * @param {'left'|'right'|'center'} props.justification The justification. + */ +export default function Visualization( { + contentSize, + wideSize, + alignments, + justification, +} ) { + return ( + <> + +
+
+ { [ ...alignments ] + .reverse() + .map( + ( { name } ) => + ( name === 'full' || name === 'wide' ) && ( + + ) + ) } + + { alignments.map( + ( { name } ) => + ( name === 'full' || name === 'wide' ) && ( + + ) + ) } +
+
+ + ); +} + +function VisualizationSegment( { side, alignment } ) { + return ( +
+ ); +} diff --git a/packages/block-editor/src/components/block-list/content.scss b/packages/block-editor/src/components/block-list/content.scss index 268301e598d5a..c6648061dcbf8 100644 --- a/packages/block-editor/src/components/block-list/content.scss +++ b/packages/block-editor/src/components/block-list/content.scss @@ -87,7 +87,7 @@ .block-editor-block-list__block.is-highlighted ~ .is-multi-selected, &.is-navigate-mode .block-editor-block-list__block.is-selected, & .is-block-moving-mode.block-editor-block-list__block.has-child-selected, - .block-editor-block-list__block:not([contenteditable]):focus { + .block-editor-block-list__block:not([contenteditable]):not(.hide-block-border):focus { { outline: none; // We're using a pseudo element to overflow placeholder borders diff --git a/packages/block-editor/src/components/block-list/use-block-props/use-block-class-names.js b/packages/block-editor/src/components/block-list/use-block-props/use-block-class-names.js index fce94b85f9119..c960bd2187602 100644 --- a/packages/block-editor/src/components/block-list/use-block-props/use-block-class-names.js +++ b/packages/block-editor/src/components/block-list/use-block-props/use-block-class-names.js @@ -13,6 +13,7 @@ import { isReusableBlock, getBlockType } from '@wordpress/blocks'; * Internal dependencies */ import { store as blockEditorStore } from '../../../store'; +import { unlock } from '../../../lock-unlock'; /** * Returns the class names used for the different states of the block. @@ -26,6 +27,7 @@ export function useBlockClassNames( clientId ) { ( select ) => { const { isBlockBeingDragged, + isBlockInterfaceHidden, isBlockHighlighted, isBlockSelected, isBlockMultiSelected, @@ -35,7 +37,7 @@ export function useBlockClassNames( clientId ) { isTyping, __unstableIsFullySelected, __unstableSelectionHasUnmergeableBlock, - } = select( blockEditorStore ); + } = unlock( select( blockEditorStore ) ); const { outlineMode } = getSettings(); const isDragging = isBlockBeingDragged( clientId ); const isSelected = isBlockSelected( clientId ); @@ -47,6 +49,7 @@ export function useBlockClassNames( clientId ) { checkDeep ); const isMultiSelected = isBlockMultiSelected( clientId ); + return classnames( { 'is-selected': isSelected, 'is-highlighted': isBlockHighlighted( clientId ), @@ -59,6 +62,7 @@ export function useBlockClassNames( clientId ) { 'is-dragging': isDragging, 'has-child-selected': isAncestorOfSelectedBlock, 'remove-outline': isSelected && outlineMode && isTyping(), + 'hide-block-border': isBlockInterfaceHidden(), } ); }, [ clientId ] diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js index 66830c4e57aaf..e5ba38bb153a1 100644 --- a/packages/block-editor/src/components/index.js +++ b/packages/block-editor/src/components/index.js @@ -77,6 +77,7 @@ export { default as MediaUpload } from './media-upload'; export { default as MediaUploadCheck } from './media-upload/check'; export { default as PanelColorSettings } from './panel-color-settings'; export { default as PlainText } from './plain-text'; +export { default as __experimentalResizableAlignmentControls } from './resizable-alignment-controls'; export { default as __experimentalResponsiveBlockControl } from './responsive-block-control'; export { default as RichText, diff --git a/packages/block-editor/src/components/resizable-alignment-controls/index.js b/packages/block-editor/src/components/resizable-alignment-controls/index.js new file mode 100644 index 0000000000000..a2582a6dd7095 --- /dev/null +++ b/packages/block-editor/src/components/resizable-alignment-controls/index.js @@ -0,0 +1,205 @@ +/** + * WordPress dependencies + */ +import { + ResizableBox, + __unstableAnimatePresence as AnimatePresence, + __unstableMotion as motion, +} from '@wordpress/components'; +import { useDispatch, useSelect } from '@wordpress/data'; +import { getScreenRect } from '@wordpress/dom'; +import { useMemo, useRef, useState } from '@wordpress/element'; +import { isRTL } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import BlockAlignmentVisualizer from '../block-alignment-visualizer'; +import { + BlockAlignmentGuideContextProvider, + useDetectSnapping, +} from '../block-alignment-visualizer/guide-context'; +import { store as blockEditorStore } from '../../store'; +import { unlock } from '../../lock-unlock'; + +const SNAP_GAP = 30; + +function getVisibleHandles( alignment ) { + if ( alignment === 'center' ) { + // When the image is centered, show both handles. + return { right: true, left: true, bottom: true, top: false }; + } + + if ( isRTL() ) { + // In RTL mode the image is on the right by default. + // Show the right handle and hide the left handle only when it is + // aligned left. Otherwise always show the left handle. + if ( alignment === 'left' ) { + return { right: true, left: false, bottom: true, top: false }; + } + return { left: true, right: false, bottom: true, top: false }; + } + + // Show the left handle and hide the right handle only when the + // image is aligned right. Otherwise always show the right handle. + if ( alignment === 'right' ) { + return { left: true, right: false, bottom: true, top: false }; + } + return { right: true, left: false, bottom: true, top: false }; +} + +/** + * A component that composes together the `ResizebleBox` and `BlockAlignmentVisualizer` + * and configures snapping to block alignments. + * + * @param {Object} props + * @param {?string[]} props.allowedAlignments An optional array of allowed alignments. If not provided this will be inferred from the block supports. + * @param {import('react').ReactElement} props.children Children of the ResizableBox. + * @param {string} props.clientId The clientId of the block + * @param {?string} props.currentAlignment The current alignment name. Defaults to 'none'. + * @param {number} props.minWidth Minimum width of the resizable box. + * @param {number} props.maxWidth Maximum width of the resizable box. + * @param {number} props.minHeight Minimum height of the resizable box. + * @param {number} props.maxHeight Maximum height of the resizable box. + * @param {Function} props.onResizeStart An event handler called when resizing starts. + * @param {Function} props.onResizeStop An event handler called when resizing stops. + * @param {Function} props.onSnap Function called when alignment is set. + * @param {boolean} props.showHandle Whether to show the drag handle. + * @param {Object} props.size The current dimensions. + */ +function ResizableAlignmentControls( { + allowedAlignments, + children, + clientId, + currentAlignment = 'none', + minWidth, + maxWidth, + minHeight, + maxHeight, + onResizeStart, + onResizeStop, + onSnap, + showHandle, + size, +} ) { + const resizableRef = useRef(); + const detectSnapping = useDetectSnapping( SNAP_GAP ); + const [ isAlignmentVisualizerVisible, setIsAlignmentVisualizerVisible ] = + useState( false ); + const [ snappedAlignment, setSnappedAlignment ] = useState( null ); + const { hideBlockInterface, showBlockInterface } = unlock( + useDispatch( blockEditorStore ) + ); + + const rootClientId = useSelect( + ( select ) => + select( blockEditorStore ).getBlockRootClientId( clientId ), + [ clientId ] + ); + + const contentStyle = useMemo( () => { + if ( ! snappedAlignment ) { + // By default the content takes up the full width of the resizable box. + return { width: '100%' }; + } + + const contentRect = getScreenRect( resizableRef.current ); + const alignmentRect = snappedAlignment.rect; + + return { + position: 'absolute', + left: alignmentRect.left - contentRect.left, + top: alignmentRect.top - contentRect.top, + width: alignmentRect.width, + }; + }, [ snappedAlignment ] ); + + return ( + <> + + { isAlignmentVisualizerVisible && ( + + + + ) } + + { + onResizeStart( ...resizeArgs ); + const [ , resizeDirection, resizeElement ] = resizeArgs; + + // The 'ref' prop on the `ResizableBox` component is used to expose the re-resizable API. + // This seems to be the only way to get a ref to the element. + resizableRef.current = resizeElement; + hideBlockInterface(); + + if ( + resizeDirection === 'right' || + resizeDirection === 'left' + ) { + setIsAlignmentVisualizerVisible( true ); + } + } } + onResize={ ( event, resizeDirection, resizableElement ) => { + // Detect if snapping is happening. + const newSnappedAlignment = detectSnapping( + resizableElement, + resizeDirection + ); + if ( + newSnappedAlignment?.name !== snappedAlignment?.name + ) { + setSnappedAlignment( newSnappedAlignment ); + } + } } + onResizeStop={ ( ...resizeArgs ) => { + if ( onSnap && snappedAlignment ) { + onSnap( snappedAlignment?.name ); + } else { + onResizeStop( ...resizeArgs ); + } + setIsAlignmentVisualizerVisible( false ); + showBlockInterface(); + setSnappedAlignment( null ); + } } + resizeRatio={ currentAlignment === 'center' ? 2 : 1 } + > + + { children } + + + + ); +} + +export default function ResizableAlignmentControlsWithZoneContext( { + ...props +} ) { + return ( + + + + ); +} diff --git a/packages/block-editor/src/content.scss b/packages/block-editor/src/content.scss index fa78f2d41f9f9..2893c27cd238c 100644 --- a/packages/block-editor/src/content.scss +++ b/packages/block-editor/src/content.scss @@ -1,3 +1,4 @@ +@import "./components/block-alignment-visualizer/style.scss"; @import "./components/block-icon/content.scss"; @import "./components/block-list/content.scss"; @import "./components/block-list-appender/content.scss"; diff --git a/packages/block-editor/src/hooks/dimensions.js b/packages/block-editor/src/hooks/dimensions.js index 02c3931ef9850..1049ea01d5eba 100644 --- a/packages/block-editor/src/hooks/dimensions.js +++ b/packages/block-editor/src/hooks/dimensions.js @@ -2,8 +2,9 @@ * WordPress dependencies */ import { useState, useEffect, useCallback } from '@wordpress/element'; -import { useDispatch } from '@wordpress/data'; import { getBlockSupport } from '@wordpress/blocks'; +import { usePrevious } from '@wordpress/compose'; +import { useDispatch } from '@wordpress/data'; import deprecated from '@wordpress/deprecated'; /** @@ -28,16 +29,18 @@ export const AXIAL_SIDES = [ 'vertical', 'horizontal' ]; function useVisualizer() { const [ property, setProperty ] = useState( false ); + const previousProperty = usePrevious( property ); const { hideBlockInterface, showBlockInterface } = unlock( useDispatch( blockEditorStore ) ); useEffect( () => { - if ( ! property ) { + if ( ! property && previousProperty ) { showBlockInterface(); - } else { + } + if ( property && ! previousProperty ) { hideBlockInterface(); } - }, [ property, showBlockInterface, hideBlockInterface ] ); + }, [ property, previousProperty, showBlockInterface, hideBlockInterface ] ); return [ property, setProperty ]; } diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js index c513ede8b9fe2..8003a175cdd96 100644 --- a/packages/block-library/src/image/image.js +++ b/packages/block-library/src/image/image.js @@ -10,7 +10,6 @@ import { isBlobURL } from '@wordpress/blob'; import { ExternalLink, PanelBody, - ResizableBox, Spinner, TextareaControl, TextControl, @@ -30,6 +29,7 @@ import { __experimentalImageEditor as ImageEditor, __experimentalGetElementClassName, __experimentalUseBorderProps as useBorderProps, + __experimentalResizableAlignmentControls as ResizableAlignmentControls, } from '@wordpress/block-editor'; import { useEffect, @@ -38,7 +38,7 @@ import { useRef, useCallback, } from '@wordpress/element'; -import { __, sprintf, isRTL } from '@wordpress/i18n'; +import { __, sprintf } from '@wordpress/i18n'; import { getFilename } from '@wordpress/url'; import { createBlock, @@ -300,10 +300,11 @@ export default function Image( { } ); } - function updateAlignment( nextAlign ) { - const extraUpdatedAttributes = [ 'wide', 'full' ].includes( nextAlign ) - ? { width: undefined, height: undefined } - : {}; + function updateAlignment( nextAlign, { resetDimensions } = {} ) { + const extraUpdatedAttributes = + resetDimensions || [ 'wide', 'full' ].includes( nextAlign ) + ? { width: undefined, height: undefined } + : {}; setAttributes( { ...extraUpdatedAttributes, align: nextAlign, @@ -560,53 +561,36 @@ export default function Image( { // becomes available. const maxWidthBuffer = maxWidth * 2.5; - let showRightHandle = false; - let showLeftHandle = false; - - /* eslint-disable no-lonely-if */ - // See https://github.com/WordPress/gutenberg/issues/7584. - if ( align === 'center' ) { - // When the image is centered, show both handles. - showRightHandle = true; - showLeftHandle = true; - } else if ( isRTL() ) { - // In RTL mode the image is on the right by default. - // Show the right handle and hide the left handle only when it is - // aligned left. Otherwise always show the left handle. - if ( align === 'left' ) { - showRightHandle = true; - } else { - showLeftHandle = true; - } - } else { - // Show the left handle and hide the right handle only when the - // image is aligned right. Otherwise always show the right handle. - if ( align === 'right' ) { - showLeftHandle = true; - } else { - showRightHandle = true; - } - } - /* eslint-enable no-lonely-if */ + const imgInner = ( + { onImageError() } + onLoad={ ( event ) => { + setLoadedNaturalSize( { + loadedNaturalWidth: event.target?.naturalWidth, + loadedNaturalHeight: event.target?.naturalHeight, + } ); + } } + ref={ imageRef } + className={ borderProps.className } + style={ { + ...borderProps.style, + width: 'inherit', + height: 'auto', + } } + /> + ); img = ( - { onResizeStop(); @@ -615,10 +599,20 @@ export default function Image( { height: parseInt( currentHeight + delta.height, 10 ), } ); } } - resizeRatio={ align === 'center' ? 2 : 1 } + onSnap={ ( newAlignment ) => { + // When snapping, reset the image dimensions. This ensures + // when the image is set to align 'none', any custom dimensions + // are removed, and the image appears as content width. + updateAlignment( newAlignment, { resetDimensions: true } ); + } } + showHandle={ isSelected } + size={ { + width: width ?? 'auto', + height: height && ! hasCustomBorder ? height : 'auto', + } } > - { img } - + { imgInner } + ); } diff --git a/packages/components/src/popover/index.tsx b/packages/components/src/popover/index.tsx index ff72f371c4009..ce84403d7b9ff 100644 --- a/packages/components/src/popover/index.tsx +++ b/packages/components/src/popover/index.tsx @@ -180,6 +180,7 @@ const UnforwardedPopover = ( expandOnMobile, onFocusOutside, __unstableSlotName = SLOT_NAME, + __unstableInline = false, flip = true, resize = true, shift = false, @@ -564,12 +565,14 @@ const UnforwardedPopover = ( ); - if ( slot.ref ) { - content = { content }; - } + if ( ! __unstableInline ) { + if ( slot.ref ) { + content = { content }; + } - if ( anchorRef || anchorRect || anchor ) { - return content; + if ( anchorRef || anchorRect || anchor ) { + return content; + } } return { content }; diff --git a/packages/components/src/popover/types.ts b/packages/components/src/popover/types.ts index 6dc3a4ae7d53f..71118e3ec6c04 100644 --- a/packages/components/src/popover/types.ts +++ b/packages/components/src/popover/types.ts @@ -36,6 +36,12 @@ export type PopoverProps = { * @default 'Popover' */ __unstableSlotName?: string; + /** + * Should the popover render inline (not in a slot). + * + * @default false + */ + __unstableInline?: boolean; /** * The element that should be used by the popover as its anchor. It can either * be an `Element` or, alternatively, a `VirtualElement` — ie. an object with diff --git a/packages/dom/README.md b/packages/dom/README.md index f87ccbb3ac731..eaee0213b4bfa 100644 --- a/packages/dom/README.md +++ b/packages/dom/README.md @@ -124,6 +124,20 @@ _Returns_ - `DOMRect?`: The rectangle. +### getScreenRect + +Gets an element's true screen space rect, offsetting any intervening iFrames +in the element's ancestry. + +_Parameters_ + +- _element_ `Element`: The dom element to return the rect. +- _rect_ `?DOMRect`: The rect to offset. Only use if you already have `element`'s rect, this will save a call to `getBoundingClientRect`. + +_Returns_ + +- `DOMRect|undefined`: The rect offset by any parent iFrames. + ### getScrollContainer Given a DOM node, finds the closest scrollable container node or the node itself, if scrollable. diff --git a/packages/dom/src/dom/get-screen-rect.js b/packages/dom/src/dom/get-screen-rect.js new file mode 100644 index 0000000000000..98ab3bb1f5701 --- /dev/null +++ b/packages/dom/src/dom/get-screen-rect.js @@ -0,0 +1,32 @@ +/** + * Gets an element's true screen space rect, offsetting any intervening iFrames + * in the element's ancestry. + * + * @param {Element} element The dom element to return the rect. + * @param {?DOMRect} rect The rect to offset. Only use if you already have `element`'s rect, + * this will save a call to `getBoundingClientRect`. + * + * @return {DOMRect|undefined} The rect offset by any parent iFrames. + */ +export default function getScreenRect( element, rect ) { + const frame = element?.ownerDocument?.defaultView?.frameElement; + + // Return early when there's no parent iframe. + if ( ! frame ) { + return rect ?? element.getBoundingClientRect(); + } + + const frameRect = frame?.getBoundingClientRect(); + rect = rect ?? element?.getBoundingClientRect(); + + const offsetRect = new window.DOMRect( + rect.x + ( frameRect?.left ?? 0 ), + rect.y + ( frameRect?.top ?? 0 ), + rect.width, + rect.height + ); + + // Perform a tail recursion and continue offsetting + // by the next parent iframe. + return getScreenRect( frame, offsetRect ); +} diff --git a/packages/dom/src/dom/index.js b/packages/dom/src/dom/index.js index f21ec1e4e85e6..10cc881fb5a95 100644 --- a/packages/dom/src/dom/index.js +++ b/packages/dom/src/dom/index.js @@ -3,6 +3,7 @@ export { default as documentHasTextSelection } from './document-has-text-selecti export { default as documentHasUncollapsedSelection } from './document-has-uncollapsed-selection'; export { default as documentHasSelection } from './document-has-selection'; export { default as getRectangleFromRange } from './get-rectangle-from-range'; +export { default as getScreenRect } from './get-screen-rect'; export { default as getScrollContainer } from './get-scroll-container'; export { default as getOffsetParent } from './get-offset-parent'; export { default as isEntirelySelected } from './is-entirely-selected'; From fa9c580f1c3e4fe201582db85909190db6db0f6e Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 15 Mar 2023 16:46:13 +0800 Subject: [PATCH 02/20] Make snapping more magnetic, requiring a longer dwell time from the user before snapping initiates --- .../guide-context.js | 38 +++++++++++++++++-- .../resizable-alignment-controls/index.js | 4 +- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/packages/block-editor/src/components/block-alignment-visualizer/guide-context.js b/packages/block-editor/src/components/block-alignment-visualizer/guide-context.js index c9550f93eccde..39e82d478ee60 100644 --- a/packages/block-editor/src/components/block-alignment-visualizer/guide-context.js +++ b/packages/block-editor/src/components/block-alignment-visualizer/guide-context.js @@ -67,8 +67,14 @@ function detectSnapping( element, snapEdge, alignmentGuides, snapGap ) { return candidateGuide; } -export function useDetectSnapping( snapGap ) { +export function useDetectSnapping( { + snapGap = 50, + dwellTime = 300, + throttle = 100, +} = {} ) { const alignmentGuides = useBlockAlignmentGuides(); + const snappedAlignmentInfo = useRef(); + return useThrottle( ( element, snapEdge ) => { const snappedAlignment = detectSnapping( element, @@ -76,8 +82,34 @@ export function useDetectSnapping( snapGap ) { alignmentGuides, snapGap ); - const guide = alignmentGuides.get( snappedAlignment ); + // Set snapped alignment info when the user first reaches a snap guide. + if ( + snappedAlignment && + ( ! snappedAlignmentInfo.current || + snappedAlignmentInfo.current.name !== snappedAlignment ) + ) { + snappedAlignmentInfo.current = { + timestamp: Date.now(), + name: snappedAlignment, + }; + } + + // Unset snapped alignment info when the user moves away from a snap guide. + if ( ! snappedAlignment && snappedAlignmentInfo.current ) { + snappedAlignmentInfo.current = null; + return null; + } + + // If the user hasn't dwelt long enough on the alignment, return early. + if ( + snappedAlignmentInfo.current && + Date.now() - snappedAlignmentInfo.current.timestamp < dwellTime + ) { + return null; + } + + const guide = alignmentGuides.get( snappedAlignmentInfo.current?.name ); if ( ! guide ) { return null; } @@ -86,5 +118,5 @@ export function useDetectSnapping( snapGap ) { name: snappedAlignment, rect: getScreenRect( guide ), }; - }, 100 ); + }, throttle ); } diff --git a/packages/block-editor/src/components/resizable-alignment-controls/index.js b/packages/block-editor/src/components/resizable-alignment-controls/index.js index a2582a6dd7095..07143f5de62c7 100644 --- a/packages/block-editor/src/components/resizable-alignment-controls/index.js +++ b/packages/block-editor/src/components/resizable-alignment-controls/index.js @@ -22,8 +22,6 @@ import { import { store as blockEditorStore } from '../../store'; import { unlock } from '../../lock-unlock'; -const SNAP_GAP = 30; - function getVisibleHandles( alignment ) { if ( alignment === 'center' ) { // When the image is centered, show both handles. @@ -83,7 +81,7 @@ function ResizableAlignmentControls( { size, } ) { const resizableRef = useRef(); - const detectSnapping = useDetectSnapping( SNAP_GAP ); + const detectSnapping = useDetectSnapping(); const [ isAlignmentVisualizerVisible, setIsAlignmentVisualizerVisible ] = useState( false ); const [ snappedAlignment, setSnappedAlignment ] = useState( null ); From 94a401f9e9fdb059240b14226022d91f78254faf Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Tue, 28 Mar 2023 10:35:35 +0800 Subject: [PATCH 03/20] Show a tooltip above the snapped alignment --- .../block-alignment-visualizer/index.js | 1 + .../visualization.js | 75 ++++++++++++++++--- .../resizable-alignment-controls/index.js | 2 +- 3 files changed, 68 insertions(+), 10 deletions(-) diff --git a/packages/block-editor/src/components/block-alignment-visualizer/index.js b/packages/block-editor/src/components/block-alignment-visualizer/index.js index 6b81a90534af8..f8a44f05cc7b2 100644 --- a/packages/block-editor/src/components/block-alignment-visualizer/index.js +++ b/packages/block-editor/src/components/block-alignment-visualizer/index.js @@ -67,6 +67,7 @@ export default function BlockAlignmentVisualizer( { contentSize={ layout.contentSize } wideSize={ layout.wideSize } justification={ layout.justifyContent } + highlightedAlignment={ highlightedAlignment } /> @@ -45,9 +59,31 @@ export default function Visualization( { } .block-editor-alignment-visualizer__visualization-segment { - background: #3d5af2; - opacity: 0.2; + position: relative; + background: rgba(61, 90, 242, 0.2); + border-radius: 2px; + } + + .block-editor-alignment-visualizer__visualization-segment-label { + position: absolute; + top: -32px; + right: 0px; + background: rgba( 0, 0, 0, 0.8 ); border-radius: 2px; + color: white; + font-size: 12px; + min-width: 32px; + opacity: 0; + padding: 4px 8px; + pointer-events: none; + text-align: center; + transition: opacity 120ms ease; + user-select: none; + line-height: 1.4; + } + + .block-editor-alignment-visualizer__visualization-segment-label.is-highlighted { + opacity: 1; } /* Hide wide width alignments when the container is smaller than wide size */ @@ -116,7 +152,10 @@ export default function Visualization( { /> ) ) } - + { alignments.map( ( { name } ) => ( name === 'full' || name === 'wide' ) && ( @@ -124,6 +163,9 @@ export default function Visualization( { key={ `${ name }-right` } alignment={ name } side="right" + isHighlighted={ + highlightedAlignment === name + } /> ) ) } @@ -133,7 +175,9 @@ export default function Visualization( { ); } -function VisualizationSegment( { side, alignment } ) { +function VisualizationSegment( { side, alignment, isHighlighted } ) { + const label = ALIGNMENT_LABELS[ alignment ]; + return (
+ > + { !! label && ( +
+ { label } +
+ ) } +
); } diff --git a/packages/block-editor/src/components/resizable-alignment-controls/index.js b/packages/block-editor/src/components/resizable-alignment-controls/index.js index 07143f5de62c7..78569584d8f6c 100644 --- a/packages/block-editor/src/components/resizable-alignment-controls/index.js +++ b/packages/block-editor/src/components/resizable-alignment-controls/index.js @@ -126,7 +126,7 @@ function ResizableAlignmentControls( { layoutClientId={ rootClientId } focusedClientId={ clientId } allowedAlignments={ allowedAlignments } - highlightedAlignment={ snappedAlignment } + highlightedAlignment={ snappedAlignment?.name } /> ) } From 96f47a39db0ed0aed56427d6500781ae5539d165 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Tue, 28 Mar 2023 10:46:03 +0800 Subject: [PATCH 04/20] Avoid horizontal scrollbar when resizing --- .../block-alignment-visualizer/layout-popover.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/block-editor/src/components/block-alignment-visualizer/layout-popover.js b/packages/block-editor/src/components/block-alignment-visualizer/layout-popover.js index a485676bd40dc..ad2dfbc291192 100644 --- a/packages/block-editor/src/components/block-alignment-visualizer/layout-popover.js +++ b/packages/block-editor/src/components/block-alignment-visualizer/layout-popover.js @@ -61,8 +61,8 @@ export default function LayoutPopover( { return new defaultView.DOMRect( layoutRect.x, focusedBlockRect.y, - layoutRect.width, - focusedBlockRect.height + Math.floor( layoutRect.width ) - 1, + Math.floor( focusedBlockRect.height ) ); }, } ); @@ -79,8 +79,8 @@ export default function LayoutPopover( { // ); setCoverElementStyle( { position: 'absolute', - width: resolvedLayoutElement.offsetWidth, - height: focusedBlockElement.offsetHeight, + width: Math.floor( resolvedLayoutElement.offsetWidth ) - 1, + height: Math.floor( focusedBlockElement.offsetHeight ), // paddingLeft, // paddingRight, } ); From f556dbb1c27892061a367b48800ad99875557d2c Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Tue, 28 Mar 2023 11:25:02 +0800 Subject: [PATCH 05/20] Ensure block assumes the correct height when snapping --- .../resizable-alignment-controls/index.js | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/packages/block-editor/src/components/resizable-alignment-controls/index.js b/packages/block-editor/src/components/resizable-alignment-controls/index.js index 78569584d8f6c..30aca72d1f757 100644 --- a/packages/block-editor/src/components/resizable-alignment-controls/index.js +++ b/packages/block-editor/src/components/resizable-alignment-controls/index.js @@ -81,6 +81,7 @@ function ResizableAlignmentControls( { size, } ) { const resizableRef = useRef(); + const aspect = useRef( size.height / size.width ); const detectSnapping = useDetectSnapping(); const [ isAlignmentVisualizerVisible, setIsAlignmentVisualizerVisible ] = useState( false ); @@ -95,23 +96,43 @@ function ResizableAlignmentControls( { [ clientId ] ); + // Compute the styles of the content when snapped or unsnapped. const contentStyle = useMemo( () => { if ( ! snappedAlignment ) { // By default the content takes up the full width of the resizable box. return { width: '100%' }; } - const contentRect = getScreenRect( resizableRef.current ); + // Calculate the positioning of the snapped image. + const resizableRect = getScreenRect( resizableRef.current ); const alignmentRect = snappedAlignment.rect; - return { position: 'absolute', - left: alignmentRect.left - contentRect.left, - top: alignmentRect.top - contentRect.top, + left: alignmentRect.left - resizableRect.left, + top: alignmentRect.top - resizableRect.top, width: alignmentRect.width, }; }, [ snappedAlignment ] ); + // Because the `contentStyle` is absolutely positioned when snapping occurs + // the block won't have the correct height. A separate div is used to provide + // the correct height, calculated here. + const heightStyle = useMemo( () => { + if ( ! snappedAlignment ) { + // This is a bit hacky, but using `float: left` ensures the element + // isn't part of the layout but still gives a height to the + // container. + return { float: 'left', height: 'inherit' }; + } + + const alignmentRect = snappedAlignment.rect; + + return { + float: 'left', + height: alignmentRect.width * aspect.current, + }; + }, [ snappedAlignment ] ); + return ( <> @@ -180,6 +201,11 @@ function ResizableAlignmentControls( { } } resizeRatio={ currentAlignment === 'center' ? 2 : 1 } > + Date: Tue, 28 Mar 2023 12:12:40 +0800 Subject: [PATCH 06/20] Fix typo --- .../src/components/resizable-alignment-controls/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/resizable-alignment-controls/index.js b/packages/block-editor/src/components/resizable-alignment-controls/index.js index 30aca72d1f757..5af504f432cfb 100644 --- a/packages/block-editor/src/components/resizable-alignment-controls/index.js +++ b/packages/block-editor/src/components/resizable-alignment-controls/index.js @@ -47,7 +47,7 @@ function getVisibleHandles( alignment ) { } /** - * A component that composes together the `ResizebleBox` and `BlockAlignmentVisualizer` + * A component that composes `ResizableBox` and `BlockAlignmentVisualizer` * and configures snapping to block alignments. * * @param {Object} props From ccefecea3f5dee4f5c2f1e3e5bcf116d3d5928a7 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Tue, 28 Mar 2023 12:47:10 +0800 Subject: [PATCH 07/20] Remove unused CSS --- .../src/components/block-alignment-visualizer/style.scss | 9 --------- 1 file changed, 9 deletions(-) diff --git a/packages/block-editor/src/components/block-alignment-visualizer/style.scss b/packages/block-editor/src/components/block-alignment-visualizer/style.scss index 594a88b8cfaf1..5afffd4c4f715 100644 --- a/packages/block-editor/src/components/block-alignment-visualizer/style.scss +++ b/packages/block-editor/src/components/block-alignment-visualizer/style.scss @@ -17,12 +17,3 @@ } } - -.block-editor__alignment-visualizer-guide__label { - // Values match capitalized label style. - font-size: 11px; - font-weight: 500; - text-transform: uppercase; - white-space: nowrap; - margin: $grid-unit-05 0; -} From 97e30c5a382b24f57c024e85a5db042d8c0cae10 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Wed, 29 Mar 2023 10:47:28 +0800 Subject: [PATCH 08/20] Remove unused code --- .../layout-popover.js | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/packages/block-editor/src/components/block-alignment-visualizer/layout-popover.js b/packages/block-editor/src/components/block-alignment-visualizer/layout-popover.js index ad2dfbc291192..a0cbd94b72b28 100644 --- a/packages/block-editor/src/components/block-alignment-visualizer/layout-popover.js +++ b/packages/block-editor/src/components/block-alignment-visualizer/layout-popover.js @@ -10,12 +10,6 @@ import { useContext, useEffect, useState } from '@wordpress/element'; import { BlockList } from '../'; import { __unstableUseBlockElement as useBlockElement } from '../block-list/use-block-props/use-block-refs'; -function getComputedCSS( element, property ) { - return element.ownerDocument.defaultView - .getComputedStyle( element ) - .getPropertyValue( property ); -} - export default function LayoutPopover( { className, coverClassName, @@ -67,22 +61,10 @@ export default function LayoutPopover( { }, } ); - // The cover element is an inner element within the popover. It has the width of the layout - // and height of the focused block, and also matches any padding of the layout. - // const paddingLeft = getComputedCSS( - // resolvedLayoutElement, - // 'padding-left' - // ); - // const paddingRight = getComputedCSS( - // resolvedLayoutElement, - // 'padding-right' - // ); setCoverElementStyle( { position: 'absolute', width: Math.floor( resolvedLayoutElement.offsetWidth ) - 1, height: Math.floor( focusedBlockElement.offsetHeight ), - // paddingLeft, - // paddingRight, } ); }; From 7bef33fb5308e9904e54f3b7921e0cce8b91522b Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Mon, 3 Apr 2023 16:12:55 +0800 Subject: [PATCH 09/20] Fix image not inheriting correct size --- .../src/components/resizable-alignment-controls/index.js | 2 +- packages/block-library/src/image/image.js | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/block-editor/src/components/resizable-alignment-controls/index.js b/packages/block-editor/src/components/resizable-alignment-controls/index.js index 5af504f432cfb..37eba8a3ea344 100644 --- a/packages/block-editor/src/components/resizable-alignment-controls/index.js +++ b/packages/block-editor/src/components/resizable-alignment-controls/index.js @@ -100,7 +100,7 @@ function ResizableAlignmentControls( { const contentStyle = useMemo( () => { if ( ! snappedAlignment ) { // By default the content takes up the full width of the resizable box. - return { width: '100%' }; + return { width: 'inherit' }; } // Calculate the positioning of the snapped image. diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js index 8003a175cdd96..e189d273c2b50 100644 --- a/packages/block-library/src/image/image.js +++ b/packages/block-library/src/image/image.js @@ -576,8 +576,6 @@ export default function Image( { className={ borderProps.className } style={ { ...borderProps.style, - width: 'inherit', - height: 'auto', } } /> ); From 975bbeb6caf27a436c3e854d9b9abd755dd73421 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Mon, 3 Apr 2023 16:16:37 +0800 Subject: [PATCH 10/20] Prevent image layout animation unless alignment visualizer is visible --- .../resizable-alignment-controls/index.js | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/packages/block-editor/src/components/resizable-alignment-controls/index.js b/packages/block-editor/src/components/resizable-alignment-controls/index.js index 37eba8a3ea344..87763106b09fe 100644 --- a/packages/block-editor/src/components/resizable-alignment-controls/index.js +++ b/packages/block-editor/src/components/resizable-alignment-controls/index.js @@ -201,18 +201,23 @@ function ResizableAlignmentControls( { } } resizeRatio={ currentAlignment === 'center' ? 2 : 1 } > - - - { children } - + { isAlignmentVisualizerVisible && ( + <> + + + { children } + + + ) } + { ! isAlignmentVisualizerVisible && children } ); From 8671b30ea3d6e5fd5e0908818118fa739c9f89fe Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Mon, 3 Apr 2023 16:20:59 +0800 Subject: [PATCH 11/20] Remove duplicate code --- packages/block-library/src/image/image.js | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js index e189d273c2b50..7b7d5ab10490e 100644 --- a/packages/block-library/src/image/image.js +++ b/packages/block-library/src/image/image.js @@ -561,25 +561,6 @@ export default function Image( { // becomes available. const maxWidthBuffer = maxWidth * 2.5; - const imgInner = ( - { onImageError() } - onLoad={ ( event ) => { - setLoadedNaturalSize( { - loadedNaturalWidth: event.target?.naturalWidth, - loadedNaturalHeight: event.target?.naturalHeight, - } ); - } } - ref={ imageRef } - className={ borderProps.className } - style={ { - ...borderProps.style, - } } - /> - ); - img = ( - { imgInner } + { img } ); } From dc6471ff2dc82fb1878f936909b55e769fc2a205 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Tue, 4 Apr 2023 12:13:31 +0800 Subject: [PATCH 12/20] Fix label wrapping --- .../src/components/block-alignment-visualizer/visualization.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/block-editor/src/components/block-alignment-visualizer/visualization.js b/packages/block-editor/src/components/block-alignment-visualizer/visualization.js index 8409ed6db2e2a..cd29d81b25608 100644 --- a/packages/block-editor/src/components/block-alignment-visualizer/visualization.js +++ b/packages/block-editor/src/components/block-alignment-visualizer/visualization.js @@ -73,6 +73,7 @@ export default function Visualization( { color: white; font-size: 12px; min-width: 32px; + width: max-content; opacity: 0; padding: 4px 8px; pointer-events: none; From e2e478b5e88293ca9ada9e9bad5560e83a58416b Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Tue, 4 Apr 2023 12:19:37 +0800 Subject: [PATCH 13/20] Make ResizableAlignmentControls a private api --- packages/block-editor/src/components/index.js | 1 - packages/block-editor/src/private-apis.js | 2 ++ packages/block-library/src/image/image.js | 4 +++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js index e5ba38bb153a1..66830c4e57aaf 100644 --- a/packages/block-editor/src/components/index.js +++ b/packages/block-editor/src/components/index.js @@ -77,7 +77,6 @@ export { default as MediaUpload } from './media-upload'; export { default as MediaUploadCheck } from './media-upload/check'; export { default as PanelColorSettings } from './panel-color-settings'; export { default as PlainText } from './plain-text'; -export { default as __experimentalResizableAlignmentControls } from './resizable-alignment-controls'; export { default as __experimentalResponsiveBlockControl } from './responsive-block-control'; export { default as RichText, diff --git a/packages/block-editor/src/private-apis.js b/packages/block-editor/src/private-apis.js index ceb6bcd0e31c0..785ecd70487ea 100644 --- a/packages/block-editor/src/private-apis.js +++ b/packages/block-editor/src/private-apis.js @@ -9,6 +9,7 @@ import LeafMoreMenu from './components/off-canvas-editor/leaf-more-menu'; import ResizableBoxPopover from './components/resizable-box-popover'; import { ComposedPrivateInserter as PrivateInserter } from './components/inserter'; import { PrivateListView } from './components/list-view'; +import ResizableAlignmentControls from './components/resizable-alignment-controls'; /** * Private @wordpress/block-editor APIs. @@ -22,4 +23,5 @@ lock( privateApis, { PrivateInserter, PrivateListView, ResizableBoxPopover, + ResizableAlignmentControls, } ); diff --git a/packages/block-library/src/image/image.js b/packages/block-library/src/image/image.js index 7b7d5ab10490e..47bde4282b73b 100644 --- a/packages/block-library/src/image/image.js +++ b/packages/block-library/src/image/image.js @@ -29,7 +29,7 @@ import { __experimentalImageEditor as ImageEditor, __experimentalGetElementClassName, __experimentalUseBorderProps as useBorderProps, - __experimentalResizableAlignmentControls as ResizableAlignmentControls, + privateApis as blockEditorPrivateApis, } from '@wordpress/block-editor'; import { useEffect, @@ -60,6 +60,7 @@ import { store as coreStore } from '@wordpress/core-data'; import { createUpgradedEmbedBlock } from '../embed/util'; import useClientWidth from './use-client-width'; import { isExternalImage } from './edit'; +import { unlock } from '../private-apis'; /** * Module constants @@ -81,6 +82,7 @@ export default function Image( { clientId, isContentLocked, } ) { + const { ResizableAlignmentControls } = unlock( blockEditorPrivateApis ); const { url = '', alt, From 25dd8bd729d32e140e1232f28850eb1a35e86f7f Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Tue, 4 Apr 2023 12:46:57 +0800 Subject: [PATCH 14/20] Use an experiment toggle --- lib/experimental/editor-settings.php | 3 ++ lib/experiments-page.php | 12 ++++++ .../resizable-alignment-controls/index.js | 40 ++++++++++++------- 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/lib/experimental/editor-settings.php b/lib/experimental/editor-settings.php index a700a0f484371..ba667019c17a8 100644 --- a/lib/experimental/editor-settings.php +++ b/lib/experimental/editor-settings.php @@ -92,6 +92,9 @@ function gutenberg_enable_experiments() { if ( $gutenberg_experiments && array_key_exists( 'gutenberg-details-blocks', $gutenberg_experiments ) ) { wp_add_inline_script( 'wp-block-editor', 'window.__experimentalEnableDetailsBlocks = true', 'before' ); } + if ( $gutenberg_experiments && array_key_exists( 'gutenberg-image-block-alignment-snapping', $gutenberg_experiments ) ) { + wp_add_inline_script( 'wp-block-editor', 'window.__experimentalEnableImageBlockAlignmentSnapping = true', 'before' ); + } } add_action( 'admin_init', 'gutenberg_enable_experiments' ); diff --git a/lib/experiments-page.php b/lib/experiments-page.php index 5ba1830bb4e00..e8ed0ca1fca1b 100644 --- a/lib/experiments-page.php +++ b/lib/experiments-page.php @@ -101,6 +101,18 @@ function gutenberg_initialize_experiments_settings() { ) ); + add_settings_field( + 'gutenberg-image-block-alignment-snapping', + __( 'Image block snapping ', 'gutenberg' ), + 'gutenberg_display_experiment_field', + 'gutenberg-experiments', + 'gutenberg_experiments_section', + array( + 'label' => __( 'Test new guides for snapping to alignment sizes when resizing the image block.', 'gutenberg' ), + 'id' => 'gutenberg-image-block-alignment-snapping', + ) + ); + register_setting( 'gutenberg-experiments', 'gutenberg-experiments' diff --git a/packages/block-editor/src/components/resizable-alignment-controls/index.js b/packages/block-editor/src/components/resizable-alignment-controls/index.js index 87763106b09fe..241ca26385f2b 100644 --- a/packages/block-editor/src/components/resizable-alignment-controls/index.js +++ b/packages/block-editor/src/components/resizable-alignment-controls/index.js @@ -90,6 +90,11 @@ function ResizableAlignmentControls( { useDispatch( blockEditorStore ) ); + const isSnappingExperimentEnabled = + window.__experimentalEnableImageBlockAlignmentSnapping; + const showAlignmentVisualizer = + isSnappingExperimentEnabled && isAlignmentVisualizerVisible; + const rootClientId = useSelect( ( select ) => select( blockEditorStore ).getBlockRootClientId( clientId ), @@ -136,7 +141,7 @@ function ResizableAlignmentControls( { return ( <> - { isAlignmentVisualizerVisible && ( + { showAlignmentVisualizer && ( { - // Detect if snapping is happening. - const newSnappedAlignment = detectSnapping( - resizableElement, - resizeDirection - ); - if ( - newSnappedAlignment?.name !== snappedAlignment?.name - ) { - setSnappedAlignment( newSnappedAlignment ); + if ( showAlignmentVisualizer ) { + // Detect if snapping is happening. + const newSnappedAlignment = detectSnapping( + resizableElement, + resizeDirection + ); + if ( + newSnappedAlignment?.name !== snappedAlignment?.name + ) { + setSnappedAlignment( newSnappedAlignment ); + } } } } onResizeStop={ ( ...resizeArgs ) => { @@ -195,13 +203,15 @@ function ResizableAlignmentControls( { } else { onResizeStop( ...resizeArgs ); } - setIsAlignmentVisualizerVisible( false ); + if ( isSnappingExperimentEnabled ) { + setIsAlignmentVisualizerVisible( false ); + } showBlockInterface(); setSnappedAlignment( null ); } } resizeRatio={ currentAlignment === 'center' ? 2 : 1 } > - { isAlignmentVisualizerVisible && ( + { showAlignmentVisualizer && ( <> ) } - { ! isAlignmentVisualizerVisible && children } + { ! showAlignmentVisualizer && children } ); From 134283024dc6444206b2286b5cb469261350aca2 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Tue, 4 Apr 2023 14:36:11 +0800 Subject: [PATCH 15/20] Remove popover usage --- .../block-alignment-visualizer/index.js | 21 ++-- .../layout-popover.js | 106 ------------------ .../block-alignment-visualizer/style.scss | 19 ---- .../block-alignment-visualizer/underlay.js | 77 +++++++++++++ .../resizable-alignment-controls/index.js | 9 +- packages/block-editor/src/content.scss | 1 - 6 files changed, 86 insertions(+), 147 deletions(-) delete mode 100644 packages/block-editor/src/components/block-alignment-visualizer/layout-popover.js delete mode 100644 packages/block-editor/src/components/block-alignment-visualizer/style.scss create mode 100644 packages/block-editor/src/components/block-alignment-visualizer/underlay.js diff --git a/packages/block-editor/src/components/block-alignment-visualizer/index.js b/packages/block-editor/src/components/block-alignment-visualizer/index.js index f8a44f05cc7b2..7afa9feb33a0d 100644 --- a/packages/block-editor/src/components/block-alignment-visualizer/index.js +++ b/packages/block-editor/src/components/block-alignment-visualizer/index.js @@ -8,7 +8,7 @@ import { useSelect } from '@wordpress/data'; * Internal dependencies */ import ShadowDOMContainer from './shadow-dom-container'; -import LayoutPopover from './layout-popover'; +import Underlay from './underlay'; import { useLayout } from '../block-list/layout'; import useAvailableAlignments from '../block-alignment-control/use-available-alignments'; import { store as blockEditorStore } from '../../store'; @@ -19,16 +19,14 @@ import Guides from './guides'; /** * A component that displays block alignment guidelines. * - * @param {Object} props - * @param {?string[]} props.allowedAlignments An optional array of alignments names. By default, the alignment support will be derived from the - * 'focused' block's block supports, but some blocks (image) have an ad-hoc alignment implementation. - * @param {string|null} props.layoutClientId The client id of the block that provides the layout. - * @param {string} props.focusedClientId The client id of the block to show the alignment guides for. - * @param {?string} props.highlightedAlignment The alignment name to show the label of. + * @param {Object} props + * @param {?string[]} props.allowedAlignments An optional array of alignments names. By default, the alignment support will be derived from the + * 'focused' block's block supports, but some blocks (image) have an ad-hoc alignment implementation. + * @param {string} props.focusedClientId The client id of the block to show the alignment guides for. + * @param {?string} props.highlightedAlignment The alignment name to show the label of. */ export default function BlockAlignmentVisualizer( { allowedAlignments, - layoutClientId, focusedClientId, highlightedAlignment, } ) { @@ -54,12 +52,9 @@ export default function BlockAlignmentVisualizer( { } return ( - - + ); } diff --git a/packages/block-editor/src/components/block-alignment-visualizer/layout-popover.js b/packages/block-editor/src/components/block-alignment-visualizer/layout-popover.js deleted file mode 100644 index a0cbd94b72b28..0000000000000 --- a/packages/block-editor/src/components/block-alignment-visualizer/layout-popover.js +++ /dev/null @@ -1,106 +0,0 @@ -/** - * WordPress dependencies - */ -import { Popover } from '@wordpress/components'; -import { useContext, useEffect, useState } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import { BlockList } from '../'; -import { __unstableUseBlockElement as useBlockElement } from '../block-list/use-block-props/use-block-refs'; - -export default function LayoutPopover( { - className, - coverClassName, - layoutClientId, - focusedClientId, - isConstrained, - children, -} ) { - const [ popoverAnchor, setPopoverAnchor ] = useState( null ); - const [ coverElementStyle, setCoverElementStyle ] = useState( null ); - const focusedBlockElement = useBlockElement( focusedClientId ); - const layoutBlockElement = useBlockElement( layoutClientId ); - - // useBlockElement is unable to return the document's root block list. - // __unstableElementContext seems to provide this. - const rootBlockListElement = useContext( - BlockList.__unstableElementContext - ); - - useEffect( () => { - const resolvedLayoutElement = - isConstrained && layoutBlockElement - ? layoutBlockElement - : rootBlockListElement; - if ( ! focusedBlockElement || ! resolvedLayoutElement ) { - return; - } - - const { ownerDocument } = focusedBlockElement; - const { defaultView } = ownerDocument; - - const update = () => { - // The popover is positioned to the top of the block list that provides the layout - // and left of the 'focused' block. - setPopoverAnchor( { - ownerDocument, - getBoundingClientRect() { - const layoutRect = - resolvedLayoutElement.getBoundingClientRect(); - const focusedBlockRect = - focusedBlockElement.getBoundingClientRect(); - - return new defaultView.DOMRect( - layoutRect.x, - focusedBlockRect.y, - Math.floor( layoutRect.width ) - 1, - Math.floor( focusedBlockRect.height ) - ); - }, - } ); - - setCoverElementStyle( { - position: 'absolute', - width: Math.floor( resolvedLayoutElement.offsetWidth ) - 1, - height: Math.floor( focusedBlockElement.offsetHeight ), - } ); - }; - - // Observe any resizes of both the layout and focused elements. - const resizeObserver = defaultView.ResizeObserver - ? new defaultView.ResizeObserver( update ) - : undefined; - resizeObserver?.observe( resolvedLayoutElement ); - resizeObserver?.observe( focusedBlockElement ); - update(); - - return () => { - resizeObserver?.disconnect(); - }; - }, [ - focusedBlockElement, - layoutBlockElement, - rootBlockListElement, - isConstrained, - ] ); - - return ( - -
- { children } -
-
- ); -} diff --git a/packages/block-editor/src/components/block-alignment-visualizer/style.scss b/packages/block-editor/src/components/block-alignment-visualizer/style.scss deleted file mode 100644 index 5afffd4c4f715..0000000000000 --- a/packages/block-editor/src/components/block-alignment-visualizer/style.scss +++ /dev/null @@ -1,19 +0,0 @@ - -.block-editor-alignment-visualizer { - z-index: 0; -} -.block-editor-alignment-visualizer__cover-element { - z-index: 0; - - &::before { - content: ""; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - background-color: var(--contrast-color); - opacity: 0.05; - - } -} diff --git a/packages/block-editor/src/components/block-alignment-visualizer/underlay.js b/packages/block-editor/src/components/block-alignment-visualizer/underlay.js new file mode 100644 index 0000000000000..c314736ab25ad --- /dev/null +++ b/packages/block-editor/src/components/block-alignment-visualizer/underlay.js @@ -0,0 +1,77 @@ +/** + * WordPress dependencies + */ +import { useContext, useEffect, useState } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { BlockList } from '../'; +import { __unstableUseBlockElement as useBlockElement } from '../block-list/use-block-props/use-block-refs'; + +/** @typedef {import('react').ReactNode} ReactNode */ + +/** + * Underlay is a bit like a Popover, but is inline so only requires half the code. + * + * @param {Object} props + * @param {string} props.className A classname to apply to the underlay. + * @param {string} props.focusedClientId The client id of the block being interacted with. + * @param {ReactNode} props.children Child elements. + */ +export default function Underlay( { className, focusedClientId, children } ) { + const [ underlayStyle, setUnderlayStyle ] = useState( null ); + const focusedBlockElement = useBlockElement( focusedClientId ); + + // useBlockElement is unable to return the document's root block list. + // __unstableElementContext seems to provide this. + const rootBlockListElement = useContext( + BlockList.__unstableElementContext + ); + + useEffect( () => { + if ( ! focusedBlockElement || ! rootBlockListElement ) { + return; + } + + const { ownerDocument } = focusedBlockElement; + const { defaultView } = ownerDocument; + + const update = () => { + const layoutRect = rootBlockListElement.getBoundingClientRect(); + const focusedBlockRect = + focusedBlockElement.getBoundingClientRect(); + + // The 'underlay' has the width and horizontal positioning of the root block list, + // and the height and vertical positioning of the edited block. + // Note: using the root block list is a naive implementation here, ideally the parent + // block that provides the layout should be used. + setUnderlayStyle( { + position: 'absolute', + left: layoutRect.x - focusedBlockRect.x, + top: 0, + width: Math.floor( layoutRect.width ), + height: Math.floor( focusedBlockRect.height ), + zIndex: 0, + } ); + }; + + // Observe any resizes of both the layout and focused elements. + const resizeObserver = defaultView.ResizeObserver + ? new defaultView.ResizeObserver( update ) + : undefined; + resizeObserver?.observe( rootBlockListElement ); + resizeObserver?.observe( focusedBlockElement ); + update(); + + return () => { + resizeObserver?.disconnect(); + }; + }, [ focusedBlockElement, rootBlockListElement ] ); + + return ( +
+ { children } +
+ ); +} diff --git a/packages/block-editor/src/components/resizable-alignment-controls/index.js b/packages/block-editor/src/components/resizable-alignment-controls/index.js index 241ca26385f2b..a1ddf8a2c06a1 100644 --- a/packages/block-editor/src/components/resizable-alignment-controls/index.js +++ b/packages/block-editor/src/components/resizable-alignment-controls/index.js @@ -6,7 +6,7 @@ import { __unstableAnimatePresence as AnimatePresence, __unstableMotion as motion, } from '@wordpress/components'; -import { useDispatch, useSelect } from '@wordpress/data'; +import { useDispatch } from '@wordpress/data'; import { getScreenRect } from '@wordpress/dom'; import { useMemo, useRef, useState } from '@wordpress/element'; import { isRTL } from '@wordpress/i18n'; @@ -95,12 +95,6 @@ function ResizableAlignmentControls( { const showAlignmentVisualizer = isSnappingExperimentEnabled && isAlignmentVisualizerVisible; - const rootClientId = useSelect( - ( select ) => - select( blockEditorStore ).getBlockRootClientId( clientId ), - [ clientId ] - ); - // Compute the styles of the content when snapped or unsnapped. const contentStyle = useMemo( () => { if ( ! snappedAlignment ) { @@ -149,7 +143,6 @@ function ResizableAlignmentControls( { transition={ { duration: 0.15 } } > Date: Tue, 4 Apr 2023 14:37:07 +0800 Subject: [PATCH 16/20] Revert changes to Popover --- packages/components/src/popover/index.tsx | 13 +++++-------- packages/components/src/popover/types.ts | 6 ------ 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/packages/components/src/popover/index.tsx b/packages/components/src/popover/index.tsx index ce84403d7b9ff..ff72f371c4009 100644 --- a/packages/components/src/popover/index.tsx +++ b/packages/components/src/popover/index.tsx @@ -180,7 +180,6 @@ const UnforwardedPopover = ( expandOnMobile, onFocusOutside, __unstableSlotName = SLOT_NAME, - __unstableInline = false, flip = true, resize = true, shift = false, @@ -565,14 +564,12 @@ const UnforwardedPopover = ( ); - if ( ! __unstableInline ) { - if ( slot.ref ) { - content = { content }; - } + if ( slot.ref ) { + content = { content }; + } - if ( anchorRef || anchorRect || anchor ) { - return content; - } + if ( anchorRef || anchorRect || anchor ) { + return content; } return { content }; diff --git a/packages/components/src/popover/types.ts b/packages/components/src/popover/types.ts index 71118e3ec6c04..6dc3a4ae7d53f 100644 --- a/packages/components/src/popover/types.ts +++ b/packages/components/src/popover/types.ts @@ -36,12 +36,6 @@ export type PopoverProps = { * @default 'Popover' */ __unstableSlotName?: string; - /** - * Should the popover render inline (not in a slot). - * - * @default false - */ - __unstableInline?: boolean; /** * The element that should be used by the popover as its anchor. It can either * be an `Element` or, alternatively, a `VirtualElement` — ie. an object with From c18891c52878b91ac2e77a68f561f4d30631c78d Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Tue, 4 Apr 2023 14:38:28 +0800 Subject: [PATCH 17/20] Nest entire visualizer in shadow dom container --- .../components/block-alignment-visualizer/index.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/block-editor/src/components/block-alignment-visualizer/index.js b/packages/block-editor/src/components/block-alignment-visualizer/index.js index 7afa9feb33a0d..9aec7962079b1 100644 --- a/packages/block-editor/src/components/block-alignment-visualizer/index.js +++ b/packages/block-editor/src/components/block-alignment-visualizer/index.js @@ -52,11 +52,11 @@ export default function BlockAlignmentVisualizer( { } return ( - - + + - - + + ); } From 8bcef220fa7e2326bd148186b2b17c37ffbbb4fd Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Tue, 18 Apr 2023 14:00:52 +0800 Subject: [PATCH 18/20] Fix merge conflict issue --- packages/block-editor/src/components/block-list/content.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/block-list/content.scss b/packages/block-editor/src/components/block-list/content.scss index c6648061dcbf8..b1315357162e0 100644 --- a/packages/block-editor/src/components/block-list/content.scss +++ b/packages/block-editor/src/components/block-list/content.scss @@ -87,7 +87,7 @@ .block-editor-block-list__block.is-highlighted ~ .is-multi-selected, &.is-navigate-mode .block-editor-block-list__block.is-selected, & .is-block-moving-mode.block-editor-block-list__block.has-child-selected, - .block-editor-block-list__block:not([contenteditable]):not(.hide-block-border):focus { { + .block-editor-block-list__block:not([contenteditable]):not(.hide-block-border):focus { outline: none; // We're using a pseudo element to overflow placeholder borders From 7290e2078dce25f7257cc734e00fc9852719da52 Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Tue, 18 Apr 2023 16:14:21 +0800 Subject: [PATCH 19/20] Fix aspect ratio calculation --- .../src/components/resizable-alignment-controls/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/block-editor/src/components/resizable-alignment-controls/index.js b/packages/block-editor/src/components/resizable-alignment-controls/index.js index a1ddf8a2c06a1..9c4814d267b6b 100644 --- a/packages/block-editor/src/components/resizable-alignment-controls/index.js +++ b/packages/block-editor/src/components/resizable-alignment-controls/index.js @@ -81,7 +81,6 @@ function ResizableAlignmentControls( { size, } ) { const resizableRef = useRef(); - const aspect = useRef( size.height / size.width ); const detectSnapping = useDetectSnapping(); const [ isAlignmentVisualizerVisible, setIsAlignmentVisualizerVisible ] = useState( false ); @@ -125,12 +124,13 @@ function ResizableAlignmentControls( { } const alignmentRect = snappedAlignment.rect; + const aspect = size.height / size.width; return { float: 'left', - height: alignmentRect.width * aspect.current, + height: alignmentRect.width * aspect, }; - }, [ snappedAlignment ] ); + }, [ snappedAlignment, size.width, size.height ] ); return ( <> From af59e3ba194f446d514a3c0ac8def98051431aeb Mon Sep 17 00:00:00 2001 From: Daniel Richards Date: Tue, 18 Apr 2023 16:34:31 +0800 Subject: [PATCH 20/20] Fix docs. Not sure how this is related to this branch, so :shrug: --- packages/dom/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/dom/README.md b/packages/dom/README.md index eaee0213b4bfa..0aa3af77abb15 100644 --- a/packages/dom/README.md +++ b/packages/dom/README.md @@ -126,8 +126,7 @@ _Returns_ ### getScreenRect -Gets an element's true screen space rect, offsetting any intervening iFrames -in the element's ancestry. +Gets an element's true screen space rect, offsetting any intervening iFrames in the element's ancestry. _Parameters_