@kiwicom/orbit-components
Version:
<div align="center"> <a href="https://orbit.kiwi" target="_blank"> <img alt="orbit-components" src="https://orbit.kiwi/wp-content/uploads/2018/08/orbit-components.png" srcset="https://orbit.kiwi/wp-content/uploads/2018/08/orbit-components@2x.png 2x"
433 lines (412 loc) • 17.4 kB
JavaScript
// @flow
import * as React from "react";
import styled from "styled-components";
import defaultTokens from "../defaultTokens";
import { ICON_SIZES } from "../Icon/consts";
import { TYPE_OPTIONS, SIZE_OPTIONS, TOKENS } from "./consts";
import Loading, { StyledSpinner } from "../Loading";
import { getSize } from "../Icon";
import type { Props } from "./index";
const getSizeToken = name => ({ theme, size }) => {
const tokens = {
[TOKENS.heightButton]: {
[SIZE_OPTIONS.LARGE]: theme.orbit.heightButtonLarge,
[SIZE_OPTIONS.NORMAL]: theme.orbit.heightButtonNormal,
[SIZE_OPTIONS.SMALL]: theme.orbit.heightButtonSmall,
},
[TOKENS.loadingWidth]: {
[SIZE_OPTIONS.LARGE]: theme.orbit.widthIconMedium,
[SIZE_OPTIONS.NORMAL]: theme.orbit.widthIconMedium,
[SIZE_OPTIONS.SMALL]: theme.orbit.widthIconSmall,
},
[TOKENS.loadingHeight]: {
[SIZE_OPTIONS.LARGE]: theme.orbit.heightIconMedium,
[SIZE_OPTIONS.NORMAL]: theme.orbit.heightIconMedium,
[SIZE_OPTIONS.SMALL]: theme.orbit.heightIconSmall,
},
[TOKENS.fontSizeButton]: {
[SIZE_OPTIONS.LARGE]: theme.orbit.fontSizeButtonLarge,
[SIZE_OPTIONS.NORMAL]: theme.orbit.fontSizeButtonNormal,
[SIZE_OPTIONS.SMALL]: theme.orbit.fontSizeButtonSmall,
},
[TOKENS.paddingButton]: {
[SIZE_OPTIONS.LARGE]: theme.orbit.paddingButtonLarge,
[SIZE_OPTIONS.NORMAL]: theme.orbit.paddingButtonNormal,
[SIZE_OPTIONS.SMALL]: theme.orbit.paddingButtonSmall,
},
[TOKENS.paddingButtonWithIcon]: {
[SIZE_OPTIONS.LARGE]: theme.orbit.paddingButtonLargeWithIcon,
[SIZE_OPTIONS.NORMAL]: theme.orbit.paddingButtonNormalWithIcon,
[SIZE_OPTIONS.SMALL]: theme.orbit.paddingButtonSmallWithIcon,
},
[TOKENS.marginRightIcon]: {
[SIZE_OPTIONS.LARGE]: theme.orbit.marginButtonIconLarge,
[SIZE_OPTIONS.NORMAL]: theme.orbit.marginButtonIconNormal,
[SIZE_OPTIONS.SMALL]: theme.orbit.marginButtonIconSmall,
},
};
return tokens[name][size];
};
const getTypeToken = name => ({ theme, type }) => {
const tokens = {
[TOKENS.backgroundButton]: {
[TYPE_OPTIONS.PRIMARY]: theme.orbit.backgroundButtonPrimary,
[TYPE_OPTIONS.SECONDARY]: theme.orbit.backgroundButtonSecondary,
[TYPE_OPTIONS.INFO]: theme.orbit.backgroundButtonInfo,
[TYPE_OPTIONS.SUCCESS]: theme.orbit.backgroundButtonSuccess,
[TYPE_OPTIONS.WARNING]: theme.orbit.backgroundButtonWarning,
[TYPE_OPTIONS.CRITICAL]: theme.orbit.backgroundButtonCritical,
[TYPE_OPTIONS.FACEBOOK]: theme.orbit.backgroundButtonFacebook,
[TYPE_OPTIONS.GOOGLE]: theme.orbit.backgroundButtonGoogle,
},
[TOKENS.backgroundButtonHover]: {
[TYPE_OPTIONS.PRIMARY]: theme.orbit.backgroundButtonPrimaryHover,
[TYPE_OPTIONS.SECONDARY]: theme.orbit.backgroundButtonSecondaryHover,
[TYPE_OPTIONS.INFO]: theme.orbit.backgroundButtonInfoHover,
[TYPE_OPTIONS.SUCCESS]: theme.orbit.backgroundButtonSuccessHover,
[TYPE_OPTIONS.WARNING]: theme.orbit.backgroundButtonWarningHover,
[TYPE_OPTIONS.CRITICAL]: theme.orbit.backgroundButtonCriticalHover,
[TYPE_OPTIONS.FACEBOOK]: theme.orbit.backgroundButtonFacebookHover,
[TYPE_OPTIONS.GOOGLE]: theme.orbit.backgroundButtonGoogleHover,
},
[TOKENS.backgroundButtonActive]: {
[TYPE_OPTIONS.PRIMARY]: theme.orbit.backgroundButtonPrimaryActive,
[TYPE_OPTIONS.SECONDARY]: theme.orbit.backgroundButtonSecondaryActive,
[TYPE_OPTIONS.INFO]: theme.orbit.backgroundButtonInfoActive,
[TYPE_OPTIONS.SUCCESS]: theme.orbit.backgroundButtonSuccessActive,
[TYPE_OPTIONS.WARNING]: theme.orbit.backgroundButtonWarningActive,
[TYPE_OPTIONS.CRITICAL]: theme.orbit.backgroundButtonCriticalActive,
[TYPE_OPTIONS.FACEBOOK]: theme.orbit.backgroundButtonFacebookActive,
[TYPE_OPTIONS.GOOGLE]: theme.orbit.backgroundButtonGoogleActive,
},
[TOKENS.colorTextButton]: {
[TYPE_OPTIONS.PRIMARY]: theme.orbit.colorTextButtonPrimary,
[TYPE_OPTIONS.SECONDARY]: theme.orbit.colorTextButtonSecondary,
[TYPE_OPTIONS.INFO]: theme.orbit.colorTextButtonInfo,
[TYPE_OPTIONS.SUCCESS]: theme.orbit.colorTextButtonSuccess,
[TYPE_OPTIONS.WARNING]: theme.orbit.colorTextButtonWarning,
[TYPE_OPTIONS.CRITICAL]: theme.orbit.colorTextButtonCritical,
[TYPE_OPTIONS.FACEBOOK]: theme.orbit.colorTextButtonFacebook,
[TYPE_OPTIONS.GOOGLE]: theme.orbit.colorTextButtonGoogle,
},
[TOKENS.colorTextButtonBordered]: {
[TYPE_OPTIONS.PRIMARY]: theme.orbit.colorTextButtonPrimaryBordered,
[TYPE_OPTIONS.SECONDARY]: theme.orbit.colorTextButtonSecondaryBordered,
[TYPE_OPTIONS.INFO]: theme.orbit.colorTextButtonInfoBordered,
[TYPE_OPTIONS.SUCCESS]: theme.orbit.colorTextButtonSuccessBordered,
[TYPE_OPTIONS.WARNING]: theme.orbit.colorTextButtonWarningBordered,
[TYPE_OPTIONS.CRITICAL]: theme.orbit.colorTextButtonCriticalBordered,
[TYPE_OPTIONS.FACEBOOK]: theme.orbit.colorTextButtonFacebookBordered,
[TYPE_OPTIONS.GOOGLE]: theme.orbit.colorTextButtonGoogleBordered,
},
[TOKENS.colorTextButtonHover]: {
[TYPE_OPTIONS.PRIMARY]: theme.orbit.colorTextButtonPrimaryHover,
[TYPE_OPTIONS.SECONDARY]: theme.orbit.colorTextButtonSecondaryHover,
[TYPE_OPTIONS.INFO]: theme.orbit.colorTextButtonInfoHover,
[TYPE_OPTIONS.SUCCESS]: theme.orbit.colorTextButtonSuccessHover,
[TYPE_OPTIONS.WARNING]: theme.orbit.colorTextButtonWarningHover,
[TYPE_OPTIONS.CRITICAL]: theme.orbit.colorTextButtonCriticalHover,
[TYPE_OPTIONS.FACEBOOK]: theme.orbit.colorTextButtonFacebookHover,
[TYPE_OPTIONS.GOOGLE]: theme.orbit.colorTextButtonGoogleHover,
},
[TOKENS.colorTextButtonBorderedHover]: {
[TYPE_OPTIONS.PRIMARY]: theme.orbit.colorTextButtonPrimaryBorderedHover,
[TYPE_OPTIONS.SECONDARY]: theme.orbit.colorTextButtonSecondaryBorderedHover,
[TYPE_OPTIONS.INFO]: theme.orbit.colorTextButtonInfoBorderedHover,
[TYPE_OPTIONS.SUCCESS]: theme.orbit.colorTextButtonSuccessBorderedHover,
[TYPE_OPTIONS.WARNING]: theme.orbit.colorTextButtonWarningBorderedHover,
[TYPE_OPTIONS.CRITICAL]: theme.orbit.colorTextButtonCriticalBorderedHover,
[TYPE_OPTIONS.FACEBOOK]: theme.orbit.colorTextButtonFacebookBorderedHover,
[TYPE_OPTIONS.GOOGLE]: theme.orbit.colorTextButtonGoogleBorderedHover,
},
[TOKENS.colorTextButtonActive]: {
[TYPE_OPTIONS.PRIMARY]: theme.orbit.colorTextButtonPrimaryActive,
[TYPE_OPTIONS.SECONDARY]: theme.orbit.colorTextButtonSecondaryActive,
[TYPE_OPTIONS.INFO]: theme.orbit.colorTextButtonInfoActive,
[TYPE_OPTIONS.SUCCESS]: theme.orbit.colorTextButtonSuccessActive,
[TYPE_OPTIONS.WARNING]: theme.orbit.colorTextButtonWarningActive,
[TYPE_OPTIONS.CRITICAL]: theme.orbit.colorTextButtonCriticalActive,
[TYPE_OPTIONS.FACEBOOK]: theme.orbit.colorTextButtonFacebookActive,
[TYPE_OPTIONS.GOOGLE]: theme.orbit.colorTextButtonGoogleActive,
},
[TOKENS.colorTextButtonBorderedActive]: {
[TYPE_OPTIONS.PRIMARY]: theme.orbit.colorTextButtonPrimaryBorderedActive,
[TYPE_OPTIONS.SECONDARY]: theme.orbit.colorTextButtonSecondaryBorderedActive,
[TYPE_OPTIONS.INFO]: theme.orbit.colorTextButtonInfoBorderedActive,
[TYPE_OPTIONS.SUCCESS]: theme.orbit.colorTextButtonSuccessBorderedActive,
[TYPE_OPTIONS.WARNING]: theme.orbit.colorTextButtonWarningBorderedActive,
[TYPE_OPTIONS.CRITICAL]: theme.orbit.colorTextButtonCriticalBorderedActive,
[TYPE_OPTIONS.FACEBOOK]: theme.orbit.colorTextButtonFacebookBorderedActive,
[TYPE_OPTIONS.GOOGLE]: theme.orbit.colorTextButtonGoogleBorderedActive,
},
[TOKENS.borderColorButton]: {
[TYPE_OPTIONS.PRIMARY]: theme.orbit.borderColorButtonPrimaryBordered,
[TYPE_OPTIONS.SECONDARY]: theme.orbit.borderColorButtonSecondaryBordered,
[TYPE_OPTIONS.INFO]: theme.orbit.borderColorButtonInfoBordered,
[TYPE_OPTIONS.SUCCESS]: theme.orbit.borderColorButtonSuccessBordered,
[TYPE_OPTIONS.WARNING]: theme.orbit.borderColorButtonWarningBordered,
[TYPE_OPTIONS.CRITICAL]: theme.orbit.borderColorButtonCriticalBordered,
[TYPE_OPTIONS.FACEBOOK]: theme.orbit.borderColorButtonFacebookBordered,
[TYPE_OPTIONS.GOOGLE]: theme.orbit.borderColorButtonGoogleBordered,
},
[TOKENS.borderColorButtonHover]: {
[TYPE_OPTIONS.PRIMARY]: theme.orbit.borderColorButtonPrimaryBorderedHover,
[TYPE_OPTIONS.SECONDARY]: theme.orbit.borderColorButtonSecondaryBorderedHover,
[TYPE_OPTIONS.INFO]: theme.orbit.borderColorButtonInfoBorderedHover,
[TYPE_OPTIONS.SUCCESS]: theme.orbit.borderColorButtonSuccessBorderedHover,
[TYPE_OPTIONS.WARNING]: theme.orbit.borderColorButtonWarningBorderedHover,
[TYPE_OPTIONS.CRITICAL]: theme.orbit.borderColorButtonCriticalBorderedHover,
[TYPE_OPTIONS.FACEBOOK]: theme.orbit.borderColorButtonFacebookBorderedHover,
[TYPE_OPTIONS.GOOGLE]: theme.orbit.borderColorButtonGoogleBorderedHover,
},
[TOKENS.borderColorButtonActive]: {
[TYPE_OPTIONS.PRIMARY]: theme.orbit.borderColorButtonPrimaryBorderedActive,
[TYPE_OPTIONS.SECONDARY]: theme.orbit.borderColorButtonSecondaryBorderedActive,
[TYPE_OPTIONS.INFO]: theme.orbit.borderColorButtonInfoBorderedActive,
[TYPE_OPTIONS.SUCCESS]: theme.orbit.borderColorButtonSuccessBorderedActive,
[TYPE_OPTIONS.WARNING]: theme.orbit.borderColorButtonWarningBorderedActive,
[TYPE_OPTIONS.CRITICAL]: theme.orbit.borderColorButtonCriticalBorderedActive,
[TYPE_OPTIONS.FACEBOOK]: theme.orbit.borderColorButtonFacebookBorderedActive,
[TYPE_OPTIONS.GOOGLE]: theme.orbit.borderColorButtonGoogleBorderedActive,
},
};
return tokens[name][type];
};
const IconContainer = styled(({ className, children }) => (
<div className={className}>{children}</div>
))`
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin-right: ${({ onlyIcon }) => (onlyIcon ? "0" : getSizeToken(TOKENS.marginRightIcon))};
color: ${({ bordered }) =>
bordered ? getTypeToken(TOKENS.colorTextButtonBordered) : getTypeToken(TOKENS.colorTextButton)};
transition: background ${({ theme }) => theme.orbit.durationFast} ease-in-out,
box-shadow ${({ theme }) => theme.orbit.durationFast} ease-in-out;
> * {
width: ${getSize()};
height: ${getSize()};
}
`;
IconContainer.defaultProps = {
theme: defaultTokens,
};
const IconContainerRight = styled(IconContainer)`
margin-right: 0;
margin-left: ${({ onlyIcon }) => (onlyIcon ? "0" : getSizeToken(TOKENS.marginRightIcon))};
`;
IconContainerRight.defaultProps = {
theme: defaultTokens,
};
export const StyledButton = styled(
({
theme,
component,
circled,
external,
type,
icon,
iconLeft,
iconRight,
sizeIcon,
width,
bordered,
loading,
onlyIcon,
block,
style,
dataTest,
submit,
...props
}) => {
const isButtonWithHref = component === "button" && props.href;
const Component = isButtonWithHref ? "a" : component;
const buttonType = submit ? "submit" : "button";
return (
<Component data-test={dataTest} type={!isButtonWithHref ? buttonType : undefined} {...props}>
{props.children}
</Component>
);
},
)`
position: relative;
display: ${({ href, component }) => (href || component === "a" ? "inline-flex" : "flex")};
justify-content: center;
align-items: center;
box-sizing: border-box;
appearance: none;
text-decoration: none;
width: ${({ block, width, onlyIcon }) =>
block
? "100%"
: (width && `${width}px`) || (onlyIcon && getSizeToken(TOKENS.heightButton)) || "auto"};
flex: ${({ block }) => (block ? "1 1 100%" : "0 0 auto")};
height: ${getSizeToken(TOKENS.heightButton)};
background: ${({ bordered, theme }) =>
bordered ? theme.orbit.backgroundButtonBordered : getTypeToken(TOKENS.backgroundButton)};
color: ${({ bordered }) =>
bordered
? getTypeToken(TOKENS.colorTextButtonBordered)
: getTypeToken(TOKENS.colorTextButton)} !important;
border: 0;
border-radius: ${({ theme, circled }) =>
circled ? getSizeToken(TOKENS.heightButton) : theme.orbit.borderRadiusNormal};
padding: 0;
padding-right: ${({ onlyIcon, iconRight }) =>
(onlyIcon && "0") ||
(iconRight ? getSizeToken(TOKENS.paddingButtonWithIcon) : getSizeToken(TOKENS.paddingButton))};
padding-left: ${({ onlyIcon, icon, iconLeft }) =>
(onlyIcon && "0") ||
(iconLeft || icon
? getSizeToken(TOKENS.paddingButtonWithIcon)
: getSizeToken(TOKENS.paddingButton))};
font-weight: ${({ theme }) => theme.orbit.fontWeightBold}!important;
font-size: ${getSizeToken(TOKENS.fontSizeButton)};
cursor: ${({ disabled }) => (disabled ? "default" : "pointer")};
transition: all 0.15s ease-in-out !important;
outline: 0;
opacity: ${({ disabled, theme }) => disabled && theme.orbit.opacityButtonDisabled};
pointer-events: ${({ disabled }) => disabled && "none"};
box-shadow: ${({ bordered, theme, type }) =>
bordered &&
`inset 0 0 0 1px ${getTypeToken(TOKENS.borderColorButton)({
theme,
type,
})}`}; // Cannot resolve with 0 0 0 1px getTypeToken(TOKENS.borderColorButton)
&:hover {
background: ${({ disabled, bordered, theme }) =>
!disabled &&
(bordered
? theme.orbit.backgroundButtonBorderedHover
: getTypeToken(TOKENS.backgroundButtonHover))};
box-shadow: ${({ disabled, bordered, theme, type }) =>
!disabled &&
bordered &&
`inset 0 0 0 1px ${getTypeToken(TOKENS.borderColorButtonHover)({ theme, type })}`};
color: ${({ disabled, bordered }) =>
!disabled &&
(bordered
? getTypeToken(TOKENS.colorTextButtonBorderedHover)
: getTypeToken(TOKENS.colorTextButtonHover))}!important;
${IconContainer} {
color: ${({ disabled, bordered }) =>
!disabled &&
(bordered
? getTypeToken(TOKENS.colorTextButtonBorderedHover)
: getTypeToken(TOKENS.colorTextButtonHover))};
}
}
&:active {
${({ disabled, theme }) =>
!disabled && `transform: scale(${theme.orbit.modifierScaleButtonActive})`};
background: ${({ disabled, bordered, theme }) =>
!disabled &&
(bordered
? theme.orbit.backgroundButtonBorderedActive
: getTypeToken(TOKENS.backgroundButtonActive))};
box-shadow: ${({ disabled, bordered, theme, type }) =>
!disabled &&
(bordered &&
`inset 0 0 0 1px ${getTypeToken(TOKENS.borderColorButtonActive)({ theme, type })}`)};
color: ${({ disabled, bordered }) =>
!disabled &&
(bordered
? getTypeToken(TOKENS.colorTextButtonBorderedActive)
: getTypeToken(TOKENS.colorTextButtonActive))}!important;
& ${IconContainer} {
color: ${({ disabled, bordered }) =>
!disabled &&
(bordered
? getTypeToken(TOKENS.colorTextButtonBorderedActive)
: getTypeToken(TOKENS.colorTextButtonActive))};
}
}
&:enabled:focus {
box-shadow: ${({ bordered, theme, type }) =>
bordered && `inset 0 0 0 1px ${getTypeToken(TOKENS.borderColorButton)({ theme, type })},`}
${({ theme }) => theme.orbit.boxShadowButtonFocus};
}
${StyledSpinner} {
width: ${getSizeToken(TOKENS.loadingWidth)};
height: ${getSizeToken(TOKENS.loadingHeight)};
}
`;
StyledButton.defaultProps = {
theme: defaultTokens,
};
const StyledButtonContent = styled.div`
visibility: ${({ loading }) => loading && "hidden"};
height: 100%;
display: flex;
justify-content: center;
align-items: center;
`;
StyledButtonContent.defaultProps = {
theme: defaultTokens,
};
const Button = (props: Props) => {
const {
component = "button",
children,
bordered,
disabled,
href,
size = SIZE_OPTIONS.NORMAL,
icon,
iconRight,
external,
type = TYPE_OPTIONS.PRIMARY,
block,
loading = false,
width = 0,
} = props;
const iconLeft = props.iconLeft || icon;
const sizeIcon = size === ICON_SIZES.SMALL ? ICON_SIZES.SMALL : ICON_SIZES.MEDIUM;
const onlyIcon = iconLeft && !children;
const isDisabled = loading || disabled;
return (
<StyledButton
bordered={bordered}
block={block}
component={component}
disabled={isDisabled}
loading={loading}
onlyIcon={onlyIcon}
size={size}
sizeIcon={sizeIcon}
target={href && external ? "_blank" : undefined}
type={type}
width={width}
{...props}
>
{loading && <Loading type="buttonLoader" />}
<StyledButtonContent loading={loading}>
{iconLeft && (
<IconContainer
bordered={bordered}
onlyIcon={onlyIcon}
size={size}
sizeIcon={sizeIcon}
type={type}
>
{iconLeft}
</IconContainer>
)}
{children}
{iconRight && (
<IconContainerRight
bordered={bordered}
onlyIcon={onlyIcon}
size={size}
sizeIcon={sizeIcon}
type={type}
>
{iconRight}
</IconContainerRight>
)}
</StyledButtonContent>
</StyledButton>
);
};
export default Button;