@rocketsofawesome/mirage
Version:
[Live Demo of the Pattern Library](https://rocketsofawesome.github.io/mirage/)
271 lines (241 loc) • 6.24 kB
JavaScript
import React from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import ErrorMessage from 'SRC/components/inputs/ErrorMessage'
const Container = styled.div`
background-color: white;
width: 100%;
padding: 10px 16px 6px;
border: ${props => props.theme.borders.inputBorder};
border-color: ${props => props.error ? props.theme.colors.textError : props.theme.colors.borderPrimary};
`
const Label = styled.label`
display: block;
font-size: 12px;
font-family: ${props => props.theme.fonts.primaryFont};
color: ${props => props.theme.colors.textPrimary};
letter-spacing: .5px;
font-weight: 200;
text-align: left;
`
const SelectContainer = styled.div`
display: flex;
padding-top: 5px;
`
const Select = styled.select`
background-color: transparent;
flex-grow: 1;
appearance: none;
border: none;
outline: none;
font-family: ${props => props.theme.fonts.secondaryFont};
font-size: 20px;
color: ${props => props.theme.colors.textPrimary};
&:focus {
color: ${props => props.theme.colors.focusPrimary};
}
`
function buildYears(baseYear, yearOffset) {
const years = []
for (let i = baseYear + yearOffset; i >= baseYear; --i) {
years.push(i)
}
return years
}
function buildDays(monthIndex) {
// Months with 31 days: 1,3,5,7,8,10,12
const days = []
const month = monthIndex + 1
for (let i = 1; i <= 29; ++i) {
days.push(i)
}
if (month === 2) {
return days
}
days.push(30)
if ([1, 3, 5, 7, 8, 10, 12].includes(month)) {
days.push(31)
}
return days
}
function defaultBaseYear() {
const today = new Date()
return today.getFullYear() - 15
}
function buildDate({ year, month, day }) {
if (year && month && day) {
return new Date(year, month, day)
}
return null
}
function buildDefaultValues(initialValue) {
const regex = /\d{4}-\d{2}-\d{2}/
if (typeof initialValue === 'string' && regex.test(initialValue)) {
return parseDateString(initialValue)
} else if (initialValue instanceof Date) {
return parseDateObject(initialValue)
}
return {}
}
function parseDateObject(date) {
return {
year: date.getFullYear(),
month: date.getMonth(),
day: date.getDate()
}
}
function parseDateString(string) {
const parts = string.split('-').map((part) => parseInt(part, 10))
return {
year: parts[0],
month: parts[1] - 1,
day: parts[2]
}
}
class Datepicker extends React.Component {
constructor(props) {
super(props)
this.state = { month: null }
this.blurred = {}
this.components = buildDefaultValues(props.initialValue)
this.handleMonthChange = this.handleMonthChange.bind(this)
this.handleChange = this.handleChange.bind(this)
this.handleBlur = this.handleBlur.bind(this)
}
handleMonthChange(e) {
this.setState({ month: parseInt(e.target.value, 10) })
this.handleChange(e)
}
handleChange(e) {
const { onChange } = this.props
this.components[e.target.name] = e.target.value
const date = buildDate(this.components)
onChange(date)
}
handleBlur(e) {
this.blurred[e.target.name] = true
const touched = this.blurred.year && this.blurred.month && this.blurred.day
if (touched) {
const { onTouched } = this.props
onTouched()
}
}
render() {
const {
baseYear,
error,
initialValue,
label,
months,
touched,
yearOffset
} = this.props
const { month } = this.state
const showError = touched && error
const days = buildDays(month)
const years = buildYears(baseYear, yearOffset)
const defaults = buildDefaultValues(initialValue)
return (
<div>
<Container error={showError}>
<Label>{label}</Label>
<SelectContainer>
<Select
name='month'
onChange={this.handleMonthChange}
onBlur={this.handleBlur}
defaultValue={defaults.month}
>
<option value=''>Month</option>
{
months.map(
(month, index) => (
<option value={index} key={index}>
{month}
</option>
)
)
}
</Select>
<Select
indented
name='day'
onChange={this.handleChange}
onBlur={this.handleBlur}
defaultValue={defaults.day}
>
<option value=''>Day</option>
{
days.map(
(day) => (
<option value={day} key={day}>
{day < 10 ? `0${day}` : day}
</option>
)
)
}
</Select>
<Select
indented
name='year'
onChange={this.handleChange}
onBlur={this.handleBlur}
defaultValue={defaults.year}
>
<option value=''>Year</option>
{
years.map(
(year) => (
<option value={year} key={year}>
{year}
</option>
)
)
}
</Select>
</SelectContainer>
</Container>
{showError &&
<ErrorMessage>{error}</ErrorMessage>
}
</div>
)
}
}
Datepicker.propTypes = {
baseYear: PropTypes.number,
error: PropTypes.string,
initialValue: PropTypes.oneOfType([
PropTypes.string,
PropTypes.instanceOf(Date)
]),
label: PropTypes.string.isRequired,
months: PropTypes.array,
onChange: PropTypes.func.isRequired,
onTouched: PropTypes.func,
touched: PropTypes.bool,
yearOffset: PropTypes.number
}
Datepicker.defaultProps = {
baseYear: defaultBaseYear(),
error: null,
initialValue: null,
months: [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December'
],
onTouched: () => null,
touched: false,
yearOffset: 30
}
export default Datepicker