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

Popover: fix arrow placement and design #42874

Merged
merged 6 commits into from
Aug 2, 2022
Merged
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/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- `BorderControl`: Ensure box-sizing is reset for the control ([#42754](https://github.com/WordPress/gutenberg/pull/42754)).
- `InputControl`: Fix acceptance of falsy values in controlled updates ([#42484](https://github.com/WordPress/gutenberg/pull/42484/)).
- `Tooltip (Experimental)`, `CustomSelectControl`, `TimePicker`: Add missing font-size styles which were necessary in non-WordPress contexts ([#42844](https://github.com/WordPress/gutenberg/pull/42844/)).
- `Popover`: fix arrow placement and design ([#42874](https://github.com/WordPress/gutenberg/pull/42874/)).

### Enhancements

Expand Down
58 changes: 39 additions & 19 deletions packages/components/src/popover/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
} from '@wordpress/compose';
import { close } from '@wordpress/icons';
import deprecated from '@wordpress/deprecated';
import { Path, SVG } from '@wordpress/primitives';

/**
* Internal dependencies
Expand All @@ -48,6 +49,30 @@ import { getAnimateClassName } from '../animate';
*/
const SLOT_NAME = 'Popover';

// An SVG displaying a triangle facing down, filled with a solid
// color and bordered in such a way to create an arrow-like effect.
// Keeping the SVG's viewbox squared simplify the arrow positioning
// calculations.
const ArrowTriangle = ( props ) => (
<SVG
{ ...props }
xmlns="http://www.w3.org/2000/svg"
viewBox={ `0 0 100 100` }
className="components-popover__triangle"
role="presentation"
>
<Path
className="components-popover__triangle-bg"
d="M 0 0 L 50 50 L 100 0"
/>
<Path
className="components-popover__triangle-border"
d="M 0 0 L 50 50 L 100 0"
vectorEffect="non-scaling-stroke"
/>
</SVG>
);

const slotNameContext = createContext();

const positionToPlacement = ( position ) => {
Expand Down Expand Up @@ -266,12 +291,7 @@ const Popover = (
placement: usedPlacement,
middleware: middlewares,
} );
const staticSide = {
top: 'bottom',
right: 'left',
bottom: 'top',
left: 'right',
}[ placementData.split( '-' )[ 0 ] ];

const mergedRefs = useMergeRefs( [ floating, dialogRef, ref ] );

// Updates references
Expand Down Expand Up @@ -407,22 +427,22 @@ const Popover = (
<div className="components-popover__content">{ children }</div>
{ hasArrow && (
<div
className="components-popover__arrow"
ref={ arrowRef }
className={ [
'components-popover__arrow',
`is-${ placementData.split( '-' )[ 0 ] }`,
].join( ' ' ) }
style={ {
left:
! arrowData?.x || Number.isNaN( arrowData?.x )
? 0
: arrowData.x,
top:
! arrowData?.y || Number.isNaN( arrowData?.y )
? 0
: arrowData.y,
right: undefined,
bottom: undefined,
[ staticSide ]: '-4px',
left: Number.isFinite( arrowData?.x )
? `${ arrowData.x }px`
: '',
top: Number.isFinite( arrowData?.y )
? `${ arrowData.y }px`
: '',
} }
/>
>
<ArrowTriangle />
</div>
) }
</div>
);
Expand Down
66 changes: 60 additions & 6 deletions packages/components/src/popover/style.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
$arrow-triangle-base-size: 14px;

.components-popover {
z-index: z-index(".components-popover");

Expand Down Expand Up @@ -60,12 +62,64 @@

.components-popover__arrow {
position: absolute;
background: $gray-400;
width: 8px;
height: 8px;
transform: rotate(45deg);
z-index: -1;
width: $arrow-triangle-base-size;
height: $arrow-triangle-base-size;
pointer-events: none;

// Thin line that helps to make sure that the underlying
// popover__content's outline is fully overlapped by the
// arrow
&::before {
content: "";
position: absolute;
top: -1px;
left: 0;
height: 2px;
width: 100%;
background-color: $white;
}

// Position and rotate the arrow depending on the popover's placement.
// The `!important' is necessary to override the inline styles.
&.is-top {
bottom: -1 * $arrow-triangle-base-size !important;
transform: rotate(0);
}
&.is-right {
/*rtl:begin:ignore*/
left: -1 * $arrow-triangle-base-size !important;
transform: rotate(90deg);
}
&.is-bottom {
top: -1 * $arrow-triangle-base-size !important;
transform: rotate(180deg);
}
&.is-left {
/*rtl:begin:ignore*/
right: -1 * $arrow-triangle-base-size !important;
transform: rotate(-90deg);
/*rtl:end:ignore*/
}
}

.components-popover__triangle {
position: absolute;
height: 100%;
width: 100%;
}

.components-popover__triangle-bg {
// Fill color is the same as the .components-popover__content's background
fill: $white;
}

.components-popover__triangle-border {
// Stroke colors are the same as the .components-popover__content's outline
fill: transparent;
stroke-width: $border-width;
stroke: $gray-400;

.is-alternate & {
background: $gray-900;
stroke: $gray-900;
}
}