angular / components

Component infrastructure and Material Design components for Angular
https://material.angular.io
MIT License
24.1k stars 6.67k forks source link

feat(DatePicker): select range of dates #4763

Closed alaawerfelli closed 4 years ago

alaawerfelli commented 6 years ago

Hello, it's possible to set date range in date picker (have the ability to choose startDate and endDate and get all dates between startDate and endDate).

ss-bb commented 6 years ago

Hi. As it is write Here This is a feature that will come "Support for selecting and displaying date ranges"

shyamal890 commented 6 years ago

+1 Date range picker is also supported by ng-bootstrap. Example of which is available here: https://ng-bootstrap.github.io/#/components/datepicker/examples

raugaral commented 6 years ago

Than, I understand that currently is not supported, no?

fxck commented 6 years ago

http://ej2.syncfusion.com/demos/#/material/daterangepicker/minmax.html this looks decent

RaulRG commented 6 years ago

I think this is quite important for many scenarios (we will use Material for enterprise applications and we sure need it). @alaawerfelli said that it should return all the dates between startDate and endDate, but I think that the component should just return both dates, as the dates could be then calculated if needed. The design would be easier as you could use startDate / endDate as input / output parameters.

FabienInan commented 6 years ago

You can use [min] [max] attributes with your matDatePicker.

hanyu-natsu commented 6 years ago

@FabienInan [min] [max] attributes are about date validation, not related to date range selection.

pyritewolf commented 6 years ago

Is there a timeframe for the release of this feature? (or any place where we can see what are the things to come in the next couple of months?)

fxck commented 6 years ago

(or any place where we can see what are the things to come in the next couple of months?)

readme

SaturnTeam commented 6 years ago

I'm trying to do date range picker, and I also want to contribute it. How it is better - another component(s) or write code to existing?

SaturnTeam commented 6 years ago

image image

What do you think, this styles is ok?

RaulRG commented 6 years ago

@SaturnTeam : It looks good!! I looked at the Material Design specification, but they don't say anything about selection of ranges. Your design looks very material-like! :-)

ghost commented 6 years ago

@SaturnTeam That looks awesome, care to share the code? Thanks!

SaturnTeam commented 6 years ago

@GerardoSabetta Code not ready yet

SaturnTeam commented 6 years ago

UI for selected range between one month second primary-colored circle - it is :hover image image image

SaturnTeam commented 6 years ago

Material team, How do you think better - propagate values via existing @Output() selectedChange: D or create @Output() dateRangeChange: MatDatePickerRangeValue<D> ?

varnguyen commented 6 years ago

@SaturnTeam Can you show me project demo online? Please, I need your help?

SaturnTeam commented 6 years ago

@enqminh What do you want from it? Code done only 30%

hanyu-natsu commented 6 years ago

@SaturnTeam it's not easy to help without seeing how you plan to build the component.

Will there be 2 inputs for the begin and the end of the range ? Will you have 2 outputs for each of them ?

varnguyen commented 6 years ago

I just wanna datepicker form... to.

SaturnTeam commented 6 years ago

@lihanyu23 Ok, I got it. I will commit tomorrow to my repo to show. I'm going to use one input and add 1-3 additional variables to MatDatepicker and it's childs. I started to create another component, but then I understand that 90% of code is the same with MatDatePicker, and I decided to update MatDatepicker. About selecting and showing days through MatCalendar - no any problem. The difficulty in displaying value in <input>. Examples, how I think to show them (depends on DateAdapter) 01/31/2017 — 02/18/2017 (or 01/31 — 02/18/2017) Later to do these displaying nov 2017 (for 11/01/2017 — 11/31/2017) jul 2017 — dec 2017 (for 07/01/2017 — 12/12/2017) And of course string parser will separate string (by — or -) to convert it to two dates (begin and end). Also I don't forget about sauch date formats as 01-01-2001

Den-dp commented 6 years ago

@SaturnTeam Here is my ideas/mockup based on the styles of the latest datepicker.

image

Pattern with "filling" like this should work better (especially for ranges greater than 1 month), because:

For styling I used

background-color: #e6f3ff;

and for corner points I used this trick

border-radius: 100% 0 0 100%;
SaturnTeam commented 6 years ago

@Den-dp I wanted to do the same too. I got some problem, but now I can't understand what it was. I'll try tomorrow. I didn't find your color in material pallete https://material.io/guidelines/style/color.html#color-color-palette I will look something similar

Den-dp commented 6 years ago

@SaturnTeam good catch! Any color from 50-200 should fit perfectly

hanyu-natsu commented 6 years ago

