p5.js-web-editor/client/common/Button.jsx

241 lines
5.3 KiB
React
Raw Normal View History

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';
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')};
svg * {
2020-05-03 11:54:57 +02:00
fill: ${prop('Button.active.foreground')};
}
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
}
> * + * {
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')};
cursor: pointer;
2020-05-03 11:54:57 +02:00
border: 1px solid transparent;
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')};
svg * {
2020-05-03 11:54:57 +02:00
fill: ${prop('Button.active.foreground')};
}
}
&:disabled {
2020-05-03 11:54:57 +02:00
color: ${prop('Button.disabled.foreground')};
background-color: ${prop('Button.disabled.background')};
cursor: not-allowed;
}
> * + * {
margin-left: ${remSize(8)};
}
2020-04-19 17:12:55 +02:00
}
`;
/**
* A Button performs an primary action
*/
const Button = ({
children, href, kind, iconBefore, iconAfter, 'aria-label': ariaLabel, to, type, ...props
2020-04-19 17:12:55 +02:00
}) => {
const hasChildren = React.Children.count(children) > 0;
const content = <>{iconBefore}{hasChildren && <span>{children}</span>}{iconAfter}</>;
let StyledComponent = StyledButton;
if (kind === kinds.inline) {
StyledComponent = StyledInlineButton;
} else if (kind === kinds.icon) {
StyledComponent = StyledIconButton;
}
2020-04-19 17:12:55 +02:00
if (href) {
return (
<StyledComponent
kind={kind}
as="a"
aria-label={ariaLabel}
href={href}
{...props}
>
{content}
</StyledComponent>
);
2020-04-19 17:12:55 +02:00
}
if (to) {
return <StyledComponent kind={kind} as={Link} aria-label={ariaLabel} to={to} {...props}>{content}</StyledComponent>;
2020-04-19 17:12:55 +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,
'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
};
Button.kinds = kinds;
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,
/**
* SVG icon to place after child content
*/
'iconAfter': PropTypes.element,
/**
* SVG icon to place before child content
*/
'iconBefore': PropTypes.element,
/**
* 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;