import * as _ from 'lodash'; import * as React from 'react'; import { Link as ReactRounterLink } from 'react-router-dom'; import { Link as ScrollLink } from 'react-scroll'; import { LinkType } from '../types'; import { constants } from '../utils/constants'; export interface LinkProps { to: string; type?: LinkType; shouldOpenInNewTab?: boolean; style?: React.CSSProperties; className?: string; onMouseOver?: (event: React.MouseEvent) => void; onMouseLeave?: (event: React.MouseEvent) => void; onMouseEnter?: (event: React.MouseEvent) => void; containerId?: string; } export interface LinkState {} /** * A generic link component which let's the developer render internal, external and scroll-to-hash links, and * their associated behaviors with a single link component. Many times we want a menu including a combination of * internal, external and scroll links and the abstraction of the differences of rendering each types of link * makes it much easier to do so. */ export class Link extends React.Component { public static defaultProps: Partial = { type: LinkType.ReactRoute, shouldOpenInNewTab: false, style: {}, className: '', onMouseOver: _.noop.bind(_), onMouseLeave: _.noop.bind(_), onMouseEnter: _.noop.bind(_), containerId: constants.DOCS_CONTAINER_ID, }; private _outerReactScrollSpan: HTMLSpanElement | null; constructor(props: LinkProps) { super(props); this._outerReactScrollSpan = null; } public render(): React.ReactNode { const styleWithDefault = { textDecoration: 'none', cursor: 'pointer', ...this.props.style, }; switch (this.props.type) { case LinkType.External: return ( {this.props.children} ); case LinkType.ReactRoute: return ( {this.props.children} ); case LinkType.ReactScroll: return ( (this._outerReactScrollSpan = input)} onMouseOver={this.props.onMouseOver} onMouseEnter={this.props.onMouseEnter} onMouseLeave={this.props.onMouseLeave} > {this.props.children} ); default: throw new Error(`Unrecognized LinkType: ${this.props.type}`); } } // HACK(fabio): For some reason, the react-scroll link decided to stop the propagation of click events. // We do however rely on these events being propagated in certain scenarios (e.g when the link // is within a dropdown we want to close upon being clicked). Because of this, we registry the // click event of an inner span, and pass it around the react-scroll link to an outer span. private _onClickPropagateClickEventAroundScrollLink(): void { if (!_.isNull(this._outerReactScrollSpan)) { this._outerReactScrollSpan.click(); } } }