question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

🚀 Feature request

Motivation

Inspired by #353, I’ve been working on a date picker implementation for the past couple of days and wanted to share my current level of progress and API ideas. I’ve been pulling inspiration from a number of libraries, but here is my idea for implementing some of the examples from gpbl/react-day-picker .

Note that all of the screenshots come from http://react-day-picker.js.org/examples/basic. So far I’ve done most of the stateful work and am currently working on getting the DOM output right. Only once that is done, I’ll start working on implementing style in reakit-system-bootstrap. Right now I’m really looking for feedback on the proposed API for a calendar component which can be composed into more complex examples such as a real date/time picker.

Examples

Simple Calendar

Screen Shot 2019-06-03 at 10 32 31 PM

Originally weekdays and days were of type Iterable as that seemed to be a clean way to allow for lazy computation. This API was then used as [...calendar.days].map(day => ... ). I now think that exposing them more as prop-getters is a better approach as it is more beginner-friendly and allows for passing options that determine the prop-getter output.

import React from "react";
import {
  useCalendarState,
  Calendar,
  CalendarNavigation,
  CalendarNavigationLabel,
  CalendarNavigationNext,
  CalendarNavigationPrev,
  CalendarMonthView,
  CalendarMonthViewWeekdays,
  CalendarMonthViewWeekdaysWeekday,
  CalendarMonthViewDays,
  CalendarMonthViewDay
} from "reakit/Calendar";

function Example() {
  const calendar = useCalendarState();
  return (
    <Calendar {...calendar}>
      <CalendarNavigation {...calendar}>
        <CalendarNavigationLabel {...calendar} />
        <CalendarNavigationPrev {...calendar} />
        <CalendarNavigationNext {...calendar} />
      </CalendarNavigation>
      <CalendarMonthView {...calendar}>
        <CalendarMonthViewWeekdays {...calendar}>
          {calendar.weekdays().map(weekday => (
            <CalendarMonthViewWeekdaysWeekday {...weekday} {...calendar} />
          ))}
        </CalendarMonthViewWeekdays>
        <CalendarMonthViewDays {...calendar}>
          {calendar.days().map(day => (
            <CalendarMonthViewDay {...day} {...calendar} />
          ))}
        </CalendarMonthViewDays>
      </CalendarMonthView>
    </Calendar>
  );
}

Outside Days

visibleWhenOutside in this case is passed directly as a prop to CalendarMonthViewDay but could just as easily be implemented by passing it as an option to days. I don’t have much of a preference in which way it is implemented.

Screen Shot 2019-06-03 at 10 35 26 PM
import React from "react";
import {
  useCalendarState,
  Calendar,
  CalendarNavigation,
  CalendarNavigationLabel,
  CalendarNavigationNext,
  CalendarNavigationPrev,
  CalendarMonthView,
  CalendarMonthViewWeekdays,
  CalendarMonthViewWeekdaysWeekday,
  CalendarMonthViewDays,
  CalendarMonthViewDay
} from "reakit/Calendar";

function Example() {
  const calendar = useCalendarState();
  return (
    <Calendar {...calendar}>
      <CalendarNavigation {...calendar}>
        <CalendarNavigationLabel {...calendar} />
        <CalendarNavigationPrev {...calendar} />
        <CalendarNavigationNext {...calendar} />
      </CalendarNavigation>
      <CalendarMonthView {...calendar}>
        <CalendarMonthViewWeekdays {...calendar}>
          {calendar.weekdays().map(weekday => (
            <CalendarMonthViewWeekdaysWeekday {...weekday} {...calendar} />
          ))}
        </CalendarMonthViewWeekdays>
        <CalendarMonthViewDays {...calendar}>
          {calendar.days().map(day => (
            <CalendarMonthViewDay visibleWhenOutside {...day} {...calendar} />
          ))}
        </CalendarMonthViewDays>
      </CalendarMonthView>
    </Calendar>
  );
}

Week numbers

I imagine that most styling solutions will implement CalendarMonthView as a column-oriented flexbox. This will then necessitate a wrapper div around CalendarMonthViewWeeks and CalendarMonthViewDays so that they are horizontally oriented. Another potential issue with this API is that some options passed to days need to be mirrored to weeks such as fixedWeeks or else they will render different numbers of rows.

