UNPKG

box-ui-elements-mlh

Version:
145 lines (131 loc) 4.22 kB
// @flow import * as React from 'react'; import uniqueId from 'lodash/uniqueId'; import TetherComponent from 'react-tether'; import './RadarAnimation.scss'; const BOTTOM_CENTER = 'bottom-center'; const BOTTOM_LEFT = 'bottom-left'; const BOTTOM_RIGHT = 'bottom-right'; const MIDDLE_CENTER = 'middle-center'; const MIDDLE_LEFT = 'middle-left'; const MIDDLE_RIGHT = 'middle-right'; const TOP_CENTER = 'top-center'; const TOP_LEFT = 'top-left'; const TOP_RIGHT = 'top-right'; const positions = { [BOTTOM_CENTER]: { attachment: 'top center', targetAttachment: 'bottom center', }, [BOTTOM_LEFT]: { attachment: 'top left', targetAttachment: 'bottom left', }, [BOTTOM_RIGHT]: { attachment: 'top right', targetAttachment: 'bottom right', }, [MIDDLE_CENTER]: { attachment: 'middle center', targetAttachment: 'middle center', }, [MIDDLE_LEFT]: { attachment: 'middle right', targetAttachment: 'middle left', }, [MIDDLE_RIGHT]: { attachment: 'middle left', targetAttachment: 'middle right', }, [TOP_CENTER]: { attachment: 'bottom center', targetAttachment: 'top center', }, [TOP_LEFT]: { attachment: 'bottom left', targetAttachment: 'top left', }, [TOP_RIGHT]: { attachment: 'bottom right', targetAttachment: 'top right', }, }; export type Position = $Keys<typeof positions>; type Props = { /** A React element to put the radar on */ children: React.Element<any>, /** A CSS class for the radar */ className?: string, /** Whether to constrain the radar to the element's scroll parent. Defaults to `false` */ constrainToScrollParent: boolean, /** Whether to constrain the radar to window. Defaults to `true` */ constrainToWindow: boolean, /** Forces the radar to be shown or hidden - defaults to true */ isShown: boolean, /** A string of the form 'vert-offset horiz-offset' which controls positioning */ offset?: string, /** Where to position the radar relative to the wrapped component */ position: Position, }; class RadarAnimation extends React.Component<Props> { static defaultProps = { constrainToScrollParent: false, constrainToWindow: true, isShown: true, position: MIDDLE_RIGHT, }; tetherRef = React.createRef<{ position: () => {} }>(); radarAnimationID = uniqueId('radarAnimation'); // Instance API: Forces the radar to be repositioned position = () => { const { isShown } = this.props; if (this.tetherRef.current && isShown) { this.tetherRef.current.position(); } }; render() { const { children, className = '', constrainToScrollParent, constrainToWindow, position, isShown, ...rest } = this.props; const constraints = []; if (constrainToScrollParent) { constraints.push({ to: 'scrollParent', attachment: 'together', }); } if (constrainToWindow) { constraints.push({ to: 'window', attachment: 'together', }); } const tetherPosition = positions[position]; return ( <TetherComponent attachment={tetherPosition.attachment} classPrefix="radar-animation" constraints={constraints} targetAttachment={tetherPosition.targetAttachment} ref={this.tetherRef} > {React.cloneElement(React.Children.only(children), { 'aria-describedby': this.radarAnimationID, })} {isShown && ( <div className={`radar ${className}`} id={this.radarAnimationID} {...rest}> <div className="radar-dot" /> <div className="radar-circle" /> </div> )} </TetherComponent> ); } } export default RadarAnimation;