mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-01 00:32:11 +00:00
Bug 1320647 - Implement min and max for date picker. r=mconley
MozReview-Commit-ID: Hps2CbziJqy --HG-- extra : rebase_source : 134056095b8d0531fe28e51f8157bacd1c54fe73
This commit is contained in:
parent
0ae981d837
commit
82c29b3dff
@ -53,23 +53,21 @@ Calendar.prototype = {
|
||||
* {
|
||||
* {Number} textContent
|
||||
* {Array<String>} classNames
|
||||
* {Boolean} enabled
|
||||
* }
|
||||
* {Function} getDayString: Transform day number to string
|
||||
* {Function} getWeekHeaderString: Transform day of week number to string
|
||||
* {Function} setValue: Set value for dateKeeper
|
||||
* {Number} selectionValue: The selection date value
|
||||
* {Function} setSelection: Set selection for dateKeeper
|
||||
* }
|
||||
*/
|
||||
setProps(props) {
|
||||
if (props.isVisible) {
|
||||
// Transform the days and weekHeaders array for rendering
|
||||
const days = props.days.map(({ dateValue, textContent, classNames }) => {
|
||||
const days = props.days.map(({ dateObj, classNames, enabled }) => {
|
||||
return {
|
||||
dateValue,
|
||||
textContent: props.getDayString(textContent),
|
||||
className: dateValue == props.selectionValue ?
|
||||
classNames.concat("selection").join(" ") :
|
||||
classNames.join(" ")
|
||||
textContent: props.getDayString(dateObj.getUTCDate()),
|
||||
className: classNames.join(" "),
|
||||
enabled
|
||||
};
|
||||
});
|
||||
const weekHeaders = props.weekHeaders.map(({ textContent, classNames }) => {
|
||||
@ -151,10 +149,12 @@ Calendar.prototype = {
|
||||
case "click": {
|
||||
if (event.target.parentNode == this.context.daysView) {
|
||||
let targetId = event.target.dataset.id;
|
||||
this.props.setValue({
|
||||
selectionValue: this.props.days[targetId].dateValue,
|
||||
dateValue: this.props.days[targetId].dateValue
|
||||
});
|
||||
let targetObj = this.props.days[targetId];
|
||||
if (targetObj.enabled) {
|
||||
this.props.setSelection({
|
||||
selection: targetObj.dateObj
|
||||
});
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -6,41 +6,67 @@
|
||||
|
||||
/**
|
||||
* DateKeeper keeps track of the date states.
|
||||
*
|
||||
* @param {Object} date parts
|
||||
* {
|
||||
* {Number} year
|
||||
* {Number} month
|
||||
* {Number} day
|
||||
* }
|
||||
* {Object} options
|
||||
* {
|
||||
* {Number} firstDayOfWeek [optional]
|
||||
* {Array<Number>} weekends [optional]
|
||||
* {Number} calViewSize [optional]
|
||||
* }
|
||||
*/
|
||||
function DateKeeper({ year, month, day }, { firstDayOfWeek = 0, weekends = [0], calViewSize = 42 }) {
|
||||
this.state = {
|
||||
firstDayOfWeek, weekends, calViewSize,
|
||||
dateObj: new Date(0),
|
||||
years: [],
|
||||
months: [],
|
||||
days: []
|
||||
};
|
||||
this.state.weekHeaders = this._getWeekHeaders(firstDayOfWeek);
|
||||
this._update(year, month, day);
|
||||
function DateKeeper(props) {
|
||||
this.init(props);
|
||||
}
|
||||
|
||||
{
|
||||
const DAYS_IN_A_WEEK = 7,
|
||||
MONTHS_IN_A_YEAR = 12,
|
||||
YEAR_VIEW_SIZE = 200,
|
||||
YEAR_BUFFER_SIZE = 10;
|
||||
YEAR_BUFFER_SIZE = 10,
|
||||
// The min and max values are derived from the ECMAScript spec:
|
||||
// http://ecma-international.org/ecma-262/5.1/#sec-15.9.1.1
|
||||
MIN_DATE = -8640000000000000,
|
||||
MAX_DATE = 8640000000000000;
|
||||
|
||||
DateKeeper.prototype = {
|
||||
get year() {
|
||||
return this.state.dateObj.getUTCFullYear();
|
||||
},
|
||||
|
||||
get month() {
|
||||
return this.state.dateObj.getUTCMonth();
|
||||
},
|
||||
|
||||
get day() {
|
||||
return this.state.dateObj.getUTCDate();
|
||||
},
|
||||
|
||||
/**
|
||||
* Set new date
|
||||
* Initialize DateKeeper
|
||||
* @param {Number} year
|
||||
* @param {Number} month
|
||||
* @param {Number} day
|
||||
* @param {String} min
|
||||
* @param {String} max
|
||||
* @param {Number} firstDayOfWeek
|
||||
* @param {Array<Number>} weekends
|
||||
* @param {Number} calViewSize
|
||||
*/
|
||||
init({ year, month, day, min, max, firstDayOfWeek = 0, weekends = [0], calViewSize = 42 }) {
|
||||
const today = new Date();
|
||||
const isDateSet = year != undefined && month != undefined && day != undefined;
|
||||
|
||||
this.state = {
|
||||
firstDayOfWeek, weekends, calViewSize,
|
||||
min: new Date(min != undefined ? min : MIN_DATE),
|
||||
max: new Date(max != undefined ? max : MAX_DATE),
|
||||
today: this._newUTCDate(today.getFullYear(), today.getMonth(), today.getDate()),
|
||||
weekHeaders: this._getWeekHeaders(firstDayOfWeek, weekends),
|
||||
years: [],
|
||||
months: [],
|
||||
days: [],
|
||||
};
|
||||
|
||||
this.state.dateObj = isDateSet ?
|
||||
this._newUTCDate(year, month, day) :
|
||||
new Date(this.state.today);
|
||||
},
|
||||
/**
|
||||
* Set new date. The year is always treated as full year, so the short-form
|
||||
* is not supported.
|
||||
* @param {Object} date parts
|
||||
* {
|
||||
* {Number} year [optional]
|
||||
@ -48,17 +74,19 @@ function DateKeeper({ year, month, day }, { firstDayOfWeek = 0, weekends = [0],
|
||||
* {Number} date [optional]
|
||||
* }
|
||||
*/
|
||||
set({ year = this.state.year, month = this.state.month, day = this.state.day }) {
|
||||
this._update(year, month, day);
|
||||
set({ year = this.year, month = this.month, day = this.day }) {
|
||||
// Use setUTCFullYear so that year 99 doesn't get parsed as 1999
|
||||
this.state.dateObj.setUTCFullYear(year, month, day);
|
||||
},
|
||||
|
||||
/**
|
||||
* Set date with value
|
||||
* @param {Number} value: Date value
|
||||
* Set selection date
|
||||
* @param {Date} selection
|
||||
*/
|
||||
setValue(value) {
|
||||
const dateObj = new Date(value);
|
||||
this._update(dateObj.getUTCFullYear(), dateObj.getUTCMonth(), dateObj.getUTCDate());
|
||||
setSelection(selection) {
|
||||
this.set({ year: selection.getUTCFullYear(),
|
||||
month: selection.getUTCMonth(),
|
||||
day: selection.getUTCDate() });
|
||||
},
|
||||
|
||||
/**
|
||||
@ -66,8 +94,10 @@ function DateKeeper({ year, month, day }, { firstDayOfWeek = 0, weekends = [0],
|
||||
* @param {Number} month
|
||||
*/
|
||||
setMonth(month) {
|
||||
const lastDayOfMonth = this._newUTCDate(this.state.year, month + 1, 0).getUTCDate();
|
||||
this._update(this.state.year, month, Math.min(this.state.day, lastDayOfMonth));
|
||||
const lastDayOfMonth = this._newUTCDate(this.year, month + 1, 0).getUTCDate();
|
||||
this.set({ year: this.year,
|
||||
month,
|
||||
day: Math.min(this.day, lastDayOfMonth) });
|
||||
},
|
||||
|
||||
/**
|
||||
@ -75,8 +105,10 @@ function DateKeeper({ year, month, day }, { firstDayOfWeek = 0, weekends = [0],
|
||||
* @param {Number} year
|
||||
*/
|
||||
setYear(year) {
|
||||
const lastDayOfMonth = this._newUTCDate(year, this.state.month + 1, 0).getUTCDate();
|
||||
this._update(year, this.state.month, Math.min(this.state.day, lastDayOfMonth));
|
||||
const lastDayOfMonth = this._newUTCDate(year, this.month + 1, 0).getUTCDate();
|
||||
this.set({ year,
|
||||
month: this.month,
|
||||
day: Math.min(this.day, lastDayOfMonth) });
|
||||
},
|
||||
|
||||
/**
|
||||
@ -84,22 +116,10 @@ function DateKeeper({ year, month, day }, { firstDayOfWeek = 0, weekends = [0],
|
||||
* @param {Number} offset
|
||||
*/
|
||||
setMonthByOffset(offset) {
|
||||
const lastDayOfMonth = this._newUTCDate(this.state.year, this.state.month + offset + 1, 0).getUTCDate();
|
||||
this._update(this.state.year, this.state.month + offset, Math.min(this.state.day, lastDayOfMonth));
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the states.
|
||||
* @param {Number} year [description]
|
||||
* @param {Number} month [description]
|
||||
* @param {Number} day [description]
|
||||
*/
|
||||
_update(year, month, day) {
|
||||
// Use setUTCFullYear so that year 99 doesn't get parsed as 1999
|
||||
this.state.dateObj.setUTCFullYear(year, month, day);
|
||||
this.state.year = this.state.dateObj.getUTCFullYear();
|
||||
this.state.month = this.state.dateObj.getUTCMonth();
|
||||
this.state.day = this.state.dateObj.getUTCDate();
|
||||
const lastDayOfMonth = this._newUTCDate(this.year, this.month + offset + 1, 0).getUTCDate();
|
||||
this.set({ year: this.year,
|
||||
month: this.month + offset,
|
||||
day: Math.min(this.day, lastDayOfMonth) });
|
||||
},
|
||||
|
||||
/**
|
||||
@ -111,7 +131,6 @@ function DateKeeper({ year, month, day }, { firstDayOfWeek = 0, weekends = [0],
|
||||
* }
|
||||
*/
|
||||
getMonths() {
|
||||
// TODO: add min/max and step support
|
||||
let months = [];
|
||||
|
||||
for (let i = 0; i < MONTHS_IN_A_YEAR; i++) {
|
||||
@ -133,12 +152,11 @@ function DateKeeper({ year, month, day }, { firstDayOfWeek = 0, weekends = [0],
|
||||
* }
|
||||
*/
|
||||
getYears() {
|
||||
// TODO: add min/max and step support
|
||||
let years = [];
|
||||
|
||||
const firstItem = this.state.years[0];
|
||||
const lastItem = this.state.years[this.state.years.length - 1];
|
||||
const currentYear = this.state.dateObj.getUTCFullYear();
|
||||
const currentYear = this.year;
|
||||
|
||||
// Generate new years array when the year is outside of the first &
|
||||
// last item range. If not, return the cached result.
|
||||
@ -161,30 +179,38 @@ function DateKeeper({ year, month, day }, { firstDayOfWeek = 0, weekends = [0],
|
||||
* Get days for calendar
|
||||
* @return {Array<Object>}
|
||||
* {
|
||||
* {Number} dateValue
|
||||
* {Number} textContent
|
||||
* {Date} dateObj
|
||||
* {Array<String>} classNames
|
||||
* {Boolean} enabled
|
||||
* }
|
||||
*/
|
||||
getDays() {
|
||||
// TODO: add min/max and step support
|
||||
let firstDayOfMonth = this._getFirstCalendarDate(this.state.dateObj, this.state.firstDayOfWeek);
|
||||
// TODO: add step support
|
||||
const firstDayOfMonth = this._getFirstCalendarDate(this.state.dateObj, this.state.firstDayOfWeek);
|
||||
const month = this.month;
|
||||
let days = [];
|
||||
let month = this.state.dateObj.getUTCMonth();
|
||||
|
||||
for (let i = 0; i < this.state.calViewSize; i++) {
|
||||
let dateObj = this._newUTCDate(firstDayOfMonth.getUTCFullYear(), firstDayOfMonth.getUTCMonth(), firstDayOfMonth.getUTCDate() + i);
|
||||
const dateObj = this._newUTCDate(firstDayOfMonth.getUTCFullYear(), firstDayOfMonth.getUTCMonth(), firstDayOfMonth.getUTCDate() + i);
|
||||
let classNames = [];
|
||||
let enabled = true;
|
||||
if (this.state.weekends.includes(dateObj.getUTCDay())) {
|
||||
classNames.push("weekend");
|
||||
}
|
||||
if (month != dateObj.getUTCMonth()) {
|
||||
classNames.push("outside");
|
||||
}
|
||||
if (dateObj.getTime() < this.state.min.getTime() || dateObj.getTime() > this.state.max.getTime()) {
|
||||
classNames.push("out-of-range");
|
||||
enabled = false;
|
||||
}
|
||||
if (this.state.today.getTime() == dateObj.getTime()) {
|
||||
classNames.push("today");
|
||||
}
|
||||
days.push({
|
||||
dateValue: dateObj.getTime(),
|
||||
textContent: dateObj.getUTCDate(),
|
||||
classNames
|
||||
dateObj,
|
||||
classNames,
|
||||
enabled,
|
||||
});
|
||||
}
|
||||
return days;
|
||||
@ -193,20 +219,21 @@ function DateKeeper({ year, month, day }, { firstDayOfWeek = 0, weekends = [0],
|
||||
/**
|
||||
* Get week headers for calendar
|
||||
* @param {Number} firstDayOfWeek
|
||||
* @param {Array<Number>} weekends
|
||||
* @return {Array<Object>}
|
||||
* {
|
||||
* {Number} textContent
|
||||
* {Array<String>} classNames
|
||||
* }
|
||||
*/
|
||||
_getWeekHeaders(firstDayOfWeek) {
|
||||
_getWeekHeaders(firstDayOfWeek, weekends) {
|
||||
let headers = [];
|
||||
let dayOfWeek = firstDayOfWeek;
|
||||
|
||||
for (let i = 0; i < DAYS_IN_A_WEEK; i++) {
|
||||
headers.push({
|
||||
textContent: dayOfWeek % DAYS_IN_A_WEEK,
|
||||
classNames: this.state.weekends.includes(dayOfWeek % DAYS_IN_A_WEEK) ? ["weekend"] : []
|
||||
classNames: weekends.includes(dayOfWeek % DAYS_IN_A_WEEK) ? ["weekend"] : []
|
||||
});
|
||||
dayOfWeek++;
|
||||
}
|
||||
|
@ -24,6 +24,8 @@ function DatePicker(context) {
|
||||
* {Number} year [optional]
|
||||
* {Number} month [optional]
|
||||
* {Number} date [optional]
|
||||
* {String} min
|
||||
* {String} max
|
||||
* {Number} firstDayOfWeek
|
||||
* {Array<Number>} weekends
|
||||
* {Array<String>} monthStrings
|
||||
@ -42,21 +44,10 @@ function DatePicker(context) {
|
||||
* Set initial date picker states.
|
||||
*/
|
||||
_setDefaultState() {
|
||||
const now = new Date();
|
||||
const { year = now.getFullYear(),
|
||||
month = now.getMonth(),
|
||||
day = now.getDate(),
|
||||
firstDayOfWeek,
|
||||
weekends,
|
||||
monthStrings,
|
||||
weekdayStrings,
|
||||
locale,
|
||||
dir } = this.props;
|
||||
const { year, month, day, min, max, firstDayOfWeek, weekends,
|
||||
monthStrings, weekdayStrings, locale, dir } = this.props;
|
||||
const dateKeeper = new DateKeeper({
|
||||
year, month, day
|
||||
}, {
|
||||
firstDayOfWeek,
|
||||
weekends,
|
||||
year, month, day, min, max, firstDayOfWeek, weekends,
|
||||
calViewSize: CAL_VIEW_SIZE
|
||||
});
|
||||
|
||||
@ -74,9 +65,8 @@ function DatePicker(context) {
|
||||
getDayString: new Intl.NumberFormat(locale).format,
|
||||
getWeekHeaderString: weekday => weekdayStrings[weekday],
|
||||
getMonthString: month => monthStrings[month],
|
||||
setValue: ({ dateValue, selectionValue }) => {
|
||||
dateKeeper.setValue(dateValue);
|
||||
this.state.selectionValue = selectionValue;
|
||||
setSelection: ({ selection }) => {
|
||||
dateKeeper.setSelection(selection);
|
||||
this.state.isYearSet = true;
|
||||
this.state.isMonthSet = true;
|
||||
this.state.isDateSet = true;
|
||||
@ -132,7 +122,7 @@ function DatePicker(context) {
|
||||
* Update date picker and its components.
|
||||
*/
|
||||
_update() {
|
||||
const { dateKeeper, selectionValue, isMonthPickerVisible } = this.state;
|
||||
const { dateKeeper, isMonthPickerVisible } = this.state;
|
||||
|
||||
if (isMonthPickerVisible) {
|
||||
this.state.months = dateKeeper.getMonths();
|
||||
@ -144,9 +134,7 @@ function DatePicker(context) {
|
||||
this.components.monthYear.setProps({
|
||||
isVisible: isMonthPickerVisible,
|
||||
dateObj: dateKeeper.state.dateObj,
|
||||
month: dateKeeper.state.month,
|
||||
months: this.state.months,
|
||||
year: dateKeeper.state.year,
|
||||
years: this.state.years,
|
||||
toggleMonthPicker: this.state.toggleMonthPicker
|
||||
});
|
||||
@ -154,10 +142,9 @@ function DatePicker(context) {
|
||||
isVisible: !isMonthPickerVisible,
|
||||
days: this.state.days,
|
||||
weekHeaders: dateKeeper.state.weekHeaders,
|
||||
setValue: this.state.setValue,
|
||||
setSelection: this.state.setSelection,
|
||||
getDayString: this.state.getDayString,
|
||||
getWeekHeaderString: this.state.getWeekHeaderString,
|
||||
selectionValue
|
||||
getWeekHeaderString: this.state.getWeekHeaderString
|
||||
});
|
||||
|
||||
isMonthPickerVisible ?
|
||||
@ -178,7 +165,7 @@ function DatePicker(context) {
|
||||
* Use postMessage to pass the state of picker to the panel.
|
||||
*/
|
||||
_dispatchState() {
|
||||
const { year, month, day } = this.state.dateKeeper.state;
|
||||
const { year, month, day } = this.state.dateKeeper;
|
||||
const { isYearSet, isMonthSet, isDaySet } = this.state;
|
||||
// The panel is listening to window for postMessage event, so we
|
||||
// do postMessage to itself to send data to input boxes.
|
||||
@ -347,8 +334,6 @@ function DatePicker(context) {
|
||||
* {
|
||||
* {Boolean} isVisible
|
||||
* {Date} dateObj
|
||||
* {Number} month
|
||||
* {Number} year
|
||||
* {Array<Object>} months
|
||||
* {Array<Object>} years
|
||||
* {Function} toggleMonthPicker
|
||||
@ -360,14 +345,14 @@ function DatePicker(context) {
|
||||
if (props.isVisible) {
|
||||
this.context.monthYear.classList.add("active");
|
||||
this.components.month.setState({
|
||||
value: props.month,
|
||||
value: props.dateObj.getUTCMonth(),
|
||||
items: props.months,
|
||||
isInfiniteScroll: true,
|
||||
isValueSet: this.state.isMonthSet,
|
||||
smoothScroll: !this.state.firstOpened
|
||||
});
|
||||
this.components.year.setState({
|
||||
value: props.year,
|
||||
value: props.dateObj.getUTCFullYear(),
|
||||
items: props.years,
|
||||
isInfiniteScroll: false,
|
||||
isValueSet: this.state.isYearSet,
|
||||
|
@ -166,6 +166,8 @@
|
||||
weekdayStrings,
|
||||
locale,
|
||||
dir,
|
||||
min: detail.min,
|
||||
max: detail.max,
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
@ -12,7 +12,8 @@
|
||||
--colon-width: 2rem;
|
||||
--day-period-spacing-width: 1rem;
|
||||
--calendar-width: 23.1rem;
|
||||
--date-picker-item-height: 2.4rem;
|
||||
--date-picker-item-height: 2.5rem;
|
||||
--date-picker-item-width: 3.3rem;
|
||||
|
||||
--border: 0.1rem solid #D6D6D6;
|
||||
--border-radius: 0.3rem;
|
||||
@ -21,6 +22,8 @@
|
||||
--font-color: #191919;
|
||||
--fill-color: #EBEBEB;
|
||||
|
||||
--today-fill-color: rgb(212, 212, 212);
|
||||
|
||||
--selected-font-color: #FFFFFF;
|
||||
--selected-fill-color: #0996F8;
|
||||
|
||||
@ -29,10 +32,16 @@
|
||||
--button-font-color-active: #191919;
|
||||
--button-fill-color-active: #D4D4D4;
|
||||
|
||||
--weekday-font-color: #6C6C6C;
|
||||
--weekday-outside-font-color: #6C6C6C;
|
||||
--weekend-font-color: #DA4E44;
|
||||
--weekend-outside-font-color: #FF988F;
|
||||
--weekday-header-font-color: #6C6C6C;
|
||||
--weekend-header-font-color: rgb(218, 78, 68);
|
||||
|
||||
--weekend-font-color: rgb(218, 78, 68);
|
||||
--weekday-outside-font-color: rgb(153, 153, 153);
|
||||
--weekend-outside-font-color: rgb(255, 152, 143);
|
||||
|
||||
--weekday-disabled-font-color: rgba(25, 25, 25, 0.2);
|
||||
--weekend-disabled-font-color: rgba(218, 78, 68, 0.2);
|
||||
--disabled-fill-color: rgba(235, 235, 235, 0.8);
|
||||
|
||||
--disabled-opacity: 0.2;
|
||||
}
|
||||
@ -199,11 +208,11 @@ button.month-year.active::after {
|
||||
}
|
||||
|
||||
.week-header > div {
|
||||
color: var(--weekday-font-color);
|
||||
color: var(--weekday-header-font-color);
|
||||
}
|
||||
|
||||
.week-header > div.weekend {
|
||||
color: var(--weekend-font-color);
|
||||
color: var(--weekend-header-font-color);
|
||||
}
|
||||
|
||||
.days-viewport {
|
||||
@ -224,24 +233,45 @@ button.month-year.active::after {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: var(--date-picker-item-height);
|
||||
margin: 0.05rem 0.15rem;
|
||||
position: relative;
|
||||
justify-content: center;
|
||||
width: 3rem;
|
||||
width: var(--date-picker-item-width);
|
||||
}
|
||||
|
||||
.days-view > div.outside {
|
||||
.days-view > .outside {
|
||||
color: var(--weekday-outside-font-color);
|
||||
}
|
||||
|
||||
.days-view > div.weekend {
|
||||
.days-view > .weekend {
|
||||
color: var(--weekend-font-color);
|
||||
}
|
||||
|
||||
.days-view > div.weekend.outside {
|
||||
.days-view > .weekend.outside {
|
||||
color: var(--weekend-outside-font-color);
|
||||
}
|
||||
|
||||
.days-view > .out-of-range {
|
||||
color: var(--weekday-disabled-font-color);
|
||||
background: var(--disabled-fill-color);
|
||||
}
|
||||
|
||||
.days-view > .out-of-range.weekend {
|
||||
color: var(--weekend-disabled-font-color);
|
||||
}
|
||||
|
||||
.days-view > .out-of-range::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.days-view > div:hover::before,
|
||||
.days-view > .select::before,
|
||||
.days-view > .today::before {
|
||||
top: 5%;
|
||||
bottom: 5%;
|
||||
left: 5%;
|
||||
right: 5%;
|
||||
}
|
||||
|
||||
#time-picker,
|
||||
.month-year-view {
|
||||
display: flex;
|
||||
@ -309,10 +339,6 @@ button.month-year.active::after {
|
||||
border-radius: var(--border-radius);
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0%;
|
||||
bottom: 0%;
|
||||
left: 0%;
|
||||
right: 0%;
|
||||
z-index: -10;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user