Screen Shot 2019-06-03 at 10 36 20 PM
import React from "react";
import {
  useCalendarState,
  Calendar,
  CalendarNavigation,
  CalendarNavigationLabel,
  CalendarNavigationNext,
  CalendarNavigationPrev,
  CalendarMonthView,
  CalendarMonthViewWeekdays,
  CalendarMonthViewWeekdaysWeekday,
  CalendarMonthViewWeeks,
  CalendarMonthViewWeeksWeek,
  CalendarMonthViewDays,
  CalendarMonthViewDay
} from "reakit/Calendar";

function Example() {
  const calendar = useCalendarState();
  return (
    <Calendar {...calendar}>
      <CalendarNavigation {...calendar}>
        <CalendarNavigationLabel {...calendar} />
        <CalendarNavigationPrev {...calendar} />
        <CalendarNavigationNext {...calendar} />
      </CalendarNavigation>
      <CalendarMonthView {...calendar}>
        <CalendarMonthViewWeekdays {...calendar}>
          {calendar.weekdays().map(weekday => (
            <CalendarMonthViewWeekdaysWeekday {...weekday} {...calendar} />
          ))}
        </CalendarMonthViewWeekdays>
        <div>
          <CalendarMonthViewWeeks {...calendar}>
            {calendar.weeks().map(week => (
              <CalendarMonthViewWeeksWeek {...week} {...calendar} />
            ))}
          </CalendarMonthViewWeeks>
          <CalendarMonthViewDays {...calendar}>
            {calendar.days().map(day => (
              <CalendarMonthViewDay {...day} {...calendar} />
            ))}
          </CalendarMonthViewDays>
        </div>
      </CalendarMonthView>
    </Calendar>
  );
}

Fixed Weeks

Screen Shot 2019-06-03 at 10 37 12 PM
import React from "react";
import {
  useCalendarState,
  Calendar,
  CalendarNavigation,
  CalendarNavigationLabel,
  CalendarNavigationNext,
  CalendarNavigationPrev,
  CalendarMonthView,
  CalendarMonthViewWeekdays,
  CalendarMonthViewWeekdaysWeekday,
  CalendarMonthViewDays,
  CalendarMonthViewDay
} from "reakit/Calendar";

function Example() {
  const calendar = useCalendarState();
  return (
    <Calendar {...calendar}>
      <CalendarNavigation {...calendar}>
        <CalendarNavigationLabel {...calendar} />
        <CalendarNavigationPrev {...calendar} />
        <CalendarNavigationNext {...calendar} />
      </CalendarNavigation>
      <CalendarMonthView {...calendar}>
        <CalendarMonthViewWeekdays {...calendar}>
          {calendar.weekdays().map(weekday => (
            <CalendarMonthViewWeekdaysWeekday {...weekday} {...calendar} />
          ))}
        </CalendarMonthViewWeekdays>
        <CalendarMonthViewDays {...calendar}>
          {calendar.days({ fixedWeeks: true }).map(day => (
            <CalendarMonthViewDay {...day} {...calendar} />
          ))}
        </CalendarMonthViewDays>
      </CalendarMonthView>
    </Calendar>
  );
}

Today Button

Is CalendarTodayButton too specific when it’s really just a shorthand for <Button onClick={() => calendar.setValue(new Date())}>Go to Today</Button>

Screen Shot 2019-06-03 at 10 37 56 PM
import React from "react";
import {
  useCalendarState,
  Calendar,
  CalendarNavigation,
  CalendarNavigationLabel,
  CalendarNavigationNext,
  CalendarNavigationPrev,
  CalendarMonthView,
  CalendarMonthViewWeekdays,
  CalendarMonthViewWeekdaysWeekday,
  CalendarMonthViewDays,
  CalendarMonthViewDay,
  CalendarTodayButton
} from "reakit/Calendar";

