lbry-desktop/ui/component/button/view.jsx

262 lines
6.8 KiB
React
Raw Normal View History

2018-03-26 23:32:43 +02:00
// @flow
import type { Node } from 'react';
2019-11-15 22:53:31 +01:00
import React, { forwardRef, useRef } from 'react';
2018-03-26 23:32:43 +02:00
import Icon from 'component/common/icon';
import classnames from 'classnames';
2019-04-04 23:05:23 +02:00
import { NavLink } from 'react-router-dom';
import { formatLbryUrlForWeb } from 'util/url';
import * as PAGES from 'constants/pages';
2019-11-15 22:53:31 +01:00
import useCombinedRefs from 'effects/use-combined-refs';
2018-03-26 23:32:43 +02:00
type Props = {
id: ?string,
2018-03-26 23:32:43 +02:00
href: ?string,
title: ?string,
label: ?string,
largestLabel: ?string,
2018-03-26 23:32:43 +02:00
icon: ?string,
iconRight: ?string,
disabled: ?boolean,
children: ?Node,
2018-03-26 23:32:43 +02:00
navigate: ?string,
className: ?string,
description: ?string,
type: string,
button: ?string, // primary, secondary, alt, link
iconSize?: number,
iconColor?: string,
2019-03-28 17:53:13 +01:00
activeClass?: string,
2019-06-05 08:07:53 +02:00
innerRef: ?any,
authSrc?: string,
2019-06-05 08:07:53 +02:00
// Events
onClick: ?(any) => any,
onMouseEnter: ?(any) => any,
onMouseLeave: ?(any) => any,
pathname: string,
emailVerified: boolean,
requiresAuth: ?boolean,
myref: any,
dispatch: any,
'aria-label'?: string,
2021-06-18 00:52:21 +02:00
user: ?User,
2018-03-26 23:32:43 +02:00
};
// 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,
requiresAuth,
myref,
dispatch, // <button> doesn't know what to do with dispatch
pathname,
2021-06-18 00:52:21 +02:00
user,
authSrc,
...otherProps
} = props;
2018-03-26 23:32:43 +02:00
2021-06-18 00:52:21 +02:00
const disable = disabled || (user === null && requiresAuth);
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',
2021-06-18 00:52:21 +02:00
'button--disabled': disable,
'button--link': button === 'link',
}
: 'button--no-style',
className
);
2018-03-26 23:32:43 +02:00
2019-11-15 22:53:31 +01:00
const innerRef = useRef(null);
const combinedRef = useCombinedRefs(ref, innerRef, myref);
const size = iconSize || (!label && !children) ? 18 : undefined; // Fall back to default
2019-11-15 22:53:31 +01:00
2021-07-13 22:19:52 +02:00
// Label can be a string or object ( use title instead )
const ariaLabel = description || (typeof label === 'string' ? label : title);
const content = (
2020-04-11 02:46:03 +02:00
<span className="button__content">
{icon && <Icon icon={icon} iconColor={iconColor} size={iconSize} />}
2020-04-11 02:24:23 +02:00
2020-09-23 17:00:10 +02:00
{!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' }}>
2020-04-11 02:24:23 +02:00
<div
style={{
position: 'relative',
left: '50%',
top: '50%',
transform: `translate(-50%, 0%)`,
}}
>
<span style={{ visibility: 'hidden' }}>
{largestLabel || label}
<div
style={{
2020-04-11 02:24:23 +02:00
position: 'absolute',
left: '50%',
2020-04-11 02:24:23 +02:00
top: '50%',
transform: `translate(-50%, -50%)`,
}}
>
<span style={{ visibility: 'visible' }}>{label}</span>
</div>
2020-04-11 02:24:23 +02:00
</span>
</div>
</div>
2020-04-11 02:24:23 +02:00
)}
{children && children}
{iconRight && <Icon icon={iconRight} iconColor={iconColor} size={iconSize || size} />}
</span>
);
2020-06-04 16:56:57 +02:00
// check if the link is for odysee.com
function isAnOdyseeLink(urlString) {
2022-03-25 18:57:24 +01:00
return (
urlString && (urlString.indexOf('https://odysee.com') !== -1 || urlString.indexOf('http://odysee.com') !== -1)
);
}
// if it's an internal link we won't open a new tab
const isAnInternalLink = (href || navigate) && (isAnOdyseeLink(href) || isAnOdyseeLink(navigate));
2020-09-23 17:00:10 +02:00
if (href || (navigate && navigate.startsWith('http'))) {
// TODO: replace the below with an outbound link tracker for matomo
return (
2020-09-23 17:00:10 +02:00
<a
2022-03-25 18:57:24 +01:00
target={isAnInternalLink ? '' : '_blank'}
2020-09-23 17:00:10 +02:00
rel="noopener noreferrer"
href={href || navigate}
className={combinedClassName}
title={title}
2020-09-23 17:00:10 +02:00
onClick={onClick}
aria-label={ariaLabel}
disabled={disabled} // is there a reason this wasn't here before?
2020-09-23 17:00:10 +02:00
>
{content}
2020-06-02 22:52:34 +02:00
</a>
2018-03-26 23:32:43 +02:00
);
}
2018-03-26 23:32:43 +02:00
// 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}`;
2019-03-28 17:53:13 +01:00
}
2018-03-26 23:32:43 +02:00
}
// 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;
}
}
if (requiresAuth && !emailVerified) {
let redirectUrl = `/$/${PAGES.AUTH}?redirect=${pathname}`;
if (authSrc) {
redirectUrl += `&src=${authSrc}`;
}
return (
<NavLink
exact
2021-06-18 00:52:21 +02:00
onClick={(e) => {
2019-10-23 20:59:33 +02:00
e.stopPropagation();
}}
to={redirectUrl}
title={title || defaultTooltip}
2021-06-18 00:52:21 +02:00
disabled={disable}
className={combinedClassName}
activeClassName={activeClass}
aria-label={ariaLabel}
>
{content}
</NavLink>
);
}
return path ? (
<NavLink
exact
to={path}
title={title || defaultTooltip}
2021-06-18 00:52:21 +02:00
disabled={disable}
onClick={(e) => {
e.stopPropagation();
if (onClick) {
onClick();
}
}}
className={combinedClassName}
activeClassName={activeClass}
aria-label={ariaLabel}
2020-01-06 19:32:35 +01:00
{...otherProps}
>
{content}
</NavLink>
) : (
<button
2019-11-15 22:53:31 +01:00
ref={combinedRef}
title={title || defaultTooltip}
2021-07-13 22:19:52 +02:00
aria-label={ariaLabel}
className={combinedClassName}
2021-06-18 00:52:21 +02:00
onClick={(e) => {
2019-08-13 07:35:13 +02:00
if (onClick) {
e.stopPropagation();
onClick(e);
}
}}
2021-06-18 00:52:21 +02:00
disabled={disable}
type={type}
{...otherProps}
>
{content}
</button>
);
});
2018-03-26 23:32:43 +02:00
export default Button;