@SaturnTeam try to use 50 or 100 from the palette. Also I like @Den-dp's idea.

RaulRG commented 6 years ago

The color should depend on the current theme. Not everyone will use blue. From the material design page: https://material.io/guidelines/style/color.html#color-color-system

A primary color is the color displayed most frequently across your app’s screens and components. It can also be used to accent elements, if you don’t have a secondary color. To create contrast between elements, you can use lighter or darker tones of your primary color.

I don't know if the Angular material implementation defines something like primary_color and primary_color_light for the themes, but we are looking for a color to specify the "from" and "to" dates and a lighter version of this color to show the selected dates.

RaulRG commented 6 years ago

@Den-dp I like your design. I agree with you that it looks less noisy => better

SaturnTeam commented 6 years ago

@Den-dp @RaulRG @lihanyu23 As you can see, it is not very great idea. without border-radius image with border-radius image

SaturnTeam commented 6 years ago

@lihanyu23 I commited to my repo https://github.com/SaturnTeam/material2 . Serving is the same as usual material dev. Range mode enabled on http://localhost:4200/datepicker "Result"

Den-dp commented 6 years ago

@SaturnTeam haha, but why? :stuck_out_tongue_winking_eye: Good work! :sparkles: The first one looks great for me!

Also here I tried to use a lighter 50 color and it looks even better

image

Personally, I do like the mockup without border-radius on each day, because the shape helps the eye to distinguish important information (where are the starting and ending points of the range) and f.e. expectations about whether or not the last day in calendar (NOV 30) is the end of the range is more clear.

Also for reference flights.google uses filled background in their calendar too

image

SaturnTeam commented 6 years ago

Ohh, just look how cute these neighborhood dates. image Code is ready (see my repo). Writing tests and documentation in progress. Also I applied last changes with multi year view. (keyboard not working temporally)

SaturnTeam commented 6 years ago

@juliemr @mmalerba Proposal https://docs.google.com/document/d/14S3BvSm9o8_7VKwJU0ek59V7Cm4Yjt6OOoMj_lpfYZ0/edit?usp=sharing

SaturnTeam commented 6 years ago

I created from my code standalone package. Installation: yarn add saturn-datepicker or npm install saturn-datepicker. Disscussion and info https://github.com/SaturnTeam/saturn-datepicker

SaturnTeam commented 6 years ago

Anyone knows about a11y with range selection?

mischkl commented 6 years ago

Our requirements (searching for documents within certain date ranges) dictate that the start and/or end dates may be empty if the user chooses. That way the date range can be used to mean "after a specific date" when only the start date is chosen, "before a specific date" when only the end date is chosen, "between two dates" when both are chosen, or "any date" when neither date is chosen.

This is something that most third-party implementations can't do, but that would be especially useful particularly for search interfaces! It would be greatly appreciated if this use case would be taken into account. :)

SaturnTeam commented 6 years ago

@mischkl Do you have any ideas how to do it through UI?

RaulRG commented 6 years ago

@mischkl These are exactly our requirements as well. The typical scenario for a range date picker is to use it for filtering data. Of course, there is another use case like "dates for your hotel booking" were you need fixed limits, but for filtering it is quite usual to have an "open" limit.

ThomasKientz commented 6 years ago

Do you have any ideas how to do it through UI?

@SaturnTeam My guess is to have two modes : "start date" and "end date". And a "validate" button.

When opening the date range picker :

Here is an exemple from third party Ionic 3 Date Picker :

image

The exemple is just for the switch button. It misses the validate button. We should also have an option to validate via a backdrop dismiss.

mischkl commented 6 years ago

Here is a mockup I made of what such a date picker control could look like. It would not only support empty start/end dates but also keyboard-only data entry, which I believe is another important goal that the current date picker already fulfills.

datepicker-mockup

Once the user clicks on a start date, the focus automatically switches to the end date, as in most 3rd party implementations. In case the user wants to leave the "From" date blank, they would simply click or tab to the "To" field manually. The cursor and field highlighting should be enough so they know what they are entering at any given time.

Additionally the color-coding of the input fields helps to form a mental map between what is entered above and what is selected on the calendar below - however it remains to be seen whether this is able to mesh seamlessly with the Material Design guidelines.

In keeping with other material controls there is no extra "validate" button; rather the validation would occur on tab / loss of focus. As a general rule it should not be possible to click on an end date that occurs before a start date.

Additionally there is the problem of how to show the start date and end date on the same day. For this case the color could be a blend of the start/end colors, or perhaps an overlapping venn-diagram-esque set of circles.

