Skip to content

Instantly share code, notes, and snippets.

@Beaglefoot
Created May 25, 2019 21:51
Show Gist options
  • Save Beaglefoot/35b67137698f9644370d48b3bdb3c045 to your computer and use it in GitHub Desktop.
Save Beaglefoot/35b67137698f9644370d48b3bdb3c045 to your computer and use it in GitHub Desktop.
DatePicker (with React)
console.clear();
// Helpers
const getDaysForCalendar = (date = new Date(), calendarSize = 42) => {
const visibleCalendarDays = [];
const monthStartDate = dateFns.startOfMonth(date);
const monthEndDate = dateFns.endOfMonth(date);
const monthStartDayOfWeek = monthStartDate.getDay();
const daysInMonth = dateFns.getDaysInMonth(date);
const numOfDaysToAddBefore = monthStartDayOfWeek - 1;
const calendarStartDate = dateFns.subDays(
monthStartDate,
numOfDaysToAddBefore
);
const calendarEndDate = dateFns.addDays(
monthEndDate,
calendarSize - daysInMonth - numOfDaysToAddBefore
);
dateFns
.eachDay(calendarStartDate, calendarEndDate)
.forEach(date => visibleCalendarDays.push(date));
return visibleCalendarDays;
};
const getDaysByWeek = (days = []) => {
const DAYS_IN_WEEK = 7;
const result = [];
let weekOfDays = [];
days.forEach(day => {
if (weekOfDays.length === DAYS_IN_WEEK) {
result.push(weekOfDays);
weekOfDays = [];
}
weekOfDays.push(day);
});
result.push(weekOfDays);
return result;
};
const getMonthAsWord = date => date.toString().split(' ')[1];
// View
class Calendar extends React.Component {
constructor(props) {
super(props);
this.state = { date: props.date };
}
addMonth = () => this.setState({ date: dateFns.addMonths(this.state.date, 1) });
subMonth = () => this.setState({ date: dateFns.subMonths(this.state.date, 1) });
addYear = () => this.setState({ date: dateFns.addYears(this.state.date, 1 ) });
subYear = () => this.setState({ date: dateFns.subYears(this.state.date, 1 ) });
showCurrentDate = () => this.setState({ date: new Date() });
renderControls() {
const { date } = this.state;
return (
<div className="calendar__controls-row">
<div className="calendar__controls">
<i className="fas fa-angle-left" onClick={this.subMonth}></i>
<i className="fas fa-angle-double-left" onClick={this.subYear}></i>
</div>
<div
className="calendar__month-with-year"
onClick={this.showCurrentDate}
>
{getMonthAsWord(date)}{' '}{date.getFullYear()}
</div>
<div className="calendar__controls">
<i className="fas fa-angle-double-right" onClick={this.addYear}></i>
<i className="fas fa-angle-right" onClick={this.addMonth}></i>
</div>
</div>
);
}
renderDaysOfWeek() {
return (
<div className="calendar__days-of-week">
{['M', 'T', 'W', 'T', 'F', 'S', 'S'].map(letter => (
<div className="calendar__day-of-week">{letter}</div>
))}
</div>
);
}
renderDate(date) {
return (
<div
className={classNames(
'calendar__day',
{
'calendar__day--pale': !dateFns.isSameMonth(date, this.state.date),
'calendar__day--emphasize': dateFns.isToday(date)
}
)}
onClick={() => this.props.onDatePick(date)}
>
{date.getDate()}
</div>
);
}
render() {
const weeksWithDaysInside = getDaysByWeek(getDaysForCalendar(this.state.date));
return (
<div className={classNames('calendar', this.props.className)}>
{this.renderControls()}
{this.renderDaysOfWeek()}
{weeksWithDaysInside.map(week => (
<div className="calendar__week">
{week.map(this.renderDate, this)}
</div>
))}
</div>
);
}
}
class DatePicker extends React.Component {
constructor() {
super();
this.state = {
isCalendarOpen: false,
selectedDate: null
};
}
handleClick = event => {
event.preventDefault();
event.target.blur();
this.setState({ isCalendarOpen: !this.state.isCalendarOpen });
};
handleDatePick = date => {
this.setState({
isCalendarOpen: false,
selectedDate: date
});
this.props.onDatePick(date);
};
render() {
const { onDatePick } = this.props;
const { isCalendarOpen, selectedDate } = this.state;
return (
<div className="date-picker">
<div className="date-picker__date-selection" onClick={this.handleClick}>
<i className="far fa-calendar-alt"></i>
<input
className="date-picker__input"
type="text"
placeholder="Select Date"
value={selectedDate && dateFns.format(selectedDate, 'DD.MM.YYYY')}
/>
</div>
{isCalendarOpen &&
<Calendar
className="date-picker__calendar"
date={selectedDate || new Date()}
onDatePick={this.handleDatePick}
/>
}
</div>
);
}
}
ReactDOM.render(
<DatePicker onDatePick={date => console.log(date.toString())} />,
document.getElementById('app')
);
new Footer().appendToDocument().stylize({
footer: {
background: 'rgba(11, 11, 11, 0.2)',
color: '#666'
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/date-fns/1.30.1/date_fns.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/classnames/2.2.6/index.min.js"></script>
<script src="https://codepen.io/Beaglefoot/pen/YRgEoW.js"></script>
$line-color: #fff;
$font-color: $line-color;
$font-color-emphasis: #F8CA00;
$highlight-color-background: lighten(#8A9B0F, 5%);
$app-color-background: darken(#490A3D, 7%);
$calendar-color-background: #BD1550;
$calendar-color-alt-background: #E97F02;
$calendar-padding: 10px;
html, body {
height: 100%;
}
body {
margin: 0;
display: flex;
flex-direction: column;
align-items: center;
background: linear-gradient(darken($app-color-background, 20%), $app-color-background),
linear-gradient(90deg, lighten($app-color-background, 2%), transparent),
linear-gradient(-90deg, darken($app-color-background, 2%), transparent);
background-blend-mode: screen;
background-size: cover;
}
#app {
margin-top: 30vh;
height: 100%;
}
.date-picker {
position: relative;
color: $font-color;
width: 240px;
box-sizing: border-box;
font-family: 'Nunito Sans', sans-serif;
}
.date-picker__date-selection {
padding: 10px;
font-size: 24px;
display: flex;
cursor: pointer;
}
.date-picker__input {
width: 100%;
font-size: 16px;
font-family: inherit;
margin-left: 1em;
border: none;
border-bottom: 2px solid $line-color;
cursor: inherit;
background: inherit;
color: inherit;
&:focus {
outline: none;
}
&::placeholder {
color: inherit;
opacity: 0.7;
}
}
.date-picker__calendar {
position: absolute;
}
.calendar {
box-sizing: border-box;
width: inherit;
min-height: 17em;
padding: $calendar-padding;
display: flex;
flex-direction: column;
justify-content: space-between;
color: $font-color;
font-size: 14px;
border: 1px solid $line-color;
border-radius: 5px;
user-select: none;
background: $calendar-color-background;
box-shadow: 0 8px 15px rgba(0, 0, 0, 0.1);
font-family: 'Nunito Sans', sans-serif;
}
.calendar__controls-row {
display: flex;
justify-content: space-between;
align-items: center;
text-align: center;
padding-bottom: 0.5em;
font-weight: bold;
.fas {
cursor: pointer;
}
}
.calendar__controls {
align-self: stretch;
display: flex;
.fas {
display: flex;
align-items: center;
justify-content: center;
}
}
.calendar__week,
.calendar__days-of-week {
display: flex;
justify-content: space-between;
}
.calendar__days-of-week {
font-weight: bold;
border-bottom: 1px solid $line-color;
border-top: 1px solid $line-color;
margin-bottom: 0.5em;
margin-left: -$calendar-padding;
margin-right: -$calendar-padding;
padding: 0 $calendar-padding;
background: $calendar-color-alt-background;
}
.calendar__day,
.calendar__day-of-week,
.calendar__month-with-year,
.calendar__controls .fas {
text-align: center;
min-width: 1.2em;
padding: 0.2em;
}
.calendar__day,
.calendar__month-with-year,
.calendar__controls .fas {
cursor: pointer;
border-radius: 5px;
transition: all 0.3s;
&:hover {
background: $highlight-color-background;
}
}
.calendar__day--pale {
color: rgba($font-color, 0.5);
}
.calendar__day--emphasize {
font-weight: bold;
color: $font-color-emphasis;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.8.2/css/all.min.css" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment