Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: Ability to select a date range #364

Closed
tgBryanBailes opened this issue Apr 20, 2018 · 123 comments · Fixed by #1602
Closed

Feature Request: Ability to select a date range #364

tgBryanBailes opened this issue Apr 20, 2018 · 123 comments · Fixed by #1602
Assignees
Labels
enhancement New feature or request enterprise Paid enterprise feature important Need work

Comments

@tgBryanBailes
Copy link

Thank you for this amazing component. Your work and contributions are very much appreciated.

It would be amazing if this component could also work to select a date range (similar to https://ipiz.herokuapp.com/md-date-range-picker-demo/index.html).

@dmtrKovalenko
Copy link
Member

I have think about that. We are now a little bit stack with a new features. So I will do it ASAP.

@RedHatter
Copy link

Any chance this will be implemented soon?

@vdmkotai
Copy link

+1

@RedHatter
Copy link

I implemented this feature as well as multiple selection here. Anyone that's interested can try it out. Bugs can be reported on the pull request #508.

@dmtrKovalenko dmtrKovalenko added the important Need work label Aug 30, 2018
@bellini666
Copy link

+1, eagerly waiting for this

@RedHatter
Copy link

@dmtrKovalenko Not to be a bother, but any chance you'll get to implementing this soon?

@jakeleventhal
Copy link

+1 i need this

@marjan2k
Copy link

+1 This feature will be much valued

@dvislov
Copy link

dvislov commented Feb 4, 2019

+1

2 similar comments
@KlimkinaD
Copy link

+1

@gabrielweich
Copy link

+1

@frankbolviken
Copy link

+100

@RedHatter
Copy link

I've written a quick and dirty wrapper making use of renderDay prop to provide range and multi select.

import React, { useState, useContext, useRef } from 'react'
import { DatePicker } from 'material-ui-pickers'
import { MuiPickersContext } from 'material-ui-pickers'

function DateRangePicker({
  value,
  onChange,
  labelFunc,
  format,
  emptyLabel,
  autoOk,
  onClose,
  ...props
}) {
  const [begin, setBegin] = useState(value[0])
  const [end, setEnd] = useState(value[1])
  const [hover, setHover] = useState(null)
  const picker = useRef()
  const utils = useContext(MuiPickersContext)

  const min = Math.min(begin, end || hover)
  const max = Math.max(begin, end || hover)

  function renderDay(day, selectedDate, dayInCurrentMonth, dayComponent) {
    const style = {
      margin: 0,
      width: '40px'
    }

    if (day >= min && day <= max) {
      style.backgroundColor = '#3f51b5'
      style.color = 'white'
    }

    if (utils.isSameDay(day, min)) style.borderRadius = '50% 0 0 50%'
    else if (utils.isSameDay(day, max)) style.borderRadius = '0 50% 50% 0'
    else style.borderRadius = '0'

    return React.cloneElement(dayComponent, {
      onClick: e => {
        e.stopPropagation()
        if (!begin) setBegin(day)
        else if (!end) {
          setEnd(day)
          if (autoOk) {
            onChange([begin, end].sort())
            picker.current.close()
          }
        } else {
          setBegin(day)
          setEnd(null)
        }
      },
      onMouseEnter: e => setHover(day),
      style
    })
  }

  const formatDate = date => utils.format(date, format || utils.dateFormat)

  return (
    <DatePicker
      {...props}
      value={null}
      renderDay={renderDay}
      onClose={() => {
        onChange([begin, end].sort())
        onClose()
      }}
      onChange={() => {}}
      ref={picker}
      labelFunc={(date, invalid) =>
        labelFunc
          ? labelFunc([begin, end].sort(), invalid)
          : begin && end
          ? `${formatDate(begin)} - ${formatDate(end)}`
          : emptyLabel
      }
    />
  )
}

function DateMultiPicker({
  value,
  onChange,
  labelFunc,
  format,
  emptyLabel,
  onClose,
  ...props
}) {
  const [dates, setDates] = useState(value)
  const utils = useContext(MuiPickersContext)

  function renderDay(day, selectedDate, dayInCurrentMonth, dayComponent) {
    return React.cloneElement(dayComponent, {
      onClick: e => {
        e.stopPropagation()
        const i = dates.findIndex(d => utils.isSameDay(d, day))
        if (i >= 0) {
          const nextDates = [...dates]
          nextDates.splice(i, 1)
          setDates(nextDates)
        } else {
          setDates([...dates, day])
        }
      },
      style: dates.find(d => utils.isSameDay(d, day))
        ? {
            backgroundColor: '#3f51b5',
            color: 'white'
          }
        : {}
    })
  }

  const formatDate = date => utils.format(date, format || utils.dateFormat)

  return (
    <DatePicker
      {...props}
      value={dates[0]}
      renderDay={renderDay}
      onClose={() => {
        onChange(dates)
        onClose()
      }}
      onChange={() => {}}
      labelFunc={(date, invalid) =>
        labelFunc
          ? labelFunc(dates, invalid)
          : dates.length > 0
          ? dates.map(formatDate).join(', ')
          : emptyLabel
      }
    />
  )
}

@dmtrKovalenko
Copy link
Member

I like it!

@RedHatter
Copy link

I've put up a gist to share any changes I make. Gist

@rhuanbarreto
Copy link

Great to see the power of the community here! Kudos for @RedHatter! Sad to see that the repo owner rejected PR #508

@dmtrKovalenko dmtrKovalenko pinned this issue Apr 30, 2019
@dmtrKovalenko
Copy link
Member

I will start work on this issue in a few days 🔥🔥🔥

@Alexzanderk
Copy link

@dmtrKovalenko When we can take result of this feature?

@milindR
Copy link

milindR commented May 7, 2019

@dmtrKovalenko Thanks for the amazing component, when can we have this feature

@dmtrKovalenko
Copy link
Member

I suppose 1-2 weeks

@milindR
Copy link

milindR commented May 7, 2019

@dmtrKovalenko That's great news!!

@ghost
Copy link

ghost commented May 20, 2019

@dmtrKovalenko thanks for the great work you've done so far.. we are eagerly waiting for this feature...

@TrejGun
Copy link

TrejGun commented May 25, 2019

@RedHatter can you please fix your code to work in new version while we are waiting for this feature?

@TrejGun
Copy link

TrejGun commented May 26, 2019

so far i have this

import React, {useContext, useState} from "react";
import PropTypes from "prop-types";
import {DateTimePicker, MuiPickersContext} from "@material-ui/pickers";
import classNames from "classnames";
import {FormattedMessage} from "react-intl";
import {checkInTime, checkOutTime, dateTimeFormat} from "holymotors-constants-date";
import {useStyles as useOriginalStyles} from "@material-ui/pickers/DatePicker/components/Day";

import useStyles from "./styles";


function DateRangeWrapper({value, onChange, labelFunc, format, emptyLabel, onClose, ...props}) {
  const [begin, setBegin] = useState(value[0]);
  const [end, setEnd] = useState(value[1]);
  const [hover, setHover] = useState(undefined);
  const originalClasses = useOriginalStyles();
  const classes = useStyles();
  const utils = useContext(MuiPickersContext);

  const min = Math.min(begin, end || hover);
  const max = Math.max(begin, end || hover);

  function renderDay(day, selectedDate, dayInCurrentMonth, dayComponent) {
    return React.cloneElement(dayComponent, {
      onClick: e => {
        e.stopPropagation();
        if (!begin) setBegin(day);
        else if (!end) {
          setEnd(day);
        } else {
          setBegin(day);
          setEnd(undefined);
        }
      },
      onMouseEnter: e => setHover(day),
      className: classNames(originalClasses.day, classes.day, {
        [originalClasses.hidden]: dayComponent.props.hidden,
        [originalClasses.current]: dayComponent.props.current,
        [originalClasses.dayDisabled]: dayComponent.props.disabled,
        [originalClasses.daySelected]: utils.isSameDay(day, min) || utils.isSameDay(day, max) || (day >= min && day <= max),
        [classes.beginCap]: utils.isSameDay(day, min),
        [classes.endCap]: utils.isSameDay(day, max),
      }),
    });
  }

  const formatDate = date => utils.format(date, dateTimeFormat);

  return (
    <DateTimePicker
      {...props}
      fullWidth
      value={begin}
      renderDay={renderDay}
      onClose={onClose}
      onChange={date => {
        onChange([begin, end].sort((a, b) => a - b));
      }}
      onClear={() => {
        setBegin(undefined);
        setEnd(undefined);
        setHover(undefined);
        onChange([]);
      }}
      labelFunc={(date, invalid) =>
        labelFunc
          ? labelFunc([begin, end].sort((a, b) => a - b), invalid)
          : date && begin && end
          ? [begin, end].sort((a, b) => a - b).map(formatDate).join(" - ")
          : emptyLabel || ""
      }
    />
  );
}

DateRangeWrapper.propTypes = {
  value: PropTypes.array,
  onChange: PropTypes.func,
  labelFunc: PropTypes.func,
  format: PropTypes.string,
  emptyLabel: PropTypes.string,
  onClose: PropTypes.func,
};

export default DateRangeWrapper;
import {makeStyles} from "@material-ui/styles";


export default makeStyles(() => ({
  day: {
    margin: 0,
    width: 40,
    borderRadius: 0,
  },
  beginCap: {
    borderTopLeftRadius: "50%",
    borderBottomLeftRadius: "50%",
  },
  endCap: {
    borderTopRightRadius: "50%",
    borderBottomRightRadius: "50%",
  },
}), {name: "DateRangeWrapper"});

@jakeleventhal
Copy link

@dmtrKovalenko this looks good, but i think we will want to be able to see all the dates highlighted after the user clicks. i.e. once the range is chosen, the dates in the range should all show up as green rather than just the last selected single date

@dmtrKovalenko
Copy link
Member

Make sure that in the initial example you first choose the start date and then the end
image

Did you notice any issues with displaying the final range?

@jakeleventhal
Copy link

@dmtrKovalenko i see, it works but i have to click both text fields first in order for it to show up

@jakeleventhal
Copy link

ideally, you would just be able to

  1. open date pick via a button
  2. click a date then click another date and have the range appear

If the first date picked is before the second date, the first date picked is the start date
if the first date picked is after the second date, the first date picked is the end date.

@dmtrKovalenko
Copy link
Member

dmtrKovalenko commented Mar 28, 2020

⬆️ this is how it is working now in chrome

@jakeleventhal could you please record a video how it works for you? Thanks to you I just found a bug in safari

@jakeleventhal
Copy link

Mar-28-2020 15-50-56

@jakeleventhal
Copy link

it was tricky to get it working

@dmtrKovalenko
Copy link
Member

dmtrKovalenko commented Mar 28, 2020

@jakeleventhal in your case it is a problem just because for now we have a prefilled date range for the first example. The initial value is [new Date(), null] – so when you are clicking on the first date it is treated like you want to change the first date and then choose the end. (you can click on inputs to switch from start to end selection)

This must be more obvious in the second example :) Yeah I will change examples and docs structure

