@navinc/base-react-components
Version:
Nav's Pattern Library
189 lines (162 loc) • 4.92 kB
JavaScript
import React from 'react'
import styled from 'styled-components'
import Button from '../button'
import Copy from '../copy'
import Text from '../text'
import Icon from '../icon.js'
import NumberFormat from 'react-number-format'
import CarrotDown from '../icons/actions/carrot-down'
export const HelperIcon = styled(Icon)`
max-width: 16px;
max-height: 16px;
fill: ${({ theme }) => theme.neutral400};
`
export const HelperDescription = styled(Copy).attrs(() => ({ size: 'sm', bold: true }))`
color: inherit;
`
export const HelperItem = styled.div.attrs(
({ isLink }) => isLink && { as: Button, variation: 'buttonLink', type: 'button' }
)`
display: grid;
padding-top: 4px;
grid-template-columns: min-content 1fr;
grid-gap: 4px;
text-align: left;
${({ hasSpaceForErrors }) => (hasSpaceForErrors ? 'min-height: 18px;' : '')}
${({ theme, isLink }) => !isLink && `color: ${theme.neutral400};`}
`
export const Helper = styled(({ className, hasSpaceForHelper, helperLinkAction, helperText, iconName }) => (
<HelperItem
className={className}
isLink={!!helperLinkAction}
onClick={helperLinkAction}
hasSpaceForHelper={hasSpaceForHelper}
>
{iconName && <HelperIcon name={iconName} />}
<HelperDescription data-testid={`input:helper-text:${helperText}`} light size="xs">
{helperText}
</HelperDescription>
</HelperItem>
))``
export const Label = styled(Text)`
display: block;
transition: all 0.2s linear;
pointer-events: none;
transform-origin: 0 0;
&:focus {
outline: none;
}
&::after {
content: '${({ required }) => (required ? '*' : '')}';
}
`
export const FieldWrapper = styled.div`
display: grid;
& > ${Copy} {
margin-bottom: ${({ theme }) => theme.gu(1)};
}
& * {
font-family: inherit;
}
& > ${Label} {
font-size: 14px;
}
& > ${Helper} {
margin-left: ${({ theme }) => theme.gu(2)};
}
`
FieldWrapper.displayName = 'FieldWrapper'
export const Input = styled.input.withConfig({
shouldForwardProp: (prop) => !['isInvalid', 'hasSpaceForErrors'].includes(prop),
})`
width: 100%;
border-width: 3px;
border-style: solid;
border-color: ${({ isInvalid, theme }) => (isInvalid ? theme.transparentRed : theme.bubbleBlue200)};
border-radius: 12px;
font-weight: bold;
background: ${({ isInvalid, theme }) => (isInvalid ? theme.sebastianRed100 : theme.bubbleBlue100)};
${({ disabled, theme }) => disabled && `cursor: not-allowed; background: ${theme.scuttleGray100}`};
padding: ${({ theme }) => `${theme.gu(3)} ${theme.gu(2)} ${theme.gu(1)} ${theme.gu(2)}`};
&:focus,
&:hover {
outline: none;
outline-offset: 0;
}
&:focus {
border-color: ${({ theme }) => theme.bubbleBlue300};
}
`
export const CurrencyInput = styled(Input).attrs(() => ({
as: NumberFormat,
}))``
export const PercentInput = styled(Input).attrs(() => ({ as: NumberFormat }))``
export const NumberInput = styled(Input).attrs(() => ({ as: NumberFormat }))``
const ChevronWrapper = styled.div`
pointer-events: none;
& > ${Icon} {
fill: ${({ isInvalid, disabled, theme }) =>
isInvalid ? theme.error : disabled ? theme.scuttleGray500 : theme.azure};
}
`
export const Chevron = styled(({ className, disabled, isInvalid }) => (
<ChevronWrapper className={className} disabled={disabled} isInvalid={isInvalid}>
<CarrotDown />
</ChevronWrapper>
))``
export const Field = styled.label`
align-items: stretch;
color: ${({ theme }) => theme.scuttleGray600};
cursor: pointer;
display: flex;
flex: 1 1 auto;
margin-bottom: 0;
overflow: hidden;
position: relative;
text-align: left;
white-space: nowrap;
& > * {
width: 100%;
border-radius: 12px;
appearance: none;
}
& > ${Label} {
will-change: transform;
font-weight: ${({ isVisited }) => (isVisited ? 'normal' : 'bold')};
top: ${({ isVisited, theme }) => (isVisited ? theme.gu(1) : theme.gu(2))};
transform: ${({ isVisited }) => (isVisited ? 'scale(0.75)' : 'scale(1)')};
left: ${({ theme }) => theme.gu(2)};
position: absolute;
transition: all 0.2s ease;
color: ${({ theme }) => theme.scuttleGray600};
}
* {
font-size: 16px;
}
${Input}:focus ~ ${Label}, &:hover ${Label}, &:active ${Label} {
transform: scale(0.75);
font-weight: normal;
top: ${({ theme }) => theme.gu(1)};
}
${CurrencyInput}:focus + ${Label},
&:hover ${Label},
&:active ${Label} {
color: ${({ theme }) => theme.scuttleGray600};
}
& > ${Chevron} {
width: min-content;
position: absolute;
right: 16px;
top: 16px;
}
`
export const Err = styled(Copy).attrs(() => ({ size: 'sm', bold: true }))`
color: ${({ theme }) => theme.error};
text-align: left;
`
export const Errors = styled.div`
${({ hasSpaceForErrors }) => (hasSpaceForErrors ? 'min-height: 18px;' : '')}
& > ${Err} {
margin-left: ${({ theme }) => theme.gu(2)};
}
`