-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Components: Show popovers fullscreen on mobile #3400
Changes from all commits
4defb1d
1e30431
a52de4f
d0e6c6a
8fa3cf5
5960fa3
063ea58
151066f
6ebaa82
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ import { focus, keycodes } from '@wordpress/utils'; | |
import './style.scss'; | ||
import withFocusReturn from '../higher-order/with-focus-return'; | ||
import PopoverDetectOutside from './detect-outside'; | ||
import IconButton from '../icon-button'; | ||
import { Slot, Fill } from '../slot-fill'; | ||
|
||
const FocusManaged = withFocusReturn( ( { children } ) => children ); | ||
|
@@ -35,6 +36,7 @@ const ARROW_OFFSET = 20; | |
* @type {String} | ||
*/ | ||
const SLOT_NAME = 'Popover'; | ||
const isMobile = () => window.innerWidth < 782; | ||
|
||
class Popover extends Component { | ||
constructor() { | ||
|
@@ -52,6 +54,7 @@ class Popover extends Component { | |
this.state = { | ||
forcedYAxis: null, | ||
forcedXAxis: null, | ||
isMobile: false, | ||
}; | ||
} | ||
|
||
|
@@ -156,9 +159,28 @@ class Popover extends Component { | |
} | ||
|
||
setOffset() { | ||
const { getAnchorRect = this.getAnchorRect } = this.props; | ||
const { getAnchorRect = this.getAnchorRect, expandOnMobile = false } = this.props; | ||
const { popover } = this.nodes; | ||
|
||
if ( isMobile() && expandOnMobile ) { | ||
popover.style.left = 0; | ||
popover.style.top = 0; | ||
popover.style.right = 0; | ||
popover.style.bottom = 0; | ||
if ( ! this.state.isMobile ) { | ||
this.setState( { | ||
isMobile: true, | ||
} ); | ||
} | ||
return; | ||
} | ||
|
||
if ( this.state.isMobile ) { | ||
this.setState( { | ||
isMobile: false, | ||
} ); | ||
} | ||
|
||
const [ yAxis, xAxis ] = this.getPositions(); | ||
const isTop = 'top' === yAxis; | ||
const isLeft = 'left' === xAxis; | ||
|
@@ -169,6 +191,9 @@ class Popover extends Component { | |
return; | ||
} | ||
|
||
popover.style.bottom = 'auto'; | ||
popover.style.right = 'auto'; | ||
|
||
if ( isRight ) { | ||
popover.style.left = rect.left + ARROW_OFFSET + 'px'; | ||
} else if ( isLeft ) { | ||
|
@@ -244,6 +269,7 @@ class Popover extends Component { | |
range, | ||
focusOnOpen, | ||
getAnchorRect, | ||
expandOnMobile, | ||
/* eslint-enable no-unused-vars */ | ||
...contentProps | ||
} = this.props; | ||
|
@@ -258,6 +284,9 @@ class Popover extends Component { | |
className, | ||
'is-' + yAxis, | ||
'is-' + xAxis, | ||
{ | ||
'is-mobile': this.state.isMobile, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this need to be state? Can't we just call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, because we need to rerender if it changes. |
||
} | ||
); | ||
|
||
// Disable reason: We care to capture the _bubbled_ events from inputs | ||
|
@@ -272,6 +301,11 @@ class Popover extends Component { | |
{ ...contentProps } | ||
onKeyDown={ this.maybeClose } | ||
> | ||
{ this.state.isMobile && ( | ||
<div className="components-popover__header"> | ||
<IconButton className="components-popover__close" icon="no-alt" onClick={ onClose } /> | ||
</div> | ||
) } | ||
<div | ||
ref={ this.bindNode( 'content' ) } | ||
className="components-popover__content" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,90 +3,122 @@ | |
z-index: z-index( ".components-popover" ); | ||
left: 50%; | ||
|
||
&:before { | ||
border: 8px solid $light-gray-500; | ||
} | ||
|
||
&:after { | ||
border: 8px solid $white; | ||
} | ||
|
||
&:before, | ||
&:after { | ||
content: ""; | ||
position: absolute; | ||
margin-left: -10px; | ||
height: 0; | ||
width: 0; | ||
border-left-color: transparent; | ||
border-right-color: transparent; | ||
line-height: 0; | ||
} | ||
|
||
&.is-top { | ||
bottom: 100%; | ||
margin-top: -8px; | ||
|
||
&:not(.is-mobile) { | ||
&:before { | ||
bottom: -8px; | ||
border: 8px solid $light-gray-500; | ||
} | ||
|
||
&:after { | ||
bottom: -6px; | ||
border: 8px solid $white; | ||
} | ||
|
||
&:before, | ||
&:after { | ||
border-top-style: solid; | ||
border-bottom: none; | ||
content: ""; | ||
position: absolute; | ||
margin-left: -10px; | ||
height: 0; | ||
width: 0; | ||
border-left-color: transparent; | ||
border-right-color: transparent; | ||
line-height: 0; | ||
} | ||
} | ||
|
||
&.is-bottom { | ||
top: 100%; | ||
margin-top: 8px; | ||
&.is-top { | ||
bottom: 100%; | ||
margin-top: -8px; | ||
|
||
&:before { | ||
top: -8px; | ||
} | ||
&:before { | ||
bottom: -8px; | ||
} | ||
|
||
&:after { | ||
top: -6px; | ||
&:after { | ||
bottom: -6px; | ||
} | ||
|
||
&:before, | ||
&:after { | ||
border-top-style: solid; | ||
border-bottom: none; | ||
} | ||
} | ||
|
||
&:before, | ||
&:after { | ||
border-bottom-style: solid; | ||
border-top: none; | ||
&.is-bottom { | ||
top: 100%; | ||
margin-top: 8px; | ||
|
||
&:before { | ||
top: -8px; | ||
} | ||
|
||
&:after { | ||
top: -6px; | ||
} | ||
|
||
&:before, | ||
&:after { | ||
border-bottom-style: solid; | ||
border-top: none; | ||
} | ||
} | ||
} | ||
} | ||
|
||
.components-popover__content { | ||
position: absolute; | ||
box-shadow: $shadow-popover; | ||
border: 1px solid $light-gray-500; | ||
background: $white; | ||
min-width: 240px; | ||
height: 100%; | ||
|
||
.components-popover.is-mobile & { | ||
height: calc( 100% - #{ $panel-header-height } ); | ||
border-top: 0; | ||
} | ||
|
||
.components-popover:not(.is-mobile) & { | ||
position: absolute; | ||
min-width: 240px; | ||
height: auto; | ||
} | ||
|
||
.components-popover.is-top & { | ||
.components-popover:not(.is-mobile).is-top & { | ||
bottom: 100%; | ||
} | ||
|
||
.components-popover.is-center & { | ||
.components-popover:not(.is-mobile).is-center & { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of using a series of :not(.is-mobile) and given that is the component itself that sets the is-mobile depending on a width. I think using a CSS rule that uses our variable $break-medium may be an option to simplify this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
left: 50%; | ||
transform: translateX( -50% ); | ||
} | ||
|
||
.components-popover.is-right & { | ||
.components-popover:not(.is-mobile).is-right & { | ||
position: absolute; | ||
left: 100%; | ||
margin-left: -24px; | ||
} | ||
|
||
.components-popover.is-left & { | ||
.components-popover:not(.is-mobile).is-left & { | ||
position: absolute; | ||
right: 100%; | ||
margin-right: -24px; | ||
} | ||
} | ||
|
||
// The withFocusReturn div | ||
.components-popover__content > div { | ||
height: 100%; | ||
} | ||
|
||
.components-popover__header { | ||
height: $panel-header-height; | ||
background: $light-gray-300; | ||
padding: 0 $panel-padding; | ||
border: 1px solid $light-gray-500; | ||
position: relative; | ||
} | ||
|
||
.components-popover__close.components-icon-button { | ||
position: absolute; | ||
top: 6px; | ||
right: 6px; | ||
z-index: z-index( '.components-popover__close' ); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is one more place to sync with our scss variables. I think for now it is ok but this logic could benefit from #3331 where we add a synchronization with our scss variables, and from #3332 where we add responsive-redux making is-mobile being a prop the component receives.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem is, the
Popover
is a generic component outside theeditor
module. I thought about factorizing this but we can't assume any context in thecomponents
module.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, I would say given this it would be better to simplify the component and just receive a showFullScreen and than is up the callers to decide if is mobile and the enable this option (maybe using responsive redux) or they don't enable this option. The downside is we would need to activate this option manually in the cases we want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's an option but I worry about having the component's user compute the
isMobile
on its own. I think this would create more duplication than it's supposed to avoid especially for third-party code or WP code no in theeditor
module.I personally don't mind the small duplication here to make it generic enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that is true it would require more effort from callers. In that case, another option would be instead of expandOnMobile we receive a prop showFullScreen that defaults to our isMobile function (If no value is passed). The advantage is that callers are able to use their own logic and their own breakpoint for mobile.