Skip to content

Commit

Permalink
Zoom out mode: scale iframe instead of contents (#47004)
Browse files Browse the repository at this point in the history
  • Loading branch information
ellatrix authored Feb 7, 2023
1 parent ef42b21 commit f303b93
Show file tree
Hide file tree
Showing 10 changed files with 141 additions and 76 deletions.
74 changes: 21 additions & 53 deletions packages/block-editor/src/components/block-popover/inbetween.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,53 +68,6 @@ function BlockPopoverInbetween( {
const previousElement = useBlockElement( previousClientId );
const nextElement = useBlockElement( nextClientId );
const isVertical = orientation === 'vertical';
const style = useMemo( () => {
if (
// popoverRecomputeCounter is by definition always equal or greater than 0.
// This check is only there to satisfy the correctness of the
// exhaustive-deps rule for the `useMemo` hook.
popoverRecomputeCounter < 0 ||
( ! previousElement && ! nextElement ) ||
! isVisible
) {
return {};
}

const previousRect = previousElement
? previousElement.getBoundingClientRect()
: null;
const nextRect = nextElement
? nextElement.getBoundingClientRect()
: null;

if ( isVertical ) {
return {
width: previousRect ? previousRect.width : nextRect.width,
height:
nextRect && previousRect
? nextRect.top - previousRect.bottom
: 0,
};
}

let width = 0;
if ( previousRect && nextRect ) {
width = isRTL()
? previousRect.left - nextRect.right
: nextRect.left - previousRect.right;
}

return {
width,
height: previousRect ? previousRect.height : nextRect.height,
};
}, [
previousElement,
nextElement,
isVertical,
popoverRecomputeCounter,
isVisible,
] );

const popoverAnchor = useMemo( () => {
if (
Expand Down Expand Up @@ -142,10 +95,17 @@ function BlockPopoverInbetween( {

let left = 0;
let top = 0;
let width = 0;
let height = 0;

if ( isVertical ) {
// vertical
top = previousRect ? previousRect.bottom : nextRect.top;
width = previousRect ? previousRect.width : nextRect.width;
height =
nextRect && previousRect
? nextRect.top - previousRect.bottom
: 0;

if ( isRTL() ) {
// vertical, rtl
Expand All @@ -158,21 +118,32 @@ function BlockPopoverInbetween( {
}
} else {
top = previousRect ? previousRect.top : nextRect.top;
height = previousRect
? previousRect.height
: nextRect.height;

if ( isRTL() ) {
// non vertical, rtl
left = previousRect
? previousRect.left
: nextRect.right;
width =
previousRect && nextRect
? previousRect.left - nextRect.right
: 0;
} else {
// non vertical, ltr
left = previousRect
? previousRect.right
: nextRect.left;
width =
previousRect && nextRect
? nextRect.left - previousRect.right
: 0;
}
}

return new window.DOMRect( left, top, 0, 0 );
return new window.DOMRect( left, top, width, height );
},
};
}, [
Expand Down Expand Up @@ -267,13 +238,10 @@ function BlockPopoverInbetween( {
) }
resize={ false }
flip={ false }
placement="bottom-start"
placement="overlay"
variant="unstyled"
>
<div
className="block-editor-block-popover__inbetween-container"
style={ style }
>
<div className="block-editor-block-popover__inbetween-container">
{ children }
</div>
</Popover>
Expand Down
38 changes: 20 additions & 18 deletions packages/block-editor/src/components/iframe/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ function Iframe( {
tabIndex = 0,
scale = 1,
frameSize = 0,
expand = false,
readonly,
forwardedRef: ref,
...props
Expand Down Expand Up @@ -246,11 +247,30 @@ function Iframe( {
return '<!doctype html>' + renderToString( styleAssets );
}, [] );

// We need to counter the margin created by scaling the iframe. If the scale
// is e.g. 0.45, then the top + bottom margin is 0.55 (1 - scale). Just the
// top or bottom margin is 0.55 / 2 ((1 - scale) / 2).
const marginFromScaling = ( contentHeight * ( 1 - scale ) ) / 2;

return (
<>
{ tabIndex >= 0 && before }
<iframe
{ ...props }
style={ {
...props.style,
height: expand ? contentHeight : props.style?.height,
marginTop: scale
? -marginFromScaling + frameSize
: props.style?.marginTop,
marginBottom: scale
? -marginFromScaling + frameSize
: props.style?.marginBottom,
transform: scale
? `scale( ${ scale } )`
: props.style?.transform,
transition: 'all .3s',
} }
ref={ useMergeRefs( [ ref, setRef ] ) }
tabIndex={ tabIndex }
// Correct doctype is required to enable rendering in standards
Expand All @@ -265,13 +285,6 @@ function Iframe( {
<head ref={ headRef }>
{ styleAssets }
{ head }
<style>
{ `html { transition: background 5s; ${
frameSize
? 'background: #2f2f2f; transition: background 0s;'
: ''
} }` }
</style>
</head>
<body
ref={ bodyRef }
Expand All @@ -280,17 +293,6 @@ function Iframe( {
'editor-styles-wrapper',
...bodyClasses
) }
style={ {
// This is the remaining percentage from the scaling down
// of the iframe body(`scale(0.45)`). We also need to subtract
// the body's bottom margin.
marginBottom: `-${
contentHeight * ( 1 - scale ) -
frameSize
}px`,
marginTop: frameSize,
transform: `scale( ${ scale } )`,
} }
>
{ contentResizeListener }
<StyleProvider document={ iframeDocument }>
Expand Down
2 changes: 2 additions & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
- `Dropdown`: deprecate `position` prop, use `popoverProps` instead ([46865](https://github.com/WordPress/gutenberg/pull/46865)).
- `Button`: improve padding for buttons with icon and text. ([46764](https://github.com/WordPress/gutenberg/pull/46764)).
- `ColorPalette`: Use computed color when css variable is passed to `ColorPicker` ([47181](https://github.com/WordPress/gutenberg/pull/47181)).
- `Popover`: add `overlay` option to the `placement` prop ([47004](https://github.com/WordPress/gutenberg/pull/47004)).

### Internal

Expand All @@ -44,6 +45,7 @@
- `QueryControls`: Convert to TypeScript ([#46721](https://github.com/WordPress/gutenberg/pull/46721)).
- `TreeGrid`: Convert to TypeScript ([#47516](https://github.com/WordPress/gutenberg/pull/47516)).
- `Notice`: refactor to TypeScript ([47118](https://github.com/WordPress/gutenberg/pull/47118)).
- `Popover`: Take iframe element scaling into account ([47004](https://github.com/WordPress/gutenberg/pull/47004)).

### Bug Fix

Expand Down
5 changes: 4 additions & 1 deletion packages/components/src/popover/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,10 +196,13 @@ A callback invoked when the popover should be closed.

- Required: No

### `placement`: `'top' | 'top-start' | 'top-end' | 'right' | 'right-start' | 'right-end' | 'bottom' | 'bottom-start' | 'bottom-end' | 'left' | 'left-start' | 'left-end'`
### `placement`: `'top' | 'top-start' | 'top-end' | 'right' | 'right-start' | 'right-end' | 'bottom' | 'bottom-start' | 'bottom-end' | 'left' | 'left-start' | 'left-end' | 'overlay'`

Used to specify the popover's position with respect to its anchor.

`overlay` is a special case that places the popover over the reference element.
Please note that other placement related props may not behave as excepted.

- Required: No
- Default: `"bottom-start"`

Expand Down
18 changes: 17 additions & 1 deletion packages/components/src/popover/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
import { close } from '@wordpress/icons';
import deprecated from '@wordpress/deprecated';
import { Path, SVG } from '@wordpress/primitives';
import { getScrollContainer } from '@wordpress/dom';

/**
* Internal dependencies
Expand All @@ -52,6 +53,7 @@ import ScrollLock from '../scroll-lock';
import { Slot, Fill, useSlot } from '../slot-fill';
import {
getFrameOffset,
getFrameScale,
positionToPlacement,
placementToMotionAnimationProps,
getReferenceOwnerDocument,
Expand All @@ -65,6 +67,7 @@ import type {
PopoverAnchorRefTopBottom,
} from './types';
import { limitShift as customLimitShift } from './limit-shift';
import { overlayMiddlewares } from './overlay-middlewares';

/**
* Name of slot in which popover should fill.
Expand Down Expand Up @@ -268,6 +271,7 @@ const UnforwardedPopover = (
const frameOffsetRef = useRef( getFrameOffset( referenceOwnerDocument ) );

const middleware = [
...( placementProp === 'overlay' ? overlayMiddlewares() : [] ),
// Custom middleware which adjusts the popover's position by taking into
// account the offset of the anchor's iframe (if any) compared to the page.
{
Expand Down Expand Up @@ -361,7 +365,10 @@ const UnforwardedPopover = (
placement: computedPlacement,
middlewareData: { arrow: arrowData },
} = useFloating( {
placement: normalizedPlacementFromProps,
placement:
normalizedPlacementFromProps === 'overlay'
? undefined
: normalizedPlacementFromProps,
middleware,
whileElementsMounted: ( referenceParam, floatingParam, updateParam ) =>
autoUpdate( referenceParam, floatingParam, updateParam, {
Expand Down Expand Up @@ -399,12 +406,14 @@ const UnforwardedPopover = (
fallbackReferenceElement,
fallbackDocument: document,
} );
const scale = getFrameScale( resultingReferenceOwnerDoc );
const resultingReferenceElement = getReferenceElement( {
anchor,
anchorRef,
anchorRect,
getAnchorRect,
fallbackReferenceElement,
scale,
} );

referenceCallbackRef( resultingReferenceElement );
Expand Down Expand Up @@ -441,17 +450,24 @@ const UnforwardedPopover = (
}

const { defaultView } = referenceOwnerDocument;
const { frameElement } = defaultView;

const scrollContainer = frameElement
? getScrollContainer( frameElement )
: null;

const updateFrameOffset = () => {
frameOffsetRef.current = getFrameOffset( referenceOwnerDocument );
update();
};
defaultView.addEventListener( 'resize', updateFrameOffset );
scrollContainer?.addEventListener( 'scroll', updateFrameOffset );

updateFrameOffset();

return () => {
defaultView.removeEventListener( 'resize', updateFrameOffset );
scrollContainer?.removeEventListener( 'scroll', updateFrameOffset );
};
}, [ referenceOwnerDocument, update, refs.floating ] );

Expand Down
29 changes: 29 additions & 0 deletions packages/components/src/popover/overlay-middlewares.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* External dependencies
*/
import { size, MiddlewareArguments } from '@floating-ui/react-dom';

export function overlayMiddlewares() {
return [
{
name: 'overlay',
fn( { rects }: MiddlewareArguments ) {
return rects.reference;
},
},
size( {
apply( { rects, elements } ) {
const { firstElementChild } = elements.floating ?? {};

// Only HTMLElement instances have the `style` property.
if ( ! ( firstElementChild instanceof HTMLElement ) ) return;

// Reduce the height of the popover to the available space.
Object.assign( firstElementChild.style, {
width: `${ rects.reference.width }px`,
height: `${ rects.reference.height }px`,
} );
},
} ),
];
}
8 changes: 7 additions & 1 deletion packages/components/src/popover/stories/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const AVAILABLE_PLACEMENTS: PopoverProps[ 'placement' ][] = [
'left',
'left-start',
'left-end',
'overlay',
];

const meta: ComponentMeta< typeof Popover > = {
Expand Down Expand Up @@ -170,7 +171,12 @@ export const AllPlacements: ComponentStory< typeof Popover > = ( {
</h2>
<div>
{ AVAILABLE_PLACEMENTS.map( ( p ) => (
<PopoverWithAnchor key={ p } placement={ p } { ...args }>
<PopoverWithAnchor
key={ p }
placement={ p }
{ ...args }
resize={ p === 'overlay' ? true : args.resize }
>
{ children }
<div>
<small>(placement: { p })</small>
Expand Down
6 changes: 4 additions & 2 deletions packages/components/src/popover/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ type DomRectWithOwnerDocument = DOMRect & {
ownerDocument?: Document;
};

type PopoverPlacement = Placement | 'overlay';

export type AnimatedWrapperProps = {
placement: Placement;
placement: PopoverPlacement;
shouldAnimate?: boolean;
};

Expand Down Expand Up @@ -111,7 +113,7 @@ export type PopoverProps = {
*
* @default 'bottom-start'
*/
placement?: Placement;
placement?: PopoverPlacement;
/**
* Legacy way to specify the popover's position with respect to its anchor.
* _Note: this prop is deprecated. Use the `placement` prop instead._
Expand Down
Loading

0 comments on commit f303b93

Please sign in to comment.