-
Couldn't load subscription status.
- Fork 87
feat(date-picker): Add isDateDisabled functionality #6603
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
Changes from 27 commits
7cc70a1
6a49802
7df3432
e58f796
0c119cf
848efbe
41865b2
8c687f2
2474fe7
efadb48
077d79f
f9b077e
47cd2bb
93c8e1d
e67599d
8bc4cfd
a963bd4
46a33d4
1c103f8
459a783
3b2446d
07e8687
2268b6a
59750ee
1dd26c8
1010d3c
9ab7e8a
4e93815
2143f0e
f0793b9
a1d37c1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,17 @@ | |
| <script type="module"> | ||
| import '@vaadin/date-picker'; | ||
| import '@vaadin/tooltip'; | ||
| const isDateDisabled = (date) => { | ||
| // Exclude weekends and the 16th day of each month: | ||
| const checkDate = new Date(0, 0); | ||
| checkDate.setFullYear(date.year); | ||
| checkDate.setMonth(date.month); | ||
| checkDate.setDate(date.day); | ||
| return checkDate.getDay() === 0 || checkDate.getDay() === 6 || checkDate.getDate() === 16; | ||
| } | ||
|
Comment on lines
+12
to
+19
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We recently had a brief internal discussion regarding the parameter format for the Option 1:
Option 2: ISO Formatted Date String (
If a decision between these two options proves challenging, we could consider offering flexibility by providing both ISO formatted date strings and date objects with individual components. For example, an extended There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would the ISO date be optional or should we expect that the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Modifying There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For now it seems to be working well enough with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @rolfsmeds and @yuriy-fix , can you weigh in on this discussion and see if we should alter the |
||
| const picker = document.querySelector('vaadin-date-picker'); | ||
| picker.isDateDisabled = isDateDisabled; | ||
| picker.min = '2023-11-01'; | ||
| </script> | ||
| </head> | ||
| <body> | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -53,16 +53,37 @@ export function dateEquals(date1, date2) { | |
| ); | ||
| } | ||
|
|
||
| /** | ||
| * Extracts the basic component parts of a date (day, month and year) | ||
| * to the expected format. | ||
| * @param {!Date} date | ||
| * @return {{day: number, month: number, year: number}} | ||
| */ | ||
| export function extractDateParts(date) { | ||
| return { | ||
| day: date.getDate(), | ||
| month: date.getMonth(), | ||
| year: date.getFullYear(), | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * Check if the given date is in the range of allowed dates. | ||
| * | ||
| * @param {!Date} date The date to check | ||
| * @param {Date} min Range start | ||
| * @param {Date} max Range end | ||
| * @param {function(!DatePickerDate): boolean} isDateDisabled Callback to check if the date is disabled | ||
| * @return {boolean} True if the date is in the range | ||
| */ | ||
| export function dateAllowed(date, min, max) { | ||
| return (!min || date >= min) && (!max || date <= max); | ||
| export function dateAllowed(date, min, max, isDateDisabled) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the heavy lifting function that does all the work of deciding a date needs to be disabled. So all the components that need to disable a date end up feeding here. |
||
| let dateIsDisabled = false; | ||
| if (typeof isDateDisabled === 'function' && !!date) { | ||
| const dateToCheck = extractDateParts(date); | ||
| dateIsDisabled = isDateDisabled(dateToCheck); | ||
| } | ||
|
|
||
| return (!min || date >= min) && (!max || date <= max) && !dateIsDisabled; | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -90,20 +111,6 @@ export function getClosestDate(date, dates) { | |
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Extracts the basic component parts of a date (day, month and year) | ||
| * to the expected format. | ||
| * @param {!Date} date | ||
| * @return {{day: number, month: number, year: number}} | ||
| */ | ||
| export function extractDateParts(date) { | ||
| return { | ||
| day: date.getDate(), | ||
| month: date.getMonth(), | ||
| year: date.getFullYear(), | ||
| }; | ||
| } | ||
|
|
||
| /** | ||
| * Get difference in months between today and given months value. | ||
| * | ||
|
|
||
This conversation was marked as resolved.
Show resolved
Hide resolved
|
This conversation was marked as resolved.
Show resolved
Hide resolved
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,6 +11,7 @@ import { addListener, setTouchAction } from '@vaadin/component-base/src/gestures | |
| import { MediaQueryController } from '@vaadin/component-base/src/media-query-controller.js'; | ||
| import { SlotController } from '@vaadin/component-base/src/slot-controller.js'; | ||
| import { dateAfterXMonths, dateEquals, extractDateParts, getClosestDate } from './vaadin-date-picker-helper.js'; | ||
| import { dateAllowed } from './vaadin-date-picker-helper.js'; | ||
|
|
||
| /** | ||
| * @polymerMixin | ||
|
|
@@ -107,6 +108,17 @@ export const DatePickerOverlayContentMixin = (superClass) => | |
| sync: true, | ||
| }, | ||
|
|
||
| /** | ||
| * A function to be used to determine whether the user can select a given date. | ||
| * Receives a `DatePickerDate` object of the date to be selected and should return a | ||
| * boolean. | ||
| * | ||
| * @type {function(DatePickerDate): boolean | undefined} | ||
| */ | ||
| isDateDisabled: { | ||
| type: Function, | ||
| }, | ||
|
|
||
| /** | ||
| * Input label | ||
| */ | ||
|
|
@@ -134,9 +146,9 @@ export const DatePickerOverlayContentMixin = (superClass) => | |
|
|
||
| static get observers() { | ||
| return [ | ||
| '__updateCalendars(calendars, i18n, minDate, maxDate, selectedDate, focusedDate, showWeekNumbers, _ignoreTaps, _theme)', | ||
| '__updateCalendars(calendars, i18n, minDate, maxDate, selectedDate, focusedDate, showWeekNumbers, _ignoreTaps, _theme, isDateDisabled)', | ||
| '__updateCancelButton(_cancelButton, i18n)', | ||
| '__updateTodayButton(_todayButton, i18n, minDate, maxDate)', | ||
| '__updateTodayButton(_todayButton, i18n, minDate, maxDate, isDateDisabled)', | ||
| '__updateYears(years, selectedDate, _theme)', | ||
| ]; | ||
| } | ||
|
|
@@ -303,10 +315,10 @@ export const DatePickerOverlayContentMixin = (superClass) => | |
| } | ||
|
|
||
| /** @private */ | ||
| __updateTodayButton(todayButton, i18n, minDate, maxDate) { | ||
| __updateTodayButton(todayButton, i18n, minDate, maxDate, isDateDisabled) { | ||
| if (todayButton) { | ||
| todayButton.textContent = i18n && i18n.today; | ||
| todayButton.disabled = !this._isTodayAllowed(minDate, maxDate); | ||
| todayButton.disabled = !this._isTodayAllowed(minDate, maxDate, isDateDisabled); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -321,12 +333,14 @@ export const DatePickerOverlayContentMixin = (superClass) => | |
| showWeekNumbers, | ||
| ignoreTaps, | ||
| theme, | ||
| isDateDisabled, | ||
| ) { | ||
| if (calendars && calendars.length) { | ||
| calendars.forEach((calendar) => { | ||
| calendar.i18n = i18n; | ||
| calendar.minDate = minDate; | ||
| calendar.maxDate = maxDate; | ||
| calendar.isDateDisabled = isDateDisabled; | ||
| calendar.focusedDate = focusedDate; | ||
| calendar.selectedDate = selectedDate; | ||
| calendar.showWeekNumbers = showWeekNumbers; | ||
|
|
@@ -361,10 +375,14 @@ export const DatePickerOverlayContentMixin = (superClass) => | |
| * @protected | ||
| */ | ||
| _selectDate(dateToSelect) { | ||
| if (!this._dateAllowed(dateToSelect)) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Might need to discuss this change. I updated this to be a boolean function so that the keyboard |
||
| return false; | ||
| } | ||
| this.selectedDate = dateToSelect; | ||
| this.dispatchEvent( | ||
| new CustomEvent('date-selected', { detail: { date: dateToSelect }, bubbles: true, composed: true }), | ||
| ); | ||
| return true; | ||
| } | ||
|
|
||
| /** @private */ | ||
|
|
@@ -775,9 +793,10 @@ export const DatePickerOverlayContentMixin = (superClass) => | |
| handled = true; | ||
| break; | ||
| case 'Enter': | ||
| this._selectDate(this.focusedDate); | ||
| this._close(); | ||
| handled = true; | ||
| if (this._selectDate(this.focusedDate)) { | ||
This conversation was marked as resolved.
Show resolved
Hide resolved
|
||
| this._close(); | ||
| handled = true; | ||
| } | ||
| break; | ||
| case ' ': | ||
| this.__toggleDate(this.focusedDate); | ||
|
|
@@ -931,7 +950,8 @@ export const DatePickerOverlayContentMixin = (superClass) => | |
|
|
||
| /** @private */ | ||
| _focusAllowedDate(dateToFocus, diff, keepMonth) { | ||
| if (this._dateAllowed(dateToFocus)) { | ||
| // For this check we do consider the isDateDisabled function because disabled dates are allowed to be focused, just not outside min/max | ||
| if (this._dateAllowed(dateToFocus, undefined, undefined, () => false)) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this one place we want to bypass any custom |
||
| this.focusDate(dateToFocus, keepMonth); | ||
| } else if (this._dateAllowed(this.focusedDate)) { | ||
| // Move to min or max date | ||
|
|
@@ -1009,18 +1029,18 @@ export const DatePickerOverlayContentMixin = (superClass) => | |
| } | ||
|
|
||
| /** @private */ | ||
| _dateAllowed(date, min = this.minDate, max = this.maxDate) { | ||
| return (!min || date >= min) && (!max || date <= max); | ||
| _dateAllowed(date, min = this.minDate, max = this.maxDate, isDateDisabled = this.isDateDisabled) { | ||
| return dateAllowed(date, min, max, isDateDisabled); | ||
| } | ||
|
|
||
| /** @private */ | ||
| _isTodayAllowed(min, max) { | ||
| _isTodayAllowed(min, max, isDateDisabled) { | ||
| const today = new Date(); | ||
| const todayMidnight = new Date(0, 0); | ||
| todayMidnight.setFullYear(today.getFullYear()); | ||
| todayMidnight.setMonth(today.getMonth()); | ||
| todayMidnight.setDate(today.getDate()); | ||
| return this._dateAllowed(todayMidnight, min, max); | ||
| return this._dateAllowed(todayMidnight, min, max, isDateDisabled); | ||
| } | ||
|
|
||
| /** | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.