In safari it works really weird will take a look at the issue

@Angelk90
Copy link

@dmtrKovalenko : I have a list of suggestions, the problem and if I wrote them here they would be lost among all the comments, so if I wrote them then what you think are valid would you consider them?

@dmtrKovalenko
Copy link
Member

Yes please, I’d like to hear anything :)

@Angelk90
Copy link

@dmtrKovalenko :

  • Ability to select only dates prior to the current including the current one.
  • Ability to select only dates after the current one including the current one.
  • When selecting a start and end date, the possibility of selecting a 24 hour format time, showing it in a single input field.
    example: 24/03/2020 13:00 ~ 27/03/2020 14:00
    Schermata da 2020-03-29 14-40-11

@jvdownie
Copy link

jvdownie commented Apr 1, 2020

Is there a release timeline? Trying to get a sense of when this will be available so I can let our PO know. Thanks!

@oliviertassinari
Copy link
Member

@jvdownie first betas in Q2, stable maybe in v5, Q4.

@ghost
Copy link

ghost commented Apr 2, 2020

Yes please, I’d like to hear anything :)

Hey bro, it's all right?
Would it be possible to add two optional buttons?

(CANCEL) (APPLY)

Thanks!

@kirill-konshin
Copy link

kirill-konshin commented May 8, 2020

