@muvehealth/fixins
Version:
Component library for Muvehealth
170 lines (159 loc) • 4.41 kB
Flow
// @flow
import React, { PureComponent } from 'react'
import path from 'ramda/src/path'
import prop from 'ramda/src/prop'
import styled from 'react-emotion'
import withProps from 'recompose/withProps'
import { fontSize, space, minWidth, themeGet } from 'styled-system'
import Box from '../Box'
import ErrorMessage from '../ErrorMessage'
import { DataCheckmark } from '../Icons'
import { isNotEmptyOrNotNil, shouldForwardProp } from '../../utils'
import { type InputType, type MetaType } from '../../types'
type Props = {
'data-id'?: string,
defaultChecked?: boolean,
id?: string,
input?: InputType,
label: string,
meta?: MetaType,
readOnly?: boolean,
}
const propOrInputProp = (property, props) => {
const mainProp = prop(property, props)
const inputProp = path(['input', property], props)
return mainProp != null ? mainProp : inputProp
}
class Checkbox extends PureComponent<Props & InputType> {
static defaultProps = {
'data-id': undefined,
defaultChecked: null,
id: undefined,
input: undefined,
meta: undefined,
readOnly: false,
}
render() {
const { defaultChecked, id, input, label, meta,
name, onChange, onFocus, onBlur, readOnly, value, ...styles } = this.props
// Account for Checkbox from CheckboxGroup or individual Checkbox
const inputId = id != null ? id : `${prop('name', input)}-${label}`
const inputDefaultChecked = defaultChecked != null ? defaultChecked : prop('value', input)
return (
<Box>
<Input
// eslint-disable-next-line react/destructuring-assignment
data-id={this.props['data-id']}
defaultChecked={inputDefaultChecked}
id={inputId}
readOnly={readOnly}
type="checkbox"
name={propOrInputProp('name', this.props)}
value={propOrInputProp('value', this.props)}
onChange={propOrInputProp('onChange', this.props)}
onFocus={propOrInputProp('onFocus', this.props)}
onBlur={propOrInputProp('onBlur', this.props)}
/>
<Label
htmlFor={inputId}
// $FlowFixMe: Dont want to limit styled-system props
{...styles}
>
{label}
</Label>
{ prop('touched', meta) === true && isNotEmptyOrNotNil(prop('error', meta))
&& (
<ErrorMessage
message={prop('error', meta)}
maxWidth={116}
/>
)
}
</Box>
)
}
}
export const Label = withProps({
fontSize: 2,
})(styled('label', { shouldForwardProp })(
{
alignItems: 'center',
cursor: 'pointer',
display: 'flex',
position: 'relative',
'@media not print': {
'::before': {
content: '""',
display: 'inline-block',
verticalAlign: 'middle',
minHeight: 32,
minWidth: 32,
marginRight: 16,
},
'::after': {
backgroundColor: 'transparent',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
content: '""',
height: 32,
left: 0,
position: 'absolute',
transformOrigin: 'center',
transform: 'scale(0)',
transition: 'transform 0.2s',
top: 'calc(50% - 16px)',
width: 32,
zIndex: 2,
},
'[readonly] + &': {
cursor: 'not-allowed',
pointerEvents: 'none',
},
'fieldset[disabled] &': {
cursor: 'default',
},
},
'@media print': {
marginRight: 16,
},
},
fontSize,
minWidth,
space,
props => ({
color: themeGet('colors.darkGray', '#4A4A4A')(props),
'::before': {
backgroundColor: themeGet('colors.inputGray', '#F0F0F0')(props),
},
'::after': {
backgroundImage:
`url("data:image/svg+xml;charset=utf8,${DataCheckmark('%2313535F')}")`,
},
}),
))
export const Input = styled('input')({
'@media not print': {
border: 0,
clip: 'rect(0 0 0 0)',
clipPath: 'inset(50%)',
height: 1,
margin: -1,
overflow: 'hidden',
padding: 0,
position: 'absolute',
width: 1,
whiteSpace: 'nowrap',
},
},
props => ({
':checked + label': {
'::before': {
backgroundColor: themeGet('colors.superLightBlue', '#D2FEFF')(props),
},
'::after': {
color: themeGet('colors.textBlue', '#13535F')(props),
transform: 'none',
},
},
}))
export default Checkbox