joyent-portal/packages/ui-toolkit/src/button/index.js

266 lines
5.9 KiB
JavaScript

import React from 'react';
import remcalc from 'remcalc';
import PropTypes from 'prop-types';
import is, { isOr } from 'styled-is';
import styled, { css } from 'styled-components';
import { A, Button as NButton } from 'normalized-styled-components';
import { Link } from 'react-router-dom';
import { borderRadius } from '../boxes';
import typography from '../typography';
import Baseline from '../baseline';
import StatusLoader from '../status-loader';
// Based on bootstrap 4
const style = css`
box-sizing: border-box;
display: inline-block;
justify-content: center;
align-items: center;
margin-bottom: ${remcalc(8)};
margin-top: ${remcalc(8)};
padding: ${remcalc(13)} ${remcalc(18)};
position: relative;
${typography.normal};
font-size: ${remcalc(15)};
text-align: center;
font-style: normal;
font-stretch: normal;
line-height: normal;
letter-spacing: normal;
text-decoration: none;
white-space: nowrap;
vertical-align: middle;
touch-action: manipulation;
cursor: pointer;
color: ${props => props.theme.white};
background-image: none;
background-color: ${props => props.theme.primary};
border-radius: ${borderRadius};
border: solid ${remcalc(1)} ${props => props.theme.primaryActive};
&:focus {
outline: 0;
text-decoration: none;
background-color: ${props => props.theme.primary};
border-color: ${props => props.theme.primaryActive};
}
&:hover {
background-color: ${props => props.theme.primaryHover};
border: solid ${remcalc(1)} ${props => props.theme.primaryActive};
}
&:active,
&:active:hover,
&:active:focus {
background-image: none;
outline: 0;
background-color: ${props => props.theme.primaryActive};
border-color: ${props => props.theme.primaryActive};
}
&[disabled] {
cursor: not-allowed;
pointer-events: none;
}
${is('secondary')`
color: ${props => props.theme.secondary};
background-color: ${props => props.theme.white};
border-color: ${props => props.theme.grey};
&:focus {
background-color: ${props => props.theme.white};
border-color: ${props => props.theme.grey};
}
&:hover {
background-color: ${props => props.theme.whiteHover};
border-color: ${props => props.theme.grey};
}
&:active,
&:active:hover,
&:active:focus {
background-color: ${props => props.theme.whiteActive};
border-color: ${props => props.theme.grey};
}
`};
${is('tertiary')`
color: ${props => props.theme.text};
background-color: ${props => props.theme.disabled};
border-color: ${props => props.theme.grey};
font-weight: 600;
&:focus,
&:hover,
&:active,
&:active:hover,
&:active:focus {
color: ${props => props.theme.text};
background-color: ${props => props.theme.disabled};
border-color: ${props => props.theme.grey};
}
`};
${is('tertiary', 'selected')`
background-color: rgba(59, 70, 204, 0.2);
color: ${props => props.theme.primaryActive};
border-color: ${props => props.theme.primaryActive};
&:focus,
&:hover,
&:active,
&:active:hover,
&:active:focus {
background-color: rgba(59, 70, 204, 0.2);
color: ${props => props.theme.primaryActive};
border-color: ${props => props.theme.primaryActive};
}
`};
${isOr('disabled', 'loading')`
cursor: not-allowed;
pointer-events: none;
color: ${props => props.theme.grey};
background-color: ${props => props.theme.disabled};
border-color: ${props => props.theme.grey};
&:focus {
background-color: ${props => props.theme.disabled};
border-color: ${props => props.theme.grey};
}
&:hover {
background-color: ${props => props.theme.disabled};
border-color: ${props => props.theme.disabled};
}
&:active,
&:active:hover,
&:active:focus {
background-color: ${props => props.theme.disabled};
border-color: ${props => props.theme.disabled};
}
`};
${is('rect')`
border-radius: 0;
`};
${is('small')`
padding: ${remcalc(13)} ${remcalc(18)};
`};
${is('icon')`
min-height: ${remcalc(48)};
min-width: ${remcalc(48)};
`};
${is('fluid')`
width: 100%;
max-width: 100%;
`};
${is('marginless')`
margin: 0;
`};
`;
const StyledButton = NButton.extend`
min-width: ${remcalc(120)};
${style} & + button {
margin-left: ${remcalc(20)};
}
`;
const StyledAnchor = A.extend`
display: inline-block;
${style};
`;
const StyledLink = styled(Link)`
display: inline-block;
${style};
`;
/**
* @example ./usage.md
*/
const Button = props => {
const { href = '', to = '', loading = false, secondary } = props;
const Views = [
() => (to ? StyledLink : null),
() => (href ? StyledAnchor : null),
() => StyledButton
];
const View = Views.reduce((sel, view) => (sel ? sel : view()), null);
const children = loading ? (
<StatusLoader secondary={!secondary} small />
) : (
props.children
);
return <View {...props}>{children}</View>;
};
Button.propTypes = {
/**
* The `<button>`/`<a>`/`<Link>` text
*/
children: PropTypes.node,
/**
* When used, it will render an `<a>` with the given href
*/
href: PropTypes.string,
/**
* When used, it will render a `<Link>` with the given to
*/
to: PropTypes.string,
/**
* Is it primary ?
*/
primary: PropTypes.bool,
/**
* Is it secondary ?
*/
secondary: PropTypes.bool,
/**
* Is it tertiary ?
*/
tertiary: PropTypes.bool,
/**
* When set to true a smaller version of the button will show
*/
small: PropTypes.bool,
/**
* Set to true to show loading animation
*/
loading: PropTypes.bool,
/**
* When used, will give button an active state (Only for tertiary for now)
*/
selected: PropTypes.bool
};
Button.defaultProps = {
primary: true,
secondary: false,
tertiary: false,
small: false,
loading: false,
selected: false
};
export default Baseline(Button);