// @flow import type { Node } from 'react'; import React, { forwardRef, useRef } from 'react'; import Icon from 'component/common/icon'; import classnames from 'classnames'; import { NavLink } from 'react-router-dom'; import { formatLbryUrlForWeb } from 'util/url'; import useCombinedRefs from 'effects/use-combined-refs'; type Props = { id: ?string, href: ?string, title: ?string, label: ?string, largestLabel: ?string, icon: ?string, iconRight: ?string, disabled: ?boolean, children: ?Node, navigate: ?string, className: ?string, description: ?string, type: string, button: ?string, // primary, secondary, alt, link iconSize?: number, iconColor?: string, activeClass?: string, innerRef: ?any, authSrc?: string, // Events onClick: ?(any) => any, onMouseEnter: ?(any) => any, onMouseLeave: ?(any) => any, pathname: string, emailVerified: boolean, myref: any, dispatch: any, 'aria-label'?: string, user: ?User, }; // use forwardRef to allow consumers to pass refs to the button content if they want to // flow requires forwardRef have default type arguments passed to it const Button = forwardRef<any, {}>((props: Props, ref: any) => { const { type = 'button', onClick, href, title, label, largestLabel, icon, // This should rarely be used. Regular buttons should just use `icon` // `iconRight` is used for the header (home) button with the LBRY icon and external links that are displayed inline iconRight, disabled, children, navigate, className, description, button, iconSize, iconColor, activeClass, emailVerified, myref, dispatch, // <button> doesn't know what to do with dispatch pathname, user, authSrc, ...otherProps } = props; const disable = disabled; const combinedClassName = classnames( 'button', button ? { 'button--primary': button === 'primary', 'button--secondary': button === 'secondary', 'button--alt': button === 'alt', 'button--inverse': button === 'inverse', 'button--close': button === 'close', 'button--disabled': disable, 'button--link': button === 'link', } : 'button--no-style', className ); const innerRef = useRef(null); const combinedRef = useCombinedRefs(ref, innerRef, myref); const size = iconSize || (!label && !children) ? 18 : undefined; // Fall back to default // Label can be a string or object ( use title instead ) const ariaLabel = description || (typeof label === 'string' ? label : title); const content = ( <span className="button__content"> {icon && <Icon icon={icon} iconColor={iconColor} size={iconSize} />} {!largestLabel && label && ( <span dir="auto" className="button__label"> {label} </span> )} {/* largestLabel is used when a single button has two different labels based on hover state */} {largestLabel && ( <div dir="auto" className="button__label" style={{ position: 'relative' }}> <div style={{ position: 'relative', left: '50%', top: '50%', transform: `translate(-50%, 0%)`, }} > <span style={{ visibility: 'hidden' }}> {largestLabel || label} <div style={{ position: 'absolute', left: '50%', top: '50%', transform: `translate(-50%, -50%)`, }} > <span style={{ visibility: 'visible' }}>{label}</span> </div> </span> </div> </div> )} {children && children} {iconRight && <Icon icon={iconRight} iconColor={iconColor} size={size} />} </span> ); if (href || (navigate && navigate.startsWith('http'))) { // TODO: replace the below with an outbound link tracker for matomo return ( <a target="_blank" rel="noopener noreferrer" href={href || navigate} className={combinedClassName} title={title} onClick={onClick} aria-label={ariaLabel} disabled={disabled} // is there a reason this wasn't here before? > {content} </a> ); } // Handle lbry:// uris passed in, or already formatted web urls let path = navigate; if (path) { if (path.startsWith('lbry://')) { path = formatLbryUrlForWeb(path); } else if (!path.startsWith('/')) { // Force a leading slash so new paths aren't appended on to the current path path = `/${path}`; } } // Try to generate a tooltip using available text and display it through // the 'title' mechanism, but only if it isn't being used. let defaultTooltip; if (!title) { if (props['aria-label']) { defaultTooltip = props['aria-label']; } else if (description) { defaultTooltip = description; } } return path ? ( <NavLink exact to={path} title={title || defaultTooltip} disabled={disable} onClick={(e) => { e.stopPropagation(); if (onClick) { onClick(); } }} className={combinedClassName} activeClassName={activeClass} aria-label={ariaLabel} {...otherProps} > {content} </NavLink> ) : ( <button ref={combinedRef} title={title || defaultTooltip} aria-label={ariaLabel} className={combinedClassName} onClick={(e) => { if (onClick) { e.stopPropagation(); onClick(e); } }} disabled={disable} type={type} {...otherProps} > {content} </button> ); }); export default Button;