@navinc/base-react-components
Version:
Nav's Pattern Library
200 lines (178 loc) • 5.26 kB
JavaScript
import React from 'react'
import styled from 'styled-components'
import Copy from './copy.js'
import { Errors, Err } from './form-elements/shared.js'
import isRebrand from './is-rebrand.js'
const getFillColor = (theme, checked, isInvalid, disabled) => {
if (isRebrand(theme)) {
if (checked) return isInvalid ? theme.navStatusNegative : theme.navPrimary400
return isInvalid ? theme.navStatusNegative100 : theme.navSecondary
}
if (disabled) return theme.scuttleGray100
if (checked) return theme.navPrimary
if (isInvalid) return theme.sebastianRed100
return theme.bubbleBlue100
}
const getBorderColor = (theme, checked, isInvalid, disabled) => {
if (isRebrand(theme)) {
if (checked) return isInvalid ? theme.navStatusNegative : theme.navPrimary400
return isInvalid ? theme.navStatusNegative : theme.navSecondary300
}
if (checked) return theme.bubbleBlue300
if (disabled) return theme.scuttleGray200
if (isInvalid) return theme.sebastianRed100
return theme.bubbleBlue200
}
const CopyLabel = styled(Copy)`
font-size: 14px;
`
const DivLabel = styled.div``
const CheckboxItem = styled.label`
display: flex;
flex-flow: row nowrap;
align-items: center;
flex: 1 1 auto;
margin-bottom: 0;
cursor: pointer;
text-align: left;
${({ asideContent, hasBorder, theme }) =>
(hasBorder || asideContent) &&
`
border-radius: ${isRebrand(theme) ? '8px' : '12px'};
border: 1px solid ${isRebrand(theme) ? theme.navNeutral300 : theme.border};
padding: ${isRebrand(theme) ? '14px 16px' : '8px 16px'};
min-height:${isRebrand(theme) ? '24px' : '62px'};
`}
${({ checked, checkedBackgroundColor }) =>
checked &&
checkedBackgroundColor &&
`
background-color: ${checkedBackgroundColor};
border-color: ${checkedBackgroundColor};
`}
`
const sizeVariants = {
sm: {
checkRight: '7px',
checkBottom: '7.5px',
checkboxSize: '20px',
thickness: '2.5px',
},
md: {
checkRight: '8px',
checkBottom: '9px',
checkboxSize: '24px',
thickness: '3px',
},
}
const AsideContainer = styled.div`
display: flex;
flex: 1;
justify-content: flex-end;
margin-left: 8px;
`
const BottomCheckLine = styled.div`
background-color: ${({ theme }) => theme.navNeutralLight};
border-radius: 100px;
position: absolute;
bottom: 0;
width: 100%;
`
const LeftCheckLine = styled.div`
background-color: ${({ theme }) => theme.navNeutralLight};
border-radius: 100px;
bottom: 0;
height: 50%;
left: 0;
position: absolute;
`
const CheckContainer = styled.div`
${({ checked }) => !checked && 'display: none;'}
height: 60%;
transform: rotate(-45deg);
position: absolute;
width: 50%;
`
const CheckboxContainer = styled.div`
position: relative;
${({ size }) => {
const { checkRight, checkBottom, checkboxSize, thickness } = sizeVariants[size] || sizeVariants.md
return `
${CheckContainer} {
right: ${checkRight};
bottom: ${checkBottom};
}
${LeftCheckLine} {
width: ${thickness}
}
${BottomCheckLine} {
height: ${thickness}
}
height: ${checkboxSize};
width: ${checkboxSize};
min-width: ${checkboxSize};
min-height: ${checkboxSize};
`
}}
margin-right: 16px;
`
const StyledCheckbox = styled.input.attrs(() => ({ type: 'checkbox' }))`
appearance: none;
background: ${({ checked, isInvalid, disabled, theme }) => getFillColor(theme, checked, isInvalid, disabled)};
opacity: ${({ disabled, theme }) => disabled && isRebrand(theme) && '38%'};
border-radius: 4px;
border-style: solid;
border-width: ${({ checked }) => (checked ? '3px' : '1px')};
border-color: ${({ checked, isInvalid, disabled, theme }) => getBorderColor(theme, checked, isInvalid, disabled)};
height: 100%;
margin: 0;
padding: 0;
width: 100%;
transition: box-shadow 300ms ease;
${({ theme, isInvalid }) => !isRebrand(theme) && isInvalid && `box-shadow: 0 0 0 4px ${theme.sebastianRed200};`};
&:focus {
outline: none;
box-shadow: 0 0 0 4px ${({ theme }) => (isRebrand(theme) ? theme.navStatusPositive500 : theme.bubbleBlue300)};
}
`
const Checkbox = ({
asideContent,
label,
checked,
checkedBackgroundColor,
disabled,
className,
hasBorder,
hasSpaceForErrors,
errors = [],
size,
isInvalid,
...props
}) => {
const Label = typeof label === 'string' ? CopyLabel : DivLabel
return (
<CheckboxItem
className={className}
checked={checked}
checkedBackgroundColor={checkedBackgroundColor}
hasBorder={hasBorder}
asideContent={asideContent}
>
<CheckboxContainer size={size}>
<StyledCheckbox {...{ checked, disabled, isInvalid }} {...props} />
<CheckContainer checked={checked}>
<BottomCheckLine />
<LeftCheckLine />
</CheckContainer>
</CheckboxContainer>
<div>
{label && <Label>{label}</Label>}
<Errors hasSpaceForErrors={hasSpaceForErrors}>
{!!errors.length && errors.map((err, i) => <Err key={`err-${i}`}>{err}</Err>)}
</Errors>
</div>
{asideContent && <AsideContainer>{asideContent}</AsideContainer>}
</CheckboxItem>
)
}
export default styled(Checkbox)``