@dmtrKovalenko https://dev.material-ui-pickers.dev/demo/daterangepicker work like charm! One feature request though, is it possible to select same date to make one day range? It currently gives validation error...

image

@oliviertassinari
Copy link
Member

@kirill-konshin Could you open a new issue about the same day range?

@kirill-konshin
Copy link

@oliviertassinari #1759 done

@oliviertassinari oliviertassinari added the enterprise Paid enterprise feature label May 18, 2020
@oliviertassinari
Copy link
Member

I have added the enterprise label, we will look into how we can publish the range features as part of the enterprise version of Material-UI. The current plan is to have it released as unstable_ in v4, with a warning that it will go enterprise in v5. This should leave us enough time to explore the implications before moving further.

@balazsorban44
Copy link
Contributor

@oliviertassinari

I have added the enterprise label, we will look into how we can publish the range features as part of the enterprise version of Material-UI. The current plan is to have it released as unstable_ in v4, with a warning that it will go enterprise in v5. This should leave us enough time to explore the implications before moving further.

Does this mean that selecting a date range won't be free? Just curious.

@oliviertassinari
Copy link
Member

oliviertassinari commented May 21, 2020

@balazsorban44 the high-level strategy is as follow:

  1. bring the best possible MIT date picker component. This is the continuation of the work we have started 6 months ago. There will be a couple of milestones to get there: merge the picker in the mono repository, move the documentation to the main site, provide a desktop-first experience, fix performance issues, fix inconsistencies with core, improve docs. Handle users' feedback. We will know we have succeeded once we get to rank #1 on the query react datepicker on Google. We are still far from it (10-12), but getting closer. The simple fact that a full paid date picker components can rank before our component in the US is a major issue we will address :).
  2. bring the most/best position feature-rich date picker, under a GPLv3 or paid enterprise license. To match the existing competitors. Initially, we won't make the date range or date-time range MIT and see how that goes (we can't do it the other way around).

Regarding the pricing, that would likely be approachable and come with a bunch of advances features spread among all the components, e.g tree view drag and drop, data grid virtualization, etc.

@mauricioaznar

This comment has been minimized.

@bbigras
Copy link

bbigras commented Sep 3, 2020

Is there a blog post or something about which material-ui features will only be available in the enterprise version?

@oliviertassinari
Copy link
Member

@bbigras So far, advanced data grid, date range picker. Then likely, advanced scheduler, advanced tree view, advanced charting, advanced layouts (like in jupyterlab)

@bbigras
Copy link

bbigras commented Sep 4, 2020

Thank you very much.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request enterprise Paid enterprise feature important Need work
Projects
None yet
Development

Successfully merging a pull request may close this issue.