2020-04-19 17:12:55 +02:00
|
|
|
import React from 'react';
|
|
|
|
import PropTypes from 'prop-types';
|
|
|
|
import styled from 'styled-components';
|
|
|
|
import { Link } from 'react-router';
|
|
|
|
|
|
|
|
import { remSize, prop } from '../theme';
|
|
|
|
|
2020-04-27 10:12:34 +02:00
|
|
|
const kinds = {
|
|
|
|
block: 'block',
|
|
|
|
icon: 'icon',
|
|
|
|
inline: 'inline',
|
|
|
|
};
|
|
|
|
|
2020-04-19 17:12:55 +02:00
|
|
|
// The '&&&' will increase the specificity of the
|
|
|
|
// component's CSS so that it overrides the more
|
|
|
|
// general global styles
|
|
|
|
const StyledButton = styled.button`
|
|
|
|
&&& {
|
|
|
|
display: flex;
|
|
|
|
justify-content: center;
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
width: max-content;
|
|
|
|
text-decoration: none;
|
|
|
|
|
2020-05-03 11:54:57 +02:00
|
|
|
color: ${prop('Button.default.foreground')};
|
|
|
|
background-color: ${prop('Button.default.background')};
|
2020-04-19 17:12:55 +02:00
|
|
|
cursor: pointer;
|
2020-05-03 11:54:57 +02:00
|
|
|
border: 2px solid ${prop('Button.default.border')};
|
2020-04-19 17:12:55 +02:00
|
|
|
border-radius: 2px;
|
|
|
|
padding: ${remSize(8)} ${remSize(25)};
|
|
|
|
line-height: 1;
|
2020-05-03 11:54:57 +02:00
|
|
|
|
|
|
|
svg * {
|
|
|
|
fill: ${prop('Button.default.foreground')};
|
|
|
|
}
|
2020-04-19 17:12:55 +02:00
|
|
|
|
|
|
|
&:hover:not(:disabled) {
|
2020-05-03 11:54:57 +02:00
|
|
|
color: ${prop('Button.hover.foreground')};
|
|
|
|
background-color: ${prop('Button.hover.background')};
|
|
|
|
border-color: ${prop('Button.hover.border')};
|
|
|
|
|
|
|
|
svg * {
|
|
|
|
fill: ${prop('Button.hover.foreground')};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
&:active:not(:disabled) {
|
|
|
|
color: ${prop('Button.active.foreground')};
|
|
|
|
background-color: ${prop('Button.active.background')};
|
2020-04-26 15:32:50 +02:00
|
|
|
|
|
|
|
svg * {
|
2020-05-03 11:54:57 +02:00
|
|
|
fill: ${prop('Button.active.foreground')};
|
2020-04-26 15:32:50 +02:00
|
|
|
}
|
2020-04-19 17:12:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
&:disabled {
|
2020-05-03 11:54:57 +02:00
|
|
|
color: ${prop('Button.disabled.foreground')};
|
|
|
|
background-color: ${prop('Button.disabled.background')};
|
|
|
|
border-color: ${prop('Button.disabled.border')};
|
2020-04-19 17:12:55 +02:00
|
|
|
cursor: not-allowed;
|
2020-05-03 11:54:57 +02:00
|
|
|
|
|
|
|
svg * {
|
|
|
|
fill: ${prop('Button.disabled.foreground')};
|
|
|
|
}
|
2020-04-19 17:12:55 +02:00
|
|
|
}
|
2020-04-26 11:31:45 +02:00
|
|
|
|
2020-04-27 10:12:34 +02:00
|
|
|
> * + * {
|
|
|
|
margin-left: ${remSize(8)};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
|
|
|
|
const StyledInlineButton = styled.button`
|
|
|
|
&&& {
|
|
|
|
display: flex;
|
|
|
|
justify-content: center;
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
text-decoration: none;
|
|
|
|
|
|
|
|
color: ${prop('primaryTextColor')};
|
|
|
|
cursor: pointer;
|
|
|
|
border: none;
|
|
|
|
line-height: 1;
|
|
|
|
|
|
|
|
svg * {
|
|
|
|
fill: ${prop('primaryTextColor')};
|
|
|
|
}
|
|
|
|
|
|
|
|
&:disabled {
|
|
|
|
cursor: not-allowed;
|
|
|
|
}
|
|
|
|
|
|
|
|
> * + * {
|
|
|
|
margin-left: ${remSize(8)};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
|
|
|
|
const StyledIconButton = styled.button`
|
|
|
|
&&& {
|
|
|
|
display: flex;
|
|
|
|
justify-content: center;
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
width: ${remSize(32)}px;
|
|
|
|
height: ${remSize(32)}px;
|
|
|
|
text-decoration: none;
|
|
|
|
|
2020-05-03 11:54:57 +02:00
|
|
|
color: ${prop('Button.default.foreground')};
|
|
|
|
background-color: ${prop('Button.hover.background')};
|
2020-04-27 10:12:34 +02:00
|
|
|
cursor: pointer;
|
2020-05-03 11:54:57 +02:00
|
|
|
border: 1px solid transparent;
|
2020-04-27 10:12:34 +02:00
|
|
|
border-radius: 50%;
|
|
|
|
padding: ${remSize(8)} ${remSize(25)};
|
|
|
|
line-height: 1;
|
|
|
|
|
|
|
|
&:hover:not(:disabled) {
|
2020-05-03 11:54:57 +02:00
|
|
|
color: ${prop('Button.hover.foreground')};
|
|
|
|
background-color: ${prop('Button.hover.background')};
|
|
|
|
|
|
|
|
svg * {
|
|
|
|
fill: ${prop('Button.hover.foreground')};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
&:active:not(:disabled) {
|
|
|
|
color: ${prop('Button.active.foreground')};
|
|
|
|
background-color: ${prop('Button.active.background')};
|
2020-04-27 10:12:34 +02:00
|
|
|
|
|
|
|
svg * {
|
2020-05-03 11:54:57 +02:00
|
|
|
fill: ${prop('Button.active.foreground')};
|
2020-04-27 10:12:34 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
&:disabled {
|
2020-05-03 11:54:57 +02:00
|
|
|
color: ${prop('Button.disabled.foreground')};
|
|
|
|
background-color: ${prop('Button.disabled.background')};
|
2020-04-27 10:12:34 +02:00
|
|
|
cursor: not-allowed;
|
|
|
|
}
|
|
|
|
|
|
|
|
> * + * {
|
|
|
|
margin-left: ${remSize(8)};
|
2020-04-26 11:31:45 +02:00
|
|
|
}
|
2020-04-19 17:12:55 +02:00
|
|
|
}
|
|
|
|
`;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A Button performs an primary action
|
|
|
|
*/
|
|
|
|
const Button = ({
|
2020-05-19 18:31:36 +02:00
|
|
|
children, href, kind, iconBefore, iconAfter, 'aria-label': ariaLabel, to, type, ...props
|
2020-04-19 17:12:55 +02:00
|
|
|
}) => {
|
2020-05-19 18:31:36 +02:00
|
|
|
const hasChildren = React.Children.count(children) > 0;
|
|
|
|
const content = <>{iconBefore}{hasChildren && <span>{children}</span>}{iconAfter}</>;
|
2020-04-27 10:12:34 +02:00
|
|
|
let StyledComponent = StyledButton;
|
|
|
|
|
|
|
|
if (kind === kinds.inline) {
|
|
|
|
StyledComponent = StyledInlineButton;
|
|
|
|
} else if (kind === kinds.icon) {
|
|
|
|
StyledComponent = StyledIconButton;
|
|
|
|
}
|
2020-04-26 15:32:50 +02:00
|
|
|
|
2020-04-19 17:12:55 +02:00
|
|
|
if (href) {
|
2020-05-19 18:31:36 +02:00
|
|
|
return (
|
|
|
|
<StyledComponent
|
|
|
|
kind={kind}
|
|
|
|
as="a"
|
|
|
|
aria-label={ariaLabel}
|
|
|
|
href={href}
|
|
|
|
{...props}
|
|
|
|
>
|
|
|
|
{content}
|
|
|
|
</StyledComponent>
|
|
|
|
);
|
2020-04-19 17:12:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (to) {
|
2020-05-19 18:31:36 +02:00
|
|
|
return <StyledComponent kind={kind} as={Link} aria-label={ariaLabel} to={to} {...props}>{content}</StyledComponent>;
|
2020-04-19 17:12:55 +02:00
|
|
|
}
|
|
|
|
|
2020-05-19 18:31:36 +02:00
|
|
|
return <StyledComponent kind={kind} aria-label={ariaLabel} type={type} {...props}>{content}</StyledComponent>;
|
2020-04-19 17:12:55 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
Button.defaultProps = {
|
2020-05-11 22:28:18 +02:00
|
|
|
'children': null,
|
|
|
|
'disabled': false,
|
2020-05-19 18:31:36 +02:00
|
|
|
'iconAfter': null,
|
|
|
|
'iconBefore': null,
|
2020-05-11 22:28:18 +02:00
|
|
|
'kind': kinds.block,
|
|
|
|
'href': null,
|
|
|
|
'aria-label': null,
|
|
|
|
'to': null,
|
|
|
|
'type': 'button',
|
2020-04-19 17:12:55 +02:00
|
|
|
};
|
|
|
|
|
2020-04-27 10:12:34 +02:00
|
|
|
Button.kinds = kinds;
|
2020-04-26 15:32:50 +02:00
|
|
|
|
2020-04-19 17:12:55 +02:00
|
|
|
Button.propTypes = {
|
|
|
|
/**
|
|
|
|
* The visible part of the button, telling the user what
|
|
|
|
* the action is
|
|
|
|
*/
|
2020-05-11 22:28:18 +02:00
|
|
|
'children': PropTypes.element,
|
2020-04-19 17:12:55 +02:00
|
|
|
/**
|
|
|
|
If the button can be activated or not
|
|
|
|
*/
|
2020-05-11 22:28:18 +02:00
|
|
|
'disabled': PropTypes.bool,
|
2020-05-19 18:31:36 +02:00
|
|
|
/**
|
|
|
|
* SVG icon to place after child content
|
|
|
|
*/
|
|
|
|
'iconAfter': PropTypes.element,
|
|
|
|
/**
|
|
|
|
* SVG icon to place before child content
|
|
|
|
*/
|
|
|
|
'iconBefore': PropTypes.element,
|
2020-04-27 10:12:34 +02:00
|
|
|
/**
|
|
|
|
* The kind of button - determines how it appears visually
|
|
|
|
*/
|
2020-05-11 22:28:18 +02:00
|
|
|
'kind': PropTypes.oneOf(Object.values(kinds)),
|
2020-04-19 17:12:55 +02:00
|
|
|
/**
|
|
|
|
* Specifying an href will use an <a> to link to the URL
|
|
|
|
*/
|
2020-05-11 22:28:18 +02:00
|
|
|
'href': PropTypes.string,
|
2020-04-19 17:12:55 +02:00
|
|
|
/*
|
|
|
|
* An ARIA Label used for accessibility
|
|
|
|
*/
|
2020-05-11 22:28:18 +02:00
|
|
|
'aria-label': PropTypes.string,
|
2020-04-19 17:12:55 +02:00
|
|
|
/**
|
|
|
|
* Specifying a to URL will use a react-router Link
|
|
|
|
*/
|
2020-05-11 22:28:18 +02:00
|
|
|
'to': PropTypes.string,
|
2020-04-19 17:12:55 +02:00
|
|
|
/**
|
|
|
|
* If using a button, then type is defines the type of button
|
|
|
|
*/
|
2020-05-11 22:28:18 +02:00
|
|
|
'type': PropTypes.oneOf(['button', 'submit']),
|
2020-04-19 17:12:55 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
export default Button;
|