Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Block Selection Toolbar: Support fixed blocks by flipping toolbar when block goes out of view #46085

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/base-styles/_z-index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ $z-layers: (
".block-editor-url-input__suggestions": 30,
".edit-post-layout__footer": 30,
".interface-interface-skeleton__header": 30,
".edit-site-layout__header": 30,
".interface-interface-skeleton__content": 20,
".edit-widgets-header": 30,
".block-library-button__inline-link .block-editor-url-input__suggestions": 6, // URL suggestions for button block above sibling inserter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,16 @@ import { useCallback, useLayoutEffect, useState } from '@wordpress/element';
import { store as blockEditorStore } from '../../store';
import { __unstableUseBlockElement as useBlockElement } from '../block-list/use-block-props/use-block-refs';

const COMMON_PROPS = {
placement: 'top-start',
};
const TOOLBAR_MARGIN = 12;

// By default the toolbar sets the `shift` prop. If the user scrolls the page
// down the toolbar will stay on screen by adopting a sticky position at the
// top of the viewport.
// By default the toolbar sets the `shift` prop and flip properties.
// If there is enough room, then once the block scrolls past the top of the screen,
// then the toolbar is flipped to the bottom.
const DEFAULT_PROPS = {
...COMMON_PROPS,
flip: false,
shift: true,
};

// When there isn't enough height between the top of the block and the editor
// canvas, the `shift` prop is set to `false`, as it will cause the block to be
// obscured. The `flip` behavior is enabled, which positions the toolbar below
// the block. This only happens if the block is smaller than the viewport, as
// otherwise the toolbar will be off-screen.
const RESTRICTED_HEIGHT_PROPS = {
...COMMON_PROPS,
placement: 'top-start',
strategy: 'fixed',
flip: true,
shift: false,
shift: true,
};

/**
Expand All @@ -50,22 +38,27 @@ function getProps( contentElement, selectedBlockElement, toolbarHeight ) {
}

const blockRect = selectedBlockElement.getBoundingClientRect();
const contentRect = contentElement.getBoundingClientRect();

// The document element's clientHeight represents the viewport height.
const viewportHeight =
contentElement.ownerDocument.documentElement.clientHeight;

const hasSpaceForToolbarAbove =
blockRect.top - contentRect.top > toolbarHeight;
const isBlockTallerThanViewport =
blockRect.height > viewportHeight - toolbarHeight;

if ( hasSpaceForToolbarAbove || isBlockTallerThanViewport ) {
return DEFAULT_PROPS;
if ( isBlockTallerThanViewport ) {
return {
...DEFAULT_PROPS,
flip: false,
shift: { padding: toolbarHeight - TOOLBAR_MARGIN },
};
}

return RESTRICTED_HEIGHT_PROPS;
return {
...DEFAULT_PROPS,
flip: { padding: toolbarHeight },
shift: { padding: toolbarHeight - TOOLBAR_MARGIN },
};
}

/**
Expand Down
1 change: 1 addition & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
- `ResizableBox`: Prevent unnecessary paint on resize handles ([#46196](https://github.com/WordPress/gutenberg/pull/46196)).
- `Popover`: Prevent unnecessary paint caused by using outline ([#46201](https://github.com/WordPress/gutenberg/pull/46201)).
- `PaletteEdit`: Global styles: add onChange actions to color palette items [#45681](https://github.com/WordPress/gutenberg/pull/45681).
- `Popover`: Add support for padding in `flip` and `shift` props, add `strategy` prop ([#46085](https://github.com/WordPress/gutenberg/pull/46085)).

### Experimental

Expand Down
20 changes: 19 additions & 1 deletion packages/components/src/popover/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,14 +125,16 @@ Show the popover fullscreen on mobile viewports.

- Required: No

### `flip`: `boolean`
### `flip`: `FlipProps | boolean`

Specifies whether the popover should flip across its axis if there isn't space for it in the normal placement.

When the using a 'top' placement, the popover will switch to a 'bottom' placement. When using a 'left' placement, the popover will switch to a `right' placement.

The popover will retain its alignment of 'start' or 'end' when flipping.

When passed an object, the amount of padding before flipping can be set using a `padding` property.

- Required: No
- Default: `true`

Expand Down Expand Up @@ -224,6 +226,22 @@ Adjusts the size of the popover to prevent its contents from going out of view w
- Required: No
- Default: `true`

### `shift`: `ShiftProps | boolean`

Enables the popover to shift in order to stay in view when meeting the viewport edges.

When passed an object, the amount of padding before shifting can be set using a `padding` property.

- Required: No
- Default: `true`

### `strategy`: `'absolute' | 'fixed'`

Sets the type of CSS position property to use for the popover.

- Required: No
- Default: `'absolute'`

### `variant`: `'toolbar' | 'unstyled'`

Specifies the popover's style.
Expand Down
13 changes: 11 additions & 2 deletions packages/components/src/popover/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ const UnforwardedPopover = (
resize = true,
shift = false,
variant,
strategy: strategyProp = 'absolute',

// Deprecated props
__unstableForcePosition,
Expand Down Expand Up @@ -291,7 +292,14 @@ const UnforwardedPopover = (
},
},
offsetMiddleware( offsetProp ),
computedFlipProp ? flipMiddleware() : undefined,
computedFlipProp
? flipMiddleware( {
padding:
typeof computedFlipProp === 'object'
? computedFlipProp?.padding
: undefined,
} )
: undefined,
computedResizeProp
? size( {
apply( sizeProps ) {
Expand All @@ -314,7 +322,7 @@ const UnforwardedPopover = (
? shiftMiddleware( {
crossAxis: true,
limiter: customLimitShift(),
padding: 1, // Necessary to avoid flickering at the edge of the viewport.
padding: typeof shift === 'object' ? shift?.padding : 1, // Necessary to avoid flickering at the edge of the viewport.
} )
: undefined,
arrow( { element: arrowRef } ),
Expand Down Expand Up @@ -362,6 +370,7 @@ const UnforwardedPopover = (
middlewareData: { arrow: arrowData },
} = useFloating( {
placement: normalizedPlacementFromProps,
strategy: strategyProp,
middleware,
whileElementsMounted: ( referenceParam, floatingParam, updateParam ) =>
autoUpdate( referenceParam, floatingParam, updateParam, {
Expand Down
18 changes: 16 additions & 2 deletions packages/components/src/popover/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ export type VirtualElement = Pick< Element, 'getBoundingClientRect' > & {
ownerDocument?: Document;
};

export type FlipProps = {
padding?: number;
};

export type ShiftProps = {
padding?: number;
};

export type PopoverProps = {
/**
* The name of the Slot in which the popover should be rendered. It should
Expand Down Expand Up @@ -67,7 +75,7 @@ export type PopoverProps = {
*
* @default true
*/
flip?: boolean;
flip?: FlipProps | boolean;
/**
* By default, the _first tabbable element_ in the popover will receive focus
* when it mounts. This is the same as setting this prop to `"firstElement"`.
Expand Down Expand Up @@ -133,7 +141,13 @@ export type PopoverProps = {
*
* @default false
*/
shift?: boolean;
shift?: ShiftProps | boolean;
/**
* Sets the type of CSS position property to use.
*
* @default 'absolute'
*/
strategy?: 'absolute' | 'fixed';
/**
* Specifies the popover's style.
*
Expand Down
1 change: 1 addition & 0 deletions packages/edit-site/src/components/layout/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
grid-area: header;
height: $header-height;
display: flex;
z-index: z-index(".edit-site-layout__header");
}

.edit-site-layout__logo {
Expand Down
1 change: 1 addition & 0 deletions packages/edit-widgets/src/components/header/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
display: flex;
align-items: center;
justify-content: space-between;
background: $white;
height: $header-height;
padding: 0 $grid-unit-20;
overflow: auto;
Expand Down