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

224 lines
5.7 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';
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,
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,
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
const disable = disabled;
2021-06-18 00:52:21 +02:00
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={size} />}
</span>
);
2020-06-04 16:56:57 +02:00
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
target="_blank"
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;
}
}
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;