diff --git a/packages/base/src/types/CalendarSelection.js b/packages/base/src/types/CalendarSelection.js new file mode 100644 index 000000000000..3850573e37b9 --- /dev/null +++ b/packages/base/src/types/CalendarSelection.js @@ -0,0 +1,17 @@ +import DataType from "./DataType.js"; + +const CalendarSelections = { + Single: "Single", + Multiple: "Multiple", + Range: "Range", +}; + +class CalendarSelection extends DataType { + static isValid(value) { + return !!CalendarSelections[value]; + } +} + +CalendarSelection.generateTypeAcessors(CalendarSelections); + +export default CalendarSelection; diff --git a/packages/main/src/Calendar.hbs b/packages/main/src/Calendar.hbs index f1d364cec13c..50ed216837a2 100644 --- a/packages/main/src/Calendar.hbs +++ b/packages/main/src/Calendar.hbs @@ -1,7 +1,8 @@
CalendarSelection
.
+ * Accepted property values are:CalendarSelection.Single
- enables a single date selection.(default value)CalendarSelection.Range
- enables selection of a date range.CalendarSelection.Multiple
- enables selection of multiple dates.ui5-calendar
can be used stand alone to display the years, months, weeks and days
+ * ui5-calendar
provides advanced keyboard handling.
-* If the ui5-calendar
is focused the user can
-* choose a picker by using the following shortcuts: ui5-calendar
provides advanced keyboard handling.
+ * If the ui5-calendar
is focused the user can
+ * choose a picker by using the following shortcuts: ui5-calendar
can be used standale to display the years, months, weeks and days,
- * but the main purpose of the ui5-calendar
is to be used within a ui5-date-picker
.
+ * import "@ui5/webcomponents/dist/Calendar";
*
* @constructor
* @author SAP SE
@@ -189,6 +230,7 @@ const metadata = {
* @extends sap.ui.webcomponents.base.UI5Element
* @tagname ui5-calendar
* @public
+ * @since 1.0.0-rc.11
*/
class Calendar extends UI5Element {
static get metadata() {
@@ -219,6 +261,7 @@ class Calendar extends UI5Element {
this._oMonth.onSelectedDatesChange = this._handleSelectedDatesChange.bind(this);
this._oMonth.onNavigate = this._handleMonthNavigate.bind(this);
+
this._monthPicker = {};
this._monthPicker._hidden = true;
this._monthPicker.onSelectedMonthChange = this._handleSelectedMonthChange.bind(this);
@@ -247,12 +290,14 @@ class Calendar extends UI5Element {
this._oMonth.formatPattern = this._formatPattern;
this._oMonth.timestamp = this._timestamp;
- this._oMonth.selectedDates = [...this._selectedDates];
+ this._oMonth.selectedDates = [...this.selectedDates];
this._oMonth.primaryCalendarType = this._primaryCalendarType;
+ this._oMonth.selection = this.selection;
this._oMonth.minDate = this.minDate;
this._oMonth.maxDate = this.maxDate;
this._header.monthText = localeData.getMonths("wide", this._primaryCalendarType)[this._month];
this._header.yearText = oYearFormat.format(this._localDate, true);
+ this._header.tabIndex = "-1";
// month picker
this._monthPicker.primaryCalendarType = this._primaryCalendarType;
@@ -270,6 +315,10 @@ class Calendar extends UI5Element {
this._refreshNavigationButtonsState();
}
+ onAfterRendering() {
+ this._setDayPickerCurrentIndex(this._calendarDate, false);
+ }
+
_refreshNavigationButtonsState() {
const minDateParsed = this.minDate && this.getFormat().parse(this.minDate);
const maxDateParsed = this.maxDate && this.getFormat().parse(this.maxDate);
@@ -334,6 +383,30 @@ class Calendar extends UI5Element {
}
}
+ get dayPicker() {
+ return this.shadowRoot.querySelector("ui5-daypicker");
+ }
+
+ get monthPicker() {
+ return this.shadowRoot.querySelector("ui5-monthpicker");
+ }
+
+ get yearPicker() {
+ return this.shadowRoot.querySelector("ui5-yearpicker");
+ }
+
+ get header() {
+ return this.shadowRoot.querySelector("ui5-calendar-header");
+ }
+
+ get monthButton() {
+ return this.header.shadowRoot.querySelector("[data-sap-show-picker='Month']");
+ }
+
+ get yearButton() {
+ return this.header.shadowRoot.querySelector("[data-sap-show-picker='Year']");
+ }
+
get _timestamp() {
return this.timestamp !== undefined ? this.timestamp : Math.floor(new Date().getTime() / 1000);
}
@@ -416,14 +489,84 @@ class Calendar extends UI5Element {
this._hideMonthPicker();
}
}
+
+ if (isTabNext(event)) {
+ this._handleTabNext(event);
+ }
+
+ if (isTabPrevious(event)) {
+ this._handleTabPrevous(event);
+ }
}
- _handleSelectedDatesChange(event) {
- this.selectedDates = [...event.detail.dates];
+ _handleTabNext(event) {
+ const target = event.target;
+
+ if (target.tagName === "UI5-DAYPICKER" || target.tagName === "UI5-MONTHPICKER" || target.tagName === "UI5-YEARPICKER") {
+ if (this.monthButton.getAttribute("hidden") === null) {
+ this.monthButton.focus();
+ } else {
+ this.yearButton.focus();
+ }
+ event.preventDefault();
+ } else if (target.tagName === "UI5-CALENDAR-HEADER" && event.path[0].getAttribute("data-sap-show-picker") === "Month") {
+ this.yearButton.focus();
+ event.preventDefault();
+ } else {
+ this._setPickerCurrentTabindex(-1);
+ }
+ }
+
+ _handleTabPrevous(event) {
+ const target = event.target;
+
+ if (target.tagName === "UI5-CALENDAR-HEADER" && event.path[0].getAttribute("data-sap-show-picker") === "Month") {
+ this._moveFocusToPickerContent();
+ event.preventDefault();
+ } else if (target.tagName === "UI5-CALENDAR-HEADER" && event.path[0].getAttribute("data-sap-show-picker") === "Year") {
+ if (this.monthButton.getAttribute("hidden") === null) {
+ this.monthButton.focus();
+ } else {
+ this._moveFocusToPickerContent();
+ }
+ event.preventDefault();
+ }
+ }
+
+ _moveFocusToPickerContent() {
+ if (!this._oMonth._hidden) {
+ this.dayPicker._itemNav.focusCurrent();
+ } else if (!this._monthPicker._hidden) {
+ this.monthPicker._itemNav.focusCurrent();
+ } else {
+ this.yearPicker._itemNav.focusCurrent();
+ }
+ }
+
+ _onfocusout(event) {
+ this._header.tabIndex = "-1";
+ this._setPickerCurrentTabindex(0);
+ }
+
+ _setPickerCurrentTabindex(index) {
+ if (this.dayPicker) {
+ this.dayPicker._setCurrentItemTabIndex(index);
+ }
+
+ if (this.monthPicker) {
+ this.monthPicker._setCurrentItemTabIndex(index);
+ }
- this.timestamp = this.selectedDates[0];
+ if (this.yearPicker) {
+ this.yearPicker._setCurrentItemTabIndex(index);
+ }
+ }
- this.fireEvent("selected-dates-change", { dates: event.detail.dates });
+ _handleSelectedDatesChange(event) {
+ const selectedDates = event.detail.dates;
+ this.timestamp = selectedDates[selectedDates.length - 1];
+ this.selectedDates = [...selectedDates];
+ this.fireEvent("selected-dates-change", { dates: selectedDates });
}
_handleMonthNavigate(event) {
@@ -444,16 +587,14 @@ class Calendar extends UI5Element {
let fistDayOfMonthIndex = -1;
// focus first day of the month
- const dayPicker = this.shadowRoot.querySelector("[ui5-daypicker]");
-
- dayPicker._getVisibleDays(targetDate).forEach((date, index) => {
+ this.dayPicker._getVisibleDays(targetDate).forEach((date, index) => {
if (date.getDate() === 1 && (fistDayOfMonthIndex === -1)) {
fistDayOfMonthIndex = index;
}
});
- dayPicker._itemNav.currentIndex = fistDayOfMonthIndex;
- dayPicker._itemNav.focusCurrent();
+ this.dayPicker._itemNav.currentIndex = fistDayOfMonthIndex;
+ this.dayPicker._itemNav.focusCurrent();
}
_handleSelectedMonthChange(event) {
@@ -465,7 +606,7 @@ class Calendar extends UI5Element {
this._monthPicker.timestamp = this.timestamp;
this._hideMonthPicker();
- this._setDayPickerCurrentIndex(oNewDate);
+ this._setDayPickerCurrentIndex(oNewDate, true);
}
_handleSelectedYearChange(event) {
@@ -477,14 +618,19 @@ class Calendar extends UI5Element {
this._yearPicker.timestamp = this.timestamp;
this._hideYearPicker();
- this._setDayPickerCurrentIndex(oNewDate);
+ this._setDayPickerCurrentIndex(oNewDate, true);
}
- _setDayPickerCurrentIndex(calDate) {
+ async _setDayPickerCurrentIndex(calDate, applyFocus) {
+ await RenderScheduler.whenFinished();
const currentDate = new CalendarDate(calDate);
- const dayPicker = this.shadowRoot.querySelector("[ui5-daypicker]");
- const currentDateIndex = dayPicker._getVisibleDays(currentDate).findIndex(date => date.valueOf() === currentDate.valueOf());
- dayPicker._itemNav.currentIndex = currentDateIndex;
+ const currentDateIndex = this.dayPicker._getVisibleDays(currentDate).findIndex(date => date.valueOf() === currentDate.valueOf());
+ this.dayPicker._itemNav.currentIndex = currentDateIndex;
+ if (applyFocus) {
+ this.dayPicker._itemNav.focusCurrent();
+ } else {
+ this.dayPicker._itemNav.update();
+ }
}
_handleMonthButtonPress() {
@@ -496,7 +642,6 @@ class Calendar extends UI5Element {
_handleYearButtonPress() {
this._hideMonthPicker();
-
this[`_${this._yearPicker._hidden ? "show" : "hide"}YearPicker`]();
}
@@ -545,9 +690,8 @@ class Calendar extends UI5Element {
const minCalendarDateYear = CalendarDate.fromTimestamp(this._getMinCalendarDate(), this._primaryCalendarType).getYear();
// focus first day of the month
- const dayPicker = this.shadowRoot.querySelector("[ui5-daypicker]");
- const currentMonthDate = dayPicker._calendarDate.setMonth(dayPicker._calendarDate.getMonth());
- const lastMonthDate = dayPicker._calendarDate.setMonth(dayPicker._calendarDate.getMonth() - 1);
+ const currentMonthDate = this.dayPicker._calendarDate.setMonth(this.dayPicker._calendarDate.getMonth());
+ const lastMonthDate = this.dayPicker._calendarDate.setMonth(this.dayPicker._calendarDate.getMonth() - 1);
// set the date to last day of last month
currentMonthDate.setDate(-1);
@@ -559,7 +703,7 @@ class Calendar extends UI5Element {
return;
}
- dayPicker._getVisibleDays(lastMonthDate).forEach((date, index) => {
+ this.dayPicker._getVisibleDays(lastMonthDate).forEach((date, index) => {
const isSameDate = currentMonthDate.getDate() === date.getDate();
const isSameMonth = currentMonthDate.getMonth() === date.getMonth();
@@ -570,10 +714,10 @@ class Calendar extends UI5Element {
if (lastDayOfMonthIndex !== -1) {
// find the DOM for the last day index
- const lastDay = dayPicker.shadowRoot.querySelectorAll(".ui5-dp-content .ui5-dp-item")[lastDayOfMonthIndex];
+ const lastDay = this.dayPicker.shadowRoot.querySelectorAll(".ui5-dp-content .ui5-dp-item")[lastDayOfMonthIndex];
// update current item in ItemNavigation
- dayPicker._itemNav.current = lastDayOfMonthIndex;
+ this.dayPicker._itemNav.current = lastDayOfMonthIndex;
// focus the item
lastDay.focus();
diff --git a/packages/main/src/CalendarHeader.hbs b/packages/main/src/CalendarHeader.hbs
index cab203d3ceac..aa23e27e30b0 100644
--- a/packages/main/src/CalendarHeader.hbs
+++ b/packages/main/src/CalendarHeader.hbs
@@ -26,7 +26,7 @@
class="ui5-calheader-arrowbtn ui5-calheader-middlebtn"
?hidden="{{_isMonthButtonHidden}}"
type="{{_btn1.type}}"
- tabindex="0"
+ tabindex="{{_tabIndex}}"
@click={{_showMonthPicker}}
data-sap-show-picker="Month"
>
@@ -37,7 +37,7 @@
id="{{_id}}-btn2"
class="ui5-calheader-arrowbtn ui5-calheader-middlebtn"
type="{{_btn2.type}}"
- tabindex="0"
+ tabindex="{{_tabIndex}}"
@click={{_showYearPicker}}
data-sap-show-picker="Year"
>
diff --git a/packages/main/src/CalendarHeader.js b/packages/main/src/CalendarHeader.js
index 65470d6f6d1d..4244902f1e21 100644
--- a/packages/main/src/CalendarHeader.js
+++ b/packages/main/src/CalendarHeader.js
@@ -46,6 +46,10 @@ const metadata = {
_isMonthButtonHidden: {
type: Boolean,
},
+ _tabIndex: {
+ type: String,
+ defaultValue: "0",
+ },
},
events: {
"previous-press": {},
diff --git a/packages/main/src/DatePicker.hbs b/packages/main/src/DatePicker.hbs
index 4c30167a1175..1b658c85b523 100644
--- a/packages/main/src/DatePicker.hbs
+++ b/packages/main/src/DatePicker.hbs
@@ -1,7 +1,7 @@
CalendarSelection.Single
- enables a single date selection.(default value)CalendarSelection.Range
- enables selection of a date range.CalendarSelection.Multiple
- enables selection of multiple dates.++ +
++ +
++ +
++ +
++ +
++ +
++ +
++ +