UNPKG

wix-style-react

Version:
254 lines (201 loc) • 7.21 kB
import React from 'react'; import { Popover as CorePopover } from 'wix-ui-core/dist/src/components/popover'; import { buildChildrenObject } from 'wix-ui-core/dist/src/utils'; import requestAnimationFramePolyfill from '../utils/request-animation-frame'; import PropTypes from 'prop-types'; import { st, classes } from './Popover.st.css'; import { FontUpgradeContext } from '../FontUpgrade/context'; import FontUpgrade from '../FontUpgrade'; import { ThemeProviderConsumerBackwardCompatible } from '../ThemeProvider/ThemeProviderConsumerBackwardCompatible'; import { placements } from './constants'; export { placements }; /** * This has been added in order to fix jsdom not having requestAnimation frame * installed. Jest by default has this polyfilled, but mocha fails on it. * Decided with Shlomi to get rid of this on next major version 7, where we will support * only jest. */ if (process.env.NODE_ENV === 'test') { requestAnimationFramePolyfill.install(); } const ANIMATION_ENTER = 150; const ANIMATION_EXIT = 100; const ContentElement = ({ children }) => { return ( <FontUpgradeContext.Consumer> {({ active }) => { return ( <FontUpgrade active={!!active}> <CorePopover.Content children={children} /> </FontUpgrade> ); }} </FontUpgradeContext.Consumer> ); }; ContentElement.displayName = 'Popover.Content'; class Popover extends React.Component { static displayName = 'Popover'; static Element = CorePopover.Element; static Content = ContentElement; static propTypes = { /** The Popover's placement: * * auto-start * * auto * * auto-end * * top-start * * top * * top-end * * right-start * * right * * right-end * * bottom-end * * bottom * * bottom-start * * left-end * * left * * left-start */ placement: PropTypes.oneOf(placements), /** Is the content shown or not */ shown: PropTypes.bool, /** Enables calculations in relation to a dom element */ appendTo: PropTypes.oneOf(['window', 'scrollParent', 'parent', 'viewport']), /** custom classname */ className: PropTypes.string, /** the classname to be passed to the popover's content container */ contentClassName: PropTypes.string, /** Custom arrow element */ customArrow: PropTypes.func, /** Breaking change: When true - onClickOutside will be called only when popover content is shown */ disableClickOutsideWhenClosed: PropTypes.bool, /** popovers content is set to minnimum width of trigger element, but it can expand up to the value of maxWidth. */ dynamicWidth: PropTypes.bool, /** Clicking on elements with this excluded class will will not trigger onClickOutside callback */ excludeClass: PropTypes.string, /** * Whether to enable the fixed behaviour. This behaviour is used to keep the `<Popover/>` at it's * original placement even when it's being positioned outside the boundary. */ fixed: PropTypes.bool, /** * Whether to enable the flip behaviour. This behaviour is used to flip the `<Popover/>`'s placement * when it starts to overlap the target element (`<Popover.Element/>`). */ flip: PropTypes.bool, /* stretch trigger element to the width of its container. */ fluid: PropTypes.bool, /** Hide Delay in ms */ hideDelay: PropTypes.number, /** Id */ id: PropTypes.string, /** * popover content maxWidth value * - `number` value which converts to css with `px` * - `string` value that contains `px` */ maxWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), /** * popover content minWidth value * - `number` value which converts to css with `px` * - `string` value that contains `px` */ minWidth: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), /** Moves arrow by amount */ moveArrowTo: PropTypes.number, /** Moves popover relative to the parent */ moveBy: PropTypes.shape({ x: PropTypes.number, y: PropTypes.number }), /** onClick on the component */ onClick: PropTypes.func, /** Provides callback to invoke when clicked outside of the popover */ onClickOutside: PropTypes.func, /** onKeyDown on the target component */ onKeyDown: PropTypes.func, /** onMouseEnter on the component */ onMouseEnter: PropTypes.func, /** onMouseLeave on the component */ onMouseLeave: PropTypes.func, /** target element role value */ role: PropTypes.string, /** Show show arrow from the content */ showArrow: PropTypes.bool, /** Show Delay in ms */ showDelay: PropTypes.number, /** Inline style */ style: PropTypes.object, /** Animation timer */ timeout: PropTypes.oneOfType([ PropTypes.number, PropTypes.shape({ enter: PropTypes.number, exit: PropTypes.number }), ]), /** * popover content width value * - `number` value which converts to css with `px` * - `string` value that contains `px` */ width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), /** popover z-index */ zIndex: PropTypes.number, /** Applied as data-hook HTML attribute that can be used in the tests */ dataHook: PropTypes.string, /** Adds enter and exit animation */ animate: PropTypes.bool, /** The theme of the popover */ theme: PropTypes.oneOf(['dark', 'light']), children: (props, propName) => { const childrenArr = React.Children.toArray(props[propName]); const childrenObj = buildChildrenObject(childrenArr, { Element: null, Content: null, }); if (!childrenObj.Element) { return new Error( 'Invalid children provided, <Popover.Element/> must be provided', ); } if (!childrenObj.Content) { return new Error( 'Invalid children provided, <Popover.Content/> must be provided', ); } return childrenArr.reduce((err, child) => { if ( !err && child.type.displayName !== 'Popover.Element' && child.type.displayName !== 'Popover.Content' ) { return new Error( `Invalid children provided, unknown child <${ child.type.displayName || child.type }/> supplied`, ); } return err; }, false); }, }; static defaultProps = { appendTo: 'parent', animate: false, }; render() { const { dataHook, animate, theme, className, ...rest } = this.props; const timeout = animate ? { enter: ANIMATION_ENTER, exit: ANIMATION_EXIT } : undefined; return ( <ThemeProviderConsumerBackwardCompatible> {({ className: themeClassName }) => ( <CorePopover disableClickOutsideWhenClosed timeout={timeout} data-hook={dataHook} {...rest} className={st(classes.root, { theme }, className, themeClassName)} /> )} </ThemeProviderConsumerBackwardCompatible> ); } } export default Popover;