Whether the start and/or end date is allowed to be left blank can of course be made configurable, in order to support classic "hotel booking" use cases as well as search filtering use cases.

jelbourn commented 6 years ago

@mmalerba for discussion above ^

Carniatto commented 6 years ago

+1 any news?

mmalerba commented 6 years ago

@SaturnTeam Apologies for the slow response. Your implementation appears to line up pretty well with what I envisioned for range selection. Would you like to have a quick meeting to discuss what we need to do to move it into the main repo?

Carniatto commented 6 years ago

+1 @SaturnTeam any news on that?

SaturnTeam commented 6 years ago

@Carniatto I'm working with mmalerba on range date picker. It is slow due to different timezones, my work and what mmalerba want.

Temporally you can use my datepicker.

AllNamesRTaken commented 6 years ago

In the demo stackblitz you cant drag to select and it also auto commits without first showing what was selected. I got a bit surprised. Perhaps a draggable selection would make that easier. Also a double month view would make it more clear IMO.

Great work though,

SaturnTeam commented 6 years ago

I think I need to announce progress status this feature. It's temporary frozen https://github.com/angular/material2/pull/10489#issuecomment-375034796 But anyone can use my datepicker for a time. I'm trying to maintain code as close to original code as possible

shlomiassaf commented 6 years ago

@mmalerba you're probably busy with ng-conf but I think the solution shouldn't be complicated.

Trying to force the input to show a range with masking and parsing is additional work we should avoid...

We have a working input that knows how to handle a date and sync it to/from a datapicker, so let's use 2!

something like this:

<mat-form-field>
  <input matInput placeholder="Choose a date" [matDatepicker]="resultPicker">
  <mat-datepicker #resultPicker></mat-datepicker>
</mat-form-field>
<mat-form-field>
  <input matInput [matDatepickerRangeEnd]="resultPicker">
  <mat-datepicker-toggle matSuffix [for]="resultPicker"></mat-datepicker-toggle>
</mat-form-field>

Which looks something like this: image

The use can define it's own separator in the middle, remove gap, create gaps whatever.

input[matDatepickerRangeEnd] is a directive similar to input[matDatepicker] but it will register an range end input...

min/max etc will be taken from the first input only

There are some changes to MatDatepicker, especially handling the new range end input element, new selection events for it etc... and some logic to handle selections / value changes.

e.g when a user is in range mode, if he clicks on a date we don't close the popup/dialog if it's the first selection, we will close on the 2nd...

And of course, internal changes in the month view to show range of selection and 2 selected dates.

I'v done this by extending the base classes and adding functionality and it works without much work. Because I use the same input directive the logic for parsing and handling changes is the same, I just save it in a different variable (range end...)

This design might also help with implementing dual datapicker range selection, like usually seen in airline scheduling sites...

I would be more then happy to quickly push this with a PR, this approach solves all the issues you mentioned in #10489 (comment) because it uses the same logic for range and non range pickers.

shlomiassaf commented 6 years ago

@mmalerba This is the implementation using inheritance: StackBlitz source + demo

I'll point the obvious, 95% of the code is due to inheritance boilerplate in angular templates and component metadata in derived components. Some boilerplates is due to extensive use of private modifiers and not protected.

I estimate the entire change is ~75-85 lines of code.

From the above, the majority goes to TS code in the components. SCSS changes are minimal, around ~8 LOC. HTML changes are also minimal, ~10 LOC.

Will happily post a PR for this.

You'r comments in #10489 (comment) are taken care of because it uses the same logic the already exist to handle dates.

The only thing to take care of is a keyboard input of invalid date range (e.g. end date < start date)

mmalerba commented 6 years ago

@shlomiassaf I do like the direction that's going, but it's strange that you have two different form-fields that pretend to be one. Something we could do instead would be to wrap then in another component/directive that acts as a MatFormFieldControl. I imagine the DOM would look something like this:

<mat-form-field>
  <mat-date-range [matDatepicker]="picker" [formControl]="range">
    <input matStartDate>
    <input matEndDate>
  </mat-date-range>
  <mat-datepicker #picker><mat-datepicker>
</mat-form-field>
shlomiassaf commented 6 years ago

@mmalerba I agree, my implementation was just to demonstrate the idea and that it's possible.

I'm working with this solution right now, it does require some work to tame 2 MatFormField s...

Your solution sound good but now we need to think of forms integration more carefully.

What do you mean with [formControl]="range", it looks like FormGroup to me, since we have 2 inputs... If we use FormControl it will require us to use transformation functions from 2 date inputs into 1 text and vice versa.

I know that MatFormField is limited to 1 control, so its a problem...