diff --git a/packages/main/src/Calendar.hbs b/packages/main/src/Calendar.hbs
index 7fcc4ffda6b0..3c01dd0268b3 100644
--- a/packages/main/src/Calendar.hbs
+++ b/packages/main/src/Calendar.hbs
@@ -7,7 +7,7 @@
id="{{_id}}-daypicker"
?hidden="{{_isDayPickerHidden}}"
format-pattern="{{_formatPattern}}"
- .selectedDates="{{selectedDates}}"
+ .selectedDates="{{_selectedDatesTimestamps}}"
._hidden="{{_isDayPickerHidden}}"
.primaryCalendarType="{{_primaryCalendarType}}"
.selectionMode="{{selectionMode}}"
@@ -23,7 +23,7 @@
id="{{_id}}-MP"
?hidden="{{_isMonthPickerHidden}}"
format-pattern="{{_formatPattern}}"
- .selectedDates="{{selectedDates}}"
+ .selectedDates="{{_selectedDatesTimestamps}}"
._hidden="{{_isMonthPickerHidden}}"
.primaryCalendarType="{{_primaryCalendarType}}"
.minDate="{{minDate}}"
@@ -37,7 +37,7 @@
id="{{_id}}-YP"
?hidden="{{_isYearPickerHidden}}"
format-pattern="{{_formatPattern}}"
- .selectedDates="{{selectedDates}}"
+ .selectedDates="{{_selectedDatesTimestamps}}"
._hidden="{{_isYearPickerHidden}}"
.primaryCalendarType="{{_primaryCalendarType}}"
.minDate="{{minDate}}"
diff --git a/packages/main/src/Calendar.js b/packages/main/src/Calendar.js
index 61453b37b3f7..098bbc2321ff 100644
--- a/packages/main/src/Calendar.js
+++ b/packages/main/src/Calendar.js
@@ -1,8 +1,10 @@
+import CalendarDate from "@ui5/webcomponents-localization/dist/dates/CalendarDate.js";
import RenderScheduler from "@ui5/webcomponents-base/dist/RenderScheduler.js";
import {
isF4,
isF4Shift,
} from "@ui5/webcomponents-base/dist/Keys.js";
+import * as CalendarDateComponent from "./CalendarDate.js";
import CalendarPart from "./CalendarPart.js";
import CalendarHeader from "./CalendarHeader.js";
import DayPicker from "./DayPicker.js";
@@ -73,16 +75,36 @@ const metadata = {
type: Boolean,
},
},
+ managedSlots: true,
+ slots: /** @lends sap.ui.webcomponents.main.Calendar.prototype */ {
+ /**
+ * Defines the selected date or dates (depending on the selectionMode
property) for this calendar as instances of ui5-date
+ *
+ * @type {HTMLElement[]}
+ * @slot
+ * @public
+ */
+ "default": {
+ propertyName: "dates",
+ type: HTMLElement,
+ invalidateOnChildChange: true,
+ },
+ },
events: /** @lends sap.ui.webcomponents.main.Calendar.prototype */ {
/**
- * Fired when the selected dates changed.
+ * Fired when the selected dates change.
+ * Note: If you call preventDefault()
for this event, ui5-calendar
will not
+ * create instances of ui5-date
for the newly selected dates. In that case you should do this manually.
+ *
* @event sap.ui.webcomponents.main.Calendar#selected-dates-change
- * @param {Array} dates The selected dates timestamps
+ * @param {Array} values The selected dates
+ * @param {Array} dates The selected dates as UTC timestamps
* @public
*/
"selected-dates-change": {
detail: {
dates: { type: Array },
+ values: { type: Array },
},
},
},
@@ -93,7 +115,14 @@ const metadata = {
*
*
*
- * The ui5-calendar
can be used stand alone to display the years, months, weeks and days
+ * The ui5-calendar
component allows users to select one or more dates.
+ *
+ * Currently selected dates are represented with instances of ui5-date
as
+ * children of the ui5-calendar
. The value property of each ui5-date
must be a
+ * date string, correctly formatted according to the ui5-calendar
's formatPattern
property.
+ * Whenever the user changes the date selection, ui5-calendar
will automatically create/remove instances
+ * of ui5-date
in itself, unless you prevent this behavior by calling preventDefault()
for the
+ * selected-dates-change
event. This is useful if you want to control the selected dates externally.
*
*
* Usage
@@ -105,7 +134,7 @@ const metadata = {
* Pressing over an year inside the years view
*
*
- * The user can comfirm a date selection by pressing over a date inside the days view.
+ * The user can confirm a date selection by pressing over a date inside the days view.
*
*
* Keyboard Handling
@@ -159,6 +188,7 @@ const metadata = {
* @alias sap.ui.webcomponents.main.Calendar
* @extends CalendarPart
* @tagname ui5-calendar
+ * @appenddocs CalendarDate
* @public
* @since 1.0.0-rc.11
*/
@@ -175,6 +205,36 @@ class Calendar extends CalendarPart {
return calendarCSS;
}
+ /**
+ * @private
+ */
+ get _selectedDatesTimestamps() {
+ return this.dates.map(date => {
+ const value = date.value;
+ return value && !!this.getFormat().parse(value) ? this._getTimeStampFromString(value) / 1000 : undefined;
+ }).filter(date => !!date);
+ }
+
+ /**
+ * @private
+ */
+ _setSelectedDates(selectedDates) {
+ const selectedValues = selectedDates.map(timestamp => this.getFormat().format(new Date(timestamp * 1000), true)); // Format as UTC
+ const valuesInDOM = [...this.dates].map(dateElement => dateElement.value);
+
+ // Remove all elements for dates that are no longer selected
+ this.dates.filter(dateElement => !selectedValues.includes(dateElement.value)).forEach(dateElement => {
+ this.removeChild(dateElement);
+ });
+
+ // Create tags for the selected dates that don't already exist in DOM
+ selectedValues.filter(value => !valuesInDOM.includes(value)).forEach(value => {
+ const dateElement = document.createElement("ui5-date");
+ dateElement.value = value;
+ this.appendChild(dateElement);
+ });
+ }
+
async onAfterRendering() {
await RenderScheduler.whenFinished(); // Await for the current picker to render and then ask if it has previous/next pages
this._previousButtonDisabled = !this._currentPickerDOM._hasPreviousPage();
@@ -237,10 +297,16 @@ class Calendar extends CalendarPart {
onSelectedDatesChange(event) {
const timestamp = event.detail.timestamp;
const selectedDates = event.detail.dates;
+ const datesValues = selectedDates.map(ts => {
+ const calendarDate = CalendarDate.fromTimestamp(ts * 1000, this._primaryCalendarType);
+ return this.getFormat().format(calendarDate.toUTCJSDate(), true);
+ });
this.timestamp = timestamp;
- this.selectedDates = selectedDates;
- this.fireEvent("selected-dates-change", { timestamp, dates: [...selectedDates] });
+ const defaultPrevented = !this.fireEvent("selected-dates-change", { timestamp, dates: [...selectedDates], values: datesValues }, true);
+ if (!defaultPrevented) {
+ this._setSelectedDates(selectedDates);
+ }
}
onSelectedMonthChange(event) {
@@ -267,8 +333,28 @@ class Calendar extends CalendarPart {
}
}
+ /**
+ * Returns an array of UTC timestamps, representing the selected dates.
+ * @protected
+ * @deprecated
+ */
+ get selectedDates() {
+ return this._selectedDatesTimestamps;
+ }
+
+ /**
+ * Creates instances of ui5-date
inside this ui5-calendar
with values, equal to the provided UTC timestamps
+ * @protected
+ * @deprecated
+ * @param selectedDates Array of UTC timestamps
+ */
+ set selectedDates(selectedDates) {
+ this._setSelectedDates(selectedDates);
+ }
+
static get dependencies() {
return [
+ CalendarDateComponent.default,
CalendarHeader,
DayPicker,
MonthPicker,
diff --git a/packages/main/src/CalendarDate.js b/packages/main/src/CalendarDate.js
new file mode 100644
index 000000000000..d733d2074ec5
--- /dev/null
+++ b/packages/main/src/CalendarDate.js
@@ -0,0 +1,44 @@
+import UI5Element from "@ui5/webcomponents-base/dist/UI5Element.js";
+
+/**
+* @public
+*/
+const metadata = {
+ tag: "ui5-date",
+ properties: /** @lends sap.ui.webcomponents.main.CalendarDate.prototype */ {
+
+ /**
+ * The date formatted according to the formatPattern
property of the ui5-calendar
that hosts the ui5-date
+ *
+ * @type {string}
+ * @public
+ */
+ value: {
+ type: String,
+ },
+ },
+};
+
+/**
+ * @class
+ *
+ *
+ *
+ * The ui5-date
component defines a calendar date to be used inside ui5-calendar
+ *
+ * @constructor
+ * @author SAP SE
+ * @alias sap.ui.webcomponents.main.CalendarDate
+ * @extends sap.ui.webcomponents.base.UI5Element
+ * @tagname ui5-date
+ * @public
+ */
+class CalendarDate extends UI5Element {
+ static get metadata() {
+ return metadata;
+ }
+}
+
+CalendarDate.define();
+
+export default CalendarDate;
diff --git a/packages/main/src/CalendarPart.js b/packages/main/src/CalendarPart.js
index 8ce2d2673485..d65ebdc4f701 100644
--- a/packages/main/src/CalendarPart.js
+++ b/packages/main/src/CalendarPart.js
@@ -13,22 +13,11 @@ const metadata = {
* The timestamp of the currently focused date. Set this property to move the component's focus to a certain date.
* Node: Timestamp is 10-digit Integer representing the seconds (not milliseconds) since the Unix Epoch.
* @type {Integer}
- * @public
+ * @protected
*/
timestamp: {
type: Integer,
},
-
- /**
- * An array of UTC timestamps representing the selected date or dates depending on the capabilities of the picker component.
- * @type {Array}
- * @public
- */
- selectedDates: {
- type: Integer,
- multiple: true,
- compareValues: true,
- },
},
};
diff --git a/packages/main/src/DatePicker.js b/packages/main/src/DatePicker.js
index fc6fcb9d371e..a87358bb5e72 100644
--- a/packages/main/src/DatePicker.js
+++ b/packages/main/src/DatePicker.js
@@ -23,6 +23,7 @@ import Icon from "./Icon.js";
import Button from "./Button.js";
import ResponsivePopover from "./ResponsivePopover.js";
import Calendar from "./Calendar.js";
+import * as CalendarDateComponent from "./CalendarDate.js";
import Input from "./Input.js";
import InputType from "./types/InputType.js";
import DatePickerTemplate from "./generated/templates/DatePickerTemplate.lit.js";
@@ -388,12 +389,8 @@ class DatePicker extends DateComponentBase {
* @protected
*/
get _calendarSelectedDates() {
- if (!this.value) {
- return [];
- }
-
- if (this._checkValueValidity(this.value)) {
- return [getRoundedTimestamp(this.dateValueUTC.getTime())];
+ if (this.value && this._checkValueValidity(this.value)) {
+ return [this.value];
}
return [];
@@ -640,9 +637,8 @@ class DatePicker extends DateComponentBase {
* @protected
*/
onSelectedDatesChange(event) {
- const timestamp = event.detail.dates && event.detail.dates[0];
- const calendarDate = CalendarDate.fromTimestamp(timestamp * 1000, this._primaryCalendarType);
- const newValue = this.getFormat().format(calendarDate.toUTCJSDate(), true);
+ event.preventDefault();
+ const newValue = event.detail.values && event.detail.values[0];
this._updateValueAndFireEvents(newValue, true, ["change", "value-changed"]);
this._focusInputAfterClose = true;
@@ -728,6 +724,7 @@ class DatePicker extends DateComponentBase {
Icon,
ResponsivePopover,
Calendar,
+ CalendarDateComponent.default,
Input,
Button,
];
diff --git a/packages/main/src/DatePickerPopover.hbs b/packages/main/src/DatePickerPopover.hbs
index 560fa827dd04..adc9b6bd4954 100644
--- a/packages/main/src/DatePickerPopover.hbs
+++ b/packages/main/src/DatePickerPopover.hbs
@@ -44,14 +44,17 @@
primary-calendar-type="{{_primaryCalendarType}}"
format-pattern="{{_formatPattern}}"
timestamp="{{_calendarTimestamp}}"
- .selectedDates={{_calendarSelectedDates}}
.selectionMode="{{_calendarSelectionMode}}"
.minDate="{{minDate}}"
.maxDate="{{maxDate}}"
@ui5-selected-dates-change="{{onSelectedDatesChange}}"
?hide-week-numbers="{{hideWeekNumbers}}"
._currentPicker="{{_calendarCurrentPicker}}"
- >
+ >
+ {{#each _calendarSelectedDates}}
+
+ {{/each}}
+
{{/inline}}
{{#*inline "footer"}}{{/inline}}
diff --git a/packages/main/src/DateRangePicker.js b/packages/main/src/DateRangePicker.js
index c2e9d8bb3447..d2629ac323d5 100644
--- a/packages/main/src/DateRangePicker.js
+++ b/packages/main/src/DateRangePicker.js
@@ -24,6 +24,14 @@ const metadata = {
type: String,
defaultValue: "-",
},
+
+ /**
+ * The first date in the range during selection (this is a temporary value, not the first date in the value range)
+ * @private
+ */
+ _tempValue: {
+ type: String,
+ },
},
};
@@ -104,7 +112,13 @@ class DateRangePicker extends DatePicker {
* @override
*/
get _calendarSelectedDates() {
- return [this._firstDateTimestamp, this._lastDateTimestamp].filter(date => !!date);
+ if (this._tempValue) {
+ return [this._tempValue];
+ }
+ if (this.value && this._checkValueValidity(this.value)) {
+ return this._splitValueByDelimiter(this.value);
+ }
+ return [];
}
/**
@@ -178,13 +192,21 @@ class DateRangePicker extends DatePicker {
* @override
*/
onSelectedDatesChange(event) {
- const selectedDates = event.detail.dates;
- if (selectedDates.length !== 2) { // Do nothing until the user selects 2 dates, we don't change any state at all for one date
+ event.preventDefault(); // never let the calendar update its own dates, the parent component controls them
+ const values = event.detail.values;
+
+ if (values.length === 0) {
+ return;
+ }
+
+ if (values.length === 1) { // Do nothing until the user selects 2 dates, we don't change any state at all for one date
+ this._tempValue = values[0];
return;
}
- const newValue = this._buildValue(...selectedDates); // the value will be normalized so we don't need to order them here
+ const newValue = this._buildValue(...event.detail.dates); // the value will be normalized so we don't need to order them here
this._updateValueAndFireEvents(newValue, true, ["change", "value-changed"]);
+ this._tempValue = "";
this._focusInputAfterClose = true;
this.closePicker();
}
diff --git a/packages/main/src/DateTimePicker.js b/packages/main/src/DateTimePicker.js
index dff30146ce6e..7ebb6928b82b 100644
--- a/packages/main/src/DateTimePicker.js
+++ b/packages/main/src/DateTimePicker.js
@@ -251,15 +251,15 @@ class DateTimePicker extends DatePicker {
return fallback ? localeData.getCombinedDateTimePattern("medium", "medium", this._primaryCalendarType) : this.formatPattern;
}
- get _effectiveCalendarTimestamp() {
- return this._previewValues.calendarTimestamp ? this._previewValues.calendarTimestamp : this._calendarTimestamp;
+ get _calendarTimestamp() {
+ return this._previewValues.calendarTimestamp ? this._previewValues.calendarTimestamp : super._calendarTimestamp;
}
- get _effectiveCalendarSelectedDates() {
- return this._previewValues.calendarSelectedDate ? [this._previewValues.calendarSelectedDate] : this._calendarSelectedDates;
+ get _calendarSelectedDates() {
+ return this._previewValues.calendarValue ? [this._previewValues.calendarValue] : super._calendarSelectedDates;
}
- get _effectiveTimeValue() {
+ get _timeSelectionValue() {
return this._previewValues.timeSelectionValue ? this._previewValues.timeSelectionValue : this.value;
}
@@ -315,10 +315,12 @@ class DateTimePicker extends DatePicker {
* @override
*/
onSelectedDatesChange(event) {
+ event.preventDefault();
+
this._previewValues = {
...this._previewValues,
calendarTimestamp: event.detail.timestamp,
- calendarSelectedDate: event.detail.dates[0],
+ calendarValue: event.detail.values[0],
};
}
@@ -347,7 +349,7 @@ class DateTimePicker extends DatePicker {
}
get _submitDisabled() {
- return !this._effectiveCalendarSelectedDates || !this._effectiveCalendarSelectedDates.length;
+ return !this._calendarSelectedDates || !this._calendarSelectedDates.length;
}
/**
@@ -414,8 +416,8 @@ class DateTimePicker extends DatePicker {
}
getSelectedDateTime() {
- const selectedDate = CalendarDate.fromTimestamp(this._effectiveCalendarSelectedDates[0] * 1000).toLocalJSDate();
- const selectedTime = this.getFormat().parse(this._effectiveTimeValue);
+ const selectedDate = this.getFormat().parse(this._calendarSelectedDates[0]);
+ const selectedTime = this.getFormat().parse(this._timeSelectionValue);
selectedDate.setHours(selectedTime.getHours());
selectedDate.setMinutes(selectedTime.getMinutes());
selectedDate.setSeconds(selectedTime.getSeconds());
diff --git a/packages/main/src/DateTimePickerPopover.hbs b/packages/main/src/DateTimePickerPopover.hbs
index 1bd535a52a29..680000125b11 100644
--- a/packages/main/src/DateTimePickerPopover.hbs
+++ b/packages/main/src/DateTimePickerPopover.hbs
@@ -19,15 +19,18 @@
id="{{_id}}-calendar"
primary-calendar-type="{{_primaryCalendarType}}"
format-pattern="{{_formatPattern}}"
- timestamp="{{_effectiveCalendarTimestamp}}"
- .selectedDates={{_effectiveCalendarSelectedDates}}
+ timestamp="{{_calendarTimestamp}}"
.selectionMode="{{_calendarSelectionMode}}"
.minDate="{{minDate}}"
.maxDate="{{maxDate}}"
@ui5-selected-dates-change="{{onSelectedDatesChange}}"
?hide-week-numbers="{{hideWeekNumbers}}"
._currentPicker="{{_calendarCurrentPicker}}"
- >
+ >
+ {{#each _calendarSelectedDates}}
+
+ {{/each}}
+
{{#unless phone}}
@@ -36,7 +39,7 @@
diff --git a/packages/main/src/MonthPicker.js b/packages/main/src/MonthPicker.js
index 5ac5cd3dabc3..5bfe9ea38181 100644
--- a/packages/main/src/MonthPicker.js
+++ b/packages/main/src/MonthPicker.js
@@ -14,6 +14,7 @@ import {
isPageUp,
isPageDown,
} from "@ui5/webcomponents-base/dist/Keys.js";
+import Integer from "@ui5/webcomponents-base/dist/types/Integer.js";
import getLocale from "@ui5/webcomponents-base/dist/locale/getLocale.js";
import CalendarPart from "./CalendarPart.js";
import MonthPickerTemplate from "./generated/templates/MonthPickerTemplate.lit.js";
@@ -24,6 +25,17 @@ import styles from "./generated/themes/MonthPicker.css.js";
const metadata = {
tag: "ui5-monthpicker",
properties: /** @lends sap.ui.webcomponents.main.MonthPicker.prototype */ {
+ /**
+ * An array of UTC timestamps representing the selected date or dates depending on the capabilities of the picker component.
+ * @type {Array}
+ * @public
+ */
+ selectedDates: {
+ type: Integer,
+ multiple: true,
+ compareValues: true,
+ },
+
_months: {
type: Object,
multiple: true,
diff --git a/packages/main/src/YearPicker.js b/packages/main/src/YearPicker.js
index 827a581b7a20..c108147a8c33 100644
--- a/packages/main/src/YearPicker.js
+++ b/packages/main/src/YearPicker.js
@@ -13,6 +13,7 @@ import {
isPageUp,
isPageDown,
} from "@ui5/webcomponents-base/dist/Keys.js";
+import Integer from "@ui5/webcomponents-base/dist/types/Integer.js";
import getLocale from "@ui5/webcomponents-base/dist/locale/getLocale.js";
import CalendarDate from "@ui5/webcomponents-localization/dist/dates/CalendarDate.js";
import { getMaxCalendarDate } from "@ui5/webcomponents-localization/dist/dates/ExtremeDates.js";
@@ -26,6 +27,17 @@ import styles from "./generated/themes/YearPicker.css.js";
const metadata = {
tag: "ui5-yearpicker",
properties: /** @lends sap.ui.webcomponents.main.YearPicker.prototype */ {
+ /**
+ * An array of UTC timestamps representing the selected date or dates depending on the capabilities of the picker component.
+ * @type {Array}
+ * @public
+ */
+ selectedDates: {
+ type: Integer,
+ multiple: true,
+ compareValues: true,
+ },
+
_years: {
type: Object,
multiple: true,
diff --git a/packages/main/test/pages/Calendar.html b/packages/main/test/pages/Calendar.html
index a80fec9ec263..55541e0ad4aa 100644
--- a/packages/main/test/pages/Calendar.html
+++ b/packages/main/test/pages/Calendar.html
@@ -56,6 +56,14 @@
+
+ Calendar with preset dates
+
+
+
+
+
+