function Example() {
  const calendar = useCalendarState();
  return (
    <Calendar {...calendar}>
      <CalendarNavigation {...calendar}>
        <CalendarNavigationLabel {...calendar} />
        <CalendarNavigationPrev {...calendar} />
        <CalendarNavigationNext {...calendar} />
      </CalendarNavigation>
      <CalendarMonthView {...calendar}>
        <CalendarMonthViewWeekdays {...calendar}>
          {calendar.weekdays().map(weekday => (
            <CalendarMonthViewWeekdaysWeekday {...weekday} {...calendar} />
          ))}
        </CalendarMonthViewWeekdays>
        <CalendarMonthViewDays {...calendar}>
          {calendar.days().map(day => (
            <CalendarMonthViewDay {...day} {...calendar} />
          ))}
        </CalendarMonthViewDays>
      </CalendarMonthView>
      <CalendarTodayButton {...calendar} />
    </Calendar>
  );
}

Change the initial month

Although I can’t imagine that recalculating value each render is that expensive should useCalendarState support allowing its options object and/or value be passed as factory functions.

Screen Shot 2019-06-03 at 10 38 44 PM
import React from "react";
import {
  useCalendarState,
  Calendar,
  CalendarNavigation,
  CalendarNavigationLabel,
  CalendarNavigationNext,
  CalendarNavigationPrev,
  CalendarMonthView,
  CalendarMonthViewWeekdays,
  CalendarMonthViewWeekdaysWeekday,
  CalendarMonthViewDays,
  CalendarMonthViewDay
} from "reakit/Calendar";

function Example() {
  const calendar = useCalendarState({ value: new Date(2015, 8) });
  return (
    <Calendar {...calendar}>
      <CalendarNavigation {...calendar}>
        <CalendarNavigationLabel {...calendar} />
        <CalendarNavigationPrev {...calendar} />
        <CalendarNavigationNext {...calendar} />
      </CalendarNavigation>
      <CalendarMonthView {...calendar}>
        <CalendarMonthViewWeekdays {...calendar}>
          {calendar.weekdays().map(weekday => (
            <CalendarMonthViewWeekdaysWeekday {...weekday} {...calendar} />
          ))}
        </CalendarMonthViewWeekdays>
        <CalendarMonthViewDays {...calendar}>
          {calendar.days().map(day => (
            <CalendarMonthViewDay {...day} {...calendar} />
          ))}
        </CalendarMonthViewDays>
      </CalendarMonthView>
    </Calendar>
  );
}

Prevent Months Navigation

Preventing months navigation is as simple as not rendering the navigation controls

Screen Shot 2019-06-03 at 10 39 33 PM
import React from "react";
import {
  useCalendarState,
  Calendar,
  CalendarNavigation,
  CalendarNavigationLabel,
  CalendarMonthView,
  CalendarMonthViewWeekdays,
  CalendarMonthViewWeekdaysWeekday,
  CalendarMonthViewDays,
  CalendarMonthViewDay
} from "reakit/Calendar";

function Example() {
  const calendar = useCalendarState();
  return (
    <Calendar {...calendar}>
      <CalendarNavigation {...calendar}>
        <CalendarNavigationLabel {...calendar} />
      </CalendarNavigation>
      <CalendarMonthView {...calendar}>
        <CalendarMonthViewWeekdays {...calendar}>
          {calendar.weekdays().map(weekday => (
            <CalendarMonthViewWeekdaysWeekday {...weekday} {...calendar} />
          ))}
        </CalendarMonthViewWeekdays>
        <CalendarMonthViewDays {...calendar}>
          {calendar.days().map(day => (
            <CalendarMonthViewDay {...day} {...calendar} />
          ))}
        </CalendarMonthViewDays>
      </CalendarMonthView>
    </Calendar>
  );
}

Restrict Months Navigation

In the example from react-day-picker the navigation controls are hidden when the user can no longer navigate in a direction. This can be implemented by composing the navigation controls from the Hidden component. An equally valid use-case would be to disable the buttons when the user can no longer navigate. Should one of these be the default and what would be the best API to allow for the alternative?

Screen Shot 2019-06-03 at 10 40 03 PM
import React from "react";
import {
  useCalendarState,
  Calendar,
  CalendarNavigation,
  CalendarNavigationLabel,
  CalendarNavigationNext,
  CalendarNavigationPrev,
  CalendarMonthView,
  CalendarMonthViewWeekdays,
  CalendarMonthViewWeekdaysWeekday,
  CalendarMonthViewDays,
  CalendarMonthViewDay
} from "reakit/Calendar";

