Make popover hide delay configurable to enable better UX

This commit is contained in:
Dominik Prokop 2019-01-22 09:10:47 +01:00
parent 2b34933554
commit d86d063900
4 changed files with 64 additions and 45 deletions

View File

@ -37,7 +37,7 @@ export const colorPickerFactory = <T extends ColorPickerProps>(
};
return (
<PopperController content={popoverElement} placement="bottom-start">
<PopperController content={popoverElement} hideAfter={500}>
{(showPopper, hidePopper, popperProps) => {
return (
<>
@ -79,5 +79,5 @@ export const colorPickerFactory = <T extends ColorPickerProps>(
};
};
export const ColorPicker = colorPickerFactory(ColorPickerPopover, 'ColorPicker');
export const ColorPicker = colorPickerFactory(ColorPickerPopover, 'ColorPicker');
export const SeriesColorPicker = colorPickerFactory(SeriesColorPickerPopover, 'SeriesColorPicker');

View File

@ -6,7 +6,6 @@ import { ColorPickerProps } from './ColorPicker';
import { GrafanaTheme, Themeable } from '../../types';
import { PopperContentProps } from '../Tooltip/PopperController';
// const DEFAULT_COLOR = '#000000';
export interface Props extends ColorPickerProps, Themeable, PopperContentProps {}

View File

@ -13,8 +13,8 @@ const defaultTransitionStyles = {
const transitionStyles: { [key: string]: object } = {
exited: { opacity: 0 },
entering: { opacity: 0 },
entered: { opacity: 1 },
exiting: { opacity: 0 },
entered: { opacity: 1, transitionDelay: '0s' },
exiting: { opacity: 0, transitionDelay: '500ms' },
};
export type RenderPopperArrowFn = (
@ -41,46 +41,48 @@ class Popper extends PureComponent<Props> {
return (
<Manager>
<Transition in={show} timeout={100} mountOnEnter={true} unmountOnExit={true}>
{transitionState => (
<Portal>
<ReactPopper
placement={placement}
referenceElement={this.props.referenceElement}
// TODO: move modifiers config to popper controller
modifiers={{ preventOverflow: { enabled: true, boundariesElement: 'window' } }}
>
{({ ref, style, placement, arrowProps, scheduleUpdate }) => {
return (
<div
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
ref={ref}
style={{
...style,
...defaultTransitionStyles,
...transitionStyles[transitionState],
}}
data-placement={placement}
className={`${wrapperClassName}`}
>
<div className={className}>
{typeof content === 'string'
? content
: React.cloneElement(content, {
updatePopperPosition: scheduleUpdate,
{transitionState => {
return (
<Portal>
<ReactPopper
placement={placement}
referenceElement={this.props.referenceElement}
// TODO: move modifiers config to popper controller
modifiers={{ preventOverflow: { enabled: true, boundariesElement: 'window' } }}
>
{({ ref, style, placement, arrowProps, scheduleUpdate }) => {
return (
<div
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
ref={ref}
style={{
...style,
...defaultTransitionStyles,
...transitionStyles[transitionState],
}}
data-placement={placement}
className={`${wrapperClassName}`}
>
<div className={className}>
{typeof content === 'string'
? content
: React.cloneElement(content, {
updatePopperPosition: scheduleUpdate,
})}
{renderArrow &&
renderArrow({
arrowProps,
placement,
})}
{renderArrow &&
renderArrow({
arrowProps,
placement,
})}
</div>
</div>
</div>
);
}}
</ReactPopper>
</Portal>
)}
);
}}
</ReactPopper>
</Portal>
);
}}
</Transition>
</Manager>
);

View File

@ -1,9 +1,11 @@
import React from 'react';
import * as PopperJS from 'popper.js';
// This API allows popovers to update Popper's position when e.g. popover content chaanges
// This API allows popovers to update Popper's position when e.g. popover content changes
// updatePopperPosition is delivered to content by react-popper
export interface PopperContentProps { updatePopperPosition?: () => void; }
export interface PopperContentProps {
updatePopperPosition?: () => void;
}
export type PopperContent<T extends PopperContentProps> = string | React.ReactElement<T>;
@ -29,6 +31,7 @@ interface Props {
content: PopperContent<any>;
className?: string;
children: PopperControllerRenderProp;
hideAfter?: number;
}
interface State {
@ -37,6 +40,8 @@ interface State {
}
class PopperController extends React.Component<Props, State> {
private hideTimeout: any;
constructor(props: Props) {
super(props);
@ -58,6 +63,10 @@ class PopperController extends React.Component<Props, State> {
}
showPopper = () => {
if (this.hideTimeout) {
clearTimeout(this.hideTimeout);
}
this.setState(prevState => ({
...prevState,
show: true,
@ -65,6 +74,15 @@ class PopperController extends React.Component<Props, State> {
};
hidePopper = () => {
if (this.props.hideAfter !== 0) {
this.hideTimeout = setTimeout(() => {
this.setState(prevState => ({
...prevState,
show: false,
}));
}, this.props.hideAfter);
return;
}
this.setState(prevState => ({
...prevState,
show: false,