function Example() {
  const calendar = useCalendarState({
    value: new Date(2081, 8),
    from: new Date(2018, 8),
    to: new Date(2018, 11)
  });
  return (
    <Calendar {...calendar}>
      <CalendarNavigation {...calendar}>
        <CalendarNavigationLabel {...calendar} />
        <CalendarNavigationPrev {...calendar} />
        <CalendarNavigationNext {...calendar} />
      </CalendarNavigation>
      <CalendarMonthView {...calendar}>
        <CalendarMonthViewWeekdays {...calendar}>
          {calendar.weekdays().map(weekday => (
            <CalendarMonthViewWeekdaysWeekday {...weekday} {...calendar} />
          ))}
        </CalendarMonthViewWeekdays>
        <CalendarMonthViewDays {...calendar}>
          {calendar.days({ fixedWeeks: true }).map(day => (
            <CalendarMonthViewDay {...day} {...calendar} />
          ))}
        </CalendarMonthViewDays>
      </CalendarMonthView>
    </Calendar>
  );
}

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:4
  • Comments:12 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
diegohazcommented, Aug 19, 2020

I started working on an API based on @jtmthf’s ideas:

import {
  useCalendarState,
  Calendar,
  CalendarRow,
  CalendarCell,
} from "reakit/Calendar";

function MyCalendar() {
  const calendar = useCalendarState({
    currentDate: new Date("2019-03-02"),
    firstDayOfTheWeek: 1, // Monday
  });
  // calendar.currentDate -> current selected cell JS Date object
  // calendar.currentDate.getFullYear() -> 2019
  // calendar.weeks -> 2d array with dates grouped by weeks
  // calendar.weekDays -> array with Monday, Tuesday etc.
  // calendar.move(date) -> move focus to a cell
  // calendar.next() -> move focus to next cell
  // calendar.nextMonth() -> update calendar.weeks with next month's dates
  // calendar.nextYear() -> update calendar.weeks with next year's dates
  return (
    <Calendar {...calendar}>
      <tr>
        {calendar.weekDays.map((day) => (
          <th key={day}>{day.slice(0, 3)}</th>
        ))}
      </tr>
      {calendar.weeks.map((dates, i) => (
        <CalendarRow {...calendar} key={i}>
          {dates.map((date) => (
            <CalendarCell {...calendar} key={date} date={date} />
          ))}
        </CalendarRow>
      ))}
    </Calendar>
  );
}

https://twitter.com/diegohaz/status/1295513457001988096

It’s a lot simpler and probably doesn’t address all the use cases brought up by @jtmthf (at least, not directly, but the user could probably achieve them with some customization), but I think it’s a good start.

It would use the Grid component underneath.

1reaction
diegohazcommented, May 31, 2020

@Guria The calendar is a Grid component with datetime features. We’re going to have grid soon (I’m working on it). But this can be already made on top of Composite. There are some examples here: https://codesandbox.io/s/composite-gcqs2

Read more comments on GitHub >

github_iconTop Results From Across the Web

Bootstrap 3 Datepicker v4 Docs
Bootstrap 3 Datepicker v4 Docs. Get awesome Dashboard Templates. Looking for a template with the datepicker ready to go? Then get check out...
Read more >
DateTimePicker - Element Plus
Select date and time in one picker. ... DateTimePicker is derived from DatePicker and TimePicker. For a more detailed explanation on attributes, you...
Read more >
10 Best Date And Time Picker JavaScript Plugins (2022 Update)
datetimepicker is a jQuery plugin that popups a simple and clean date & time picker interface when an input field on focus.
Read more >
DateTimePicker - XDSoft.net
DateTimePicker jQuery plugin select date and time. Use this plugin to unobtrusively add a datetimepicker, datepicker or timepicker dropdown to your forms.
Read more >
<input type="datetime-local"> - HTML - MDN Web Docs
The best way to deal with dates in forms in a cross-browser way at the moment is to get the user to enter...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found