UNPKG

@openui5/sap.m

Version:

OpenUI5 UI Library sap.m

1,544 lines (1,314 loc) 52.8 kB
/*! * OpenUI5 * (c) Copyright 2009-2023 SAP SE or an SAP affiliate company. * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. */ // Provides control sap.m.SinglePlanningCalendar. sap.ui.define([ './library', './PlanningCalendarHeader', './SegmentedButtonItem', "./SinglePlanningCalendarWeekView", './SinglePlanningCalendarGrid', './SinglePlanningCalendarMonthGrid', './SinglePlanningCalendarRenderer', 'sap/base/Log', 'sap/ui/core/Control', 'sap/ui/core/InvisibleText', 'sap/ui/core/ResizeHandler', 'sap/ui/core/format/DateFormat', 'sap/ui/unified/calendar/CalendarDate', 'sap/ui/unified/DateRange', 'sap/ui/unified/DateTypeRange', 'sap/ui/unified/library', 'sap/ui/base/ManagedObjectObserver', "sap/ui/core/date/UI5Date", "sap/ui/thirdparty/jquery", "sap/ui/core/date/CalendarWeekNumbering" ], function( library, PlanningCalendarHeader, SegmentedButtonItem, SinglePlanningCalendarWeekView, SinglePlanningCalendarGrid, SinglePlanningCalendarMonthGrid, SinglePlanningCalendarRenderer, Log, Control, InvisibleText, ResizeHandler, DateFormat, CalendarDate, DateRange, DateTypeRange, unifiedLibrary, ManagedObjectObserver, UI5Date, jQuery, CalendarWeekNumbering ) { "use strict"; var PlanningCalendarStickyMode = library.PlanningCalendarStickyMode; var SinglePlanningCalendarSelectionMode = library.SinglePlanningCalendarSelectionMode; var HEADER_RESIZE_HANDLER_ID = "_sHeaderResizeHandlerId"; var MAX_NUMBER_OF_VIEWS_IN_SEGMENTED_BUTTON = 4; var SEGMENTEDBUTTONITEM__SUFFIX = "--item"; /** * Constructor for a new <code>SinglePlanningCalendar</code>. * * @param {string} [sId] id for the new control, generated automatically if no id is given * @param {object} [mSettings] initial settings for the new control * * @class * * Displays a calendar of a single entity (such as person, resource) for the selected time interval. * * <h3>Overview</h3> * * <b>Note:</b> The application developer should add dependency to <code>sap.ui.unified</code> library on application level * to ensure that the library is loaded before the module dependencies will be required. * The <code>SinglePlanningCalendar</code> uses parts of the <code>sap.ui.unified</code> library. * This library will be loaded after the <code>SinglePlanningCalendar</code>, if it wasn't previously loaded. * This could lead to CSP compliance issues and adds an additional waiting time when a <code>SinglePlanningCalendar</code> is used for the first time. * To prevent this, apps using the <code>SinglePlanningCalendar</code> must also load the * <code>sap.ui.unified</code> library in advance. * * The <code>SinglePlanningCalendar</code> has the following structure: * * <ul> * <li>A <code>PlanningCalendarHeader</code> at the top. It contains the <code>title</code> set from the * corresponding property, the <code>SegmentedButton</code>, which facilitates navigation through the views, * controls, passed to the <code>actions</code> aggregation and the navigation, assisting the user in * choosing the desired time period. The views, either custom or not, can be configured and passed through the * <code>views</code> aggregation. * * To create custom views, extend the <code>SinglePlanningCalendarView</code> basic view class. It defines three * methods that should be overwritten: <code>getEntityCount</code>, <code>getScrollEntityCount</code> and * <code>calculateStartDate</code> * <ul> * <li><code>getEntityCount</code> - returns number of columns to be displayed</li> * <li><code>getScrollEntityCount</code> - used when next and previous arrows in the calendar are used. * For example, in work week view, the <code>getEntityCount</code> returns 5 (5 columns from Monday to * Friday), but when next arrow is selected, the control navigates 7 days ahead and * <code>getScrollEntityCount</code> returns 7.</li> * <li><code>calculateStartDate</code> - calculates the first day displayed in the calendar based on the * <code>startDate</code> property of the <code>SinglePlanningCalendar</code>. For example, it returns the * first date of a month or a week to display the first 10 days of the month.</li> * </ul> * * <li>A <code>SinglePlanningCalendarGrid</code> or <code>SinglePlanningCalendarMonthGrid</code>, which displays the appointments, set to the visual time range. * An all-day appointment is an appointment which starts at 00:00 and ends in 00:00 on any day in the future. * </ul> * * @extends sap.ui.core.Control * * @author SAP SE * @version 1.117.4 * * @constructor * @public * @since 1.61 * @alias sap.m.SinglePlanningCalendar */ var SinglePlanningCalendar = Control.extend("sap.m.SinglePlanningCalendar", /** @lends sap.m.SinglePlanningCalendar.prototype */ { metadata : { library : "sap.m", properties : { /** * Determines the title of the <code>SinglePlanningCalendar</code>. */ title: { type : "string", group : "Appearance", defaultValue : "" }, /** * Determines the start date of the grid, as a UI5Date or JavaScript Date object. It is considered as a local date. * The time part will be ignored. The current date is used as default. */ startDate: { type : "object", group : "Data" }, /** * Determines the start hour of the grid to be shown if the <code>fullDay</code> property is set to * <code>false</code>. Otherwise the previous hours are displayed as non-working. The passed hour is * considered as 24-hour based. */ startHour: {type: "int", group: "Data", defaultValue: 0}, /** * Determines the end hour of the grid to be shown if the <code>fullDay</code> property is set to * <code>false</code>. Otherwise the next hours are displayed as non-working. The passed hour is * considered as 24-hour based. */ endHour: {type: "int", group: "Data", defaultValue: 24}, /** * Determines if all of the hours in a day are displayed. If set to <code>false</code>, the hours shown are * between the <code>startHour</code> and <code>endHour</code>. */ fullDay: {type: "boolean", group: "Data", defaultValue: true}, /** * Determines which part of the control will remain fixed at the top of the page during vertical scrolling * as long as the control is in the viewport. * * <b>Note:</b> Limited browser support. Browsers which do not support this feature: * <ul> * <li>Microsoft Internet Explorer</li> * <li>Microsoft Edge lower than version 41 (EdgeHTML 16)</li> * <li>Mozilla Firefox lower than version 59</li> * </ul> * * @since 1.62 */ stickyMode: {type: "sap.m.PlanningCalendarStickyMode", group: "Behavior", defaultValue: PlanningCalendarStickyMode.None}, /** * Determines scale factor for the appointments. * * Acceptable range is from 1 to 6. * @since 1.99 */ scaleFactor: {type: "float", group: "Data", defaultValue: 1}, /** * Determines whether the appointments in the grid are draggable. * * The drag and drop interaction is visualized by a placeholder highlighting the area where the * appointment can be dropped by the user. * * @since 1.64 */ enableAppointmentsDragAndDrop: { type: "boolean", group: "Misc", defaultValue: false }, /** * Determines whether the appointments are resizable. * * The resize interaction is visualized by making the appointment transparent. * * The appointment snaps on every interval * of 30 minutes. After the resize is finished, the {@link #event:appointmentResize appointmentResize} event is fired, containing * the new start and end UI5Date or JavaScript Date objects. * * @since 1.65 */ enableAppointmentsResize: { type: "boolean", group: "Misc", defaultValue: false }, /** * Determines whether the appointments can be created by dragging on empty cells. * * See {@link #property:enableAppointmentsResize enableAppointmentsResize} for the specific points for events snapping * * @since 1.65 */ enableAppointmentsCreate: { type: "boolean", group: "Misc", defaultValue: false }, /** * If set, the first day of the displayed week is this day. Valid values are 0 to 6 starting on Sunday. * If there is no valid value set, the default of the used locale is used. * * Note: This property will only have effect in Week view and Month view of the SinglePlanningCalendar, * but it wouldn't have effect in WorkWeek view. * This property should not be used with the calendarWeekNumbering property. * * @since 1.98 */ firstDayOfWeek : {type : "int", group : "Appearance", defaultValue : -1}, /** * If set, the calendar week numbering is used for display. * If not set, the calendar week numbering of the global configuration is used. * Note: This property should not be used with firstDayOfWeek property. * @since 1.110.0 */ calendarWeekNumbering : { type : "sap.ui.core.date.CalendarWeekNumbering", group : "Appearance", defaultValue: null}, /** * Determines whether more than one day will be selectable. * <b>Note:</b> selecting more than one day is possible with a combination of <code>Ctrl + mouse click</code> */ dateSelectionMode: { type: "sap.m.SinglePlanningCalendarSelectionMode", group: "Behavior", defaultValue: SinglePlanningCalendarSelectionMode.SingleSelect } }, aggregations : { /** * The controls to be passed to the toolbar. */ actions : { type : "sap.ui.core.Control", multiple: true, singularName: "action", forwarding: { getter: "_getHeader", aggregation: "actions" } }, /** * The appointments to be displayed in the grid. Appointments outside the visible time frame are not rendered. * Appointments, longer than a day, will be displayed in all of the affected days. * To display an all-day appointment, the appointment must start at 00:00 and end on any day in the future in 00:00h. * * Note: The <code>customContent</code> functionality of the <code>CalendarAppointment</code> is not available * in the <code>SinglePlanningCalendar</code>. If set, it will not make any effect. */ appointments : { type: "sap.ui.unified.CalendarAppointment", multiple: true, singularName: "appointment", forwarding: { getter: "_getCurrentGrid", aggregation: "appointments" } }, /** * Views of the <code>SinglePlanningCalendar</code>. * * <b>Note:</b> If not set, the Week view is available. */ views : {type : "sap.m.SinglePlanningCalendarView", multiple : true, singularName : "view"}, /** * Special days in the header visualized as a date range with type. * * <b>Note:</b> If one day is assigned to more than one type, only the first type is used. * @since 1.66 */ specialDates : {type : "sap.ui.unified.DateTypeRange", multiple : true, singularName : "specialDate", forwarding: { getter: "_getCurrentGrid", aggregation: "specialDates" } }, /** * The header part of the <code>SinglePlanningCalendar</code>. * * @private */ _header : { type : "sap.m.PlanningCalendarHeader", multiple : false, visibility : "hidden" }, /** * The grid part of the <code>SinglePlanningCalendar</code>. * * @private */ _grid: { type: "sap.ui.core.Control", multiple: false, visibility: "hidden" }, /** * The grid part of the <code>SinglePlanningCalendar</code>. * * @private */ _mvgrid: { type: "sap.ui.core.Control", multiple: false, visibility: "hidden" }, /** * Dates or date ranges for selected dates. * * To set a single date (instead of a range), set only the <code>startDate</code> property * of the {@link sap.ui.unified.DateRange} class. */ selectedDates : { type : "sap.ui.unified.DateRange", multiple : true, singularName : "selectedDate", forwarding: { getter: "_getCurrentGrid", aggregation: "selectedDates" } } }, associations: { /** * Corresponds to the currently selected view. */ selectedView: { type: "sap.m.SinglePlanningCalendarView", multiple: false }, /** * Association to the <code>PlanningCalendarLegend</code> explaining the colors of the <code>Appointments</code>. * * <b>Note:</b> The legend does not have to be rendered but must exist and all required types must be assigned. * @since 1.65.0 */ legend: { type: "sap.m.PlanningCalendarLegend", multiple: false} }, events: { /** * Fired when the selected state of an appointment is changed. */ appointmentSelect: { parameters: { /** * The appointment on which the event was triggered. */ appointment: {type: "sap.ui.unified.CalendarAppointment"}, /** * All appointments with changed selected state. * @since 1.67.0 */ appointments : {type : "sap.ui.unified.CalendarAppointment[]"} } }, /** * Fired if an appointment is dropped. * @since 1.64 */ appointmentDrop : { parameters : { /** * The dropped appointment. */ appointment : {type : "sap.ui.unified.CalendarAppointment"}, /** * Start date of the dropped appointment, as a UI5Date or JavaScript Date object. */ startDate : {type : "object"}, /** * Dropped appointment end date as a UI5Date or JavaScript Date object. */ endDate : {type : "object"}, /** * The drop type. If true - it's "Copy", if false - it's "Move". */ copy : {type : "boolean"} } }, /** * Fired when an appointment is resized. * @since 1.65 */ appointmentResize: { parameters: { /** * The resized appointment. */ appointment: { type: "sap.ui.unified.CalendarAppointment" }, /** * Start date of the resized appointment, as a UI5Date or JavaScript Date object. */ startDate: { type: "object" }, /** * End date of the resized appointment, as a UI5Date or JavaScript Date object. */ endDate: { type: "object" } } }, /** * Fired if an appointment is created. * @since 1.65 */ appointmentCreate: { parameters: { /** * Start date of the created appointment, as a UI5Date or JavaScript Date object. */ startDate: {type: "object"}, /** * End date of the created appointment, as a UI5Date or JavaScript Date object. */ endDate: {type: "object"} } }, /** * Fired if a date is selected in the calendar header. */ headerDateSelect: { parameters: { /** * Date of the selected header, as a UI5Date or JavaScript Date object. It is considered as a local date. */ date: {type: "object"} } }, /** * <code>startDate</code> is changed while navigating in the <code>SinglePlanningCalendar</code>. */ startDateChange: { parameters: { /** * The new start date, as a UI5Date or JavaScript Date object. It is considered as a local date. */ date: {type: "object"} } }, /** * Fired when a grid cell is pressed. * @since 1.65 */ cellPress: { parameters: { /** * The start date as a UI5Date or JavaScript Date object of the focused grid cell. */ startDate: {type: "object"}, /** * The end date as a UI5Date or JavaScript Date object of the focused grid cell. */ endDate: {type: "object"} } }, /** * Fired when a 'more' button is pressed. * <b>Note:</b> The 'more' button appears in a month view cell * when multiple appointments exist and the available space * is not sufficient to display all of them. */ moreLinkPress: { parameters: { /** * The date as a UI5Date or JavaScript Date object of the cell with the * pressed more link. */ date: { type: "object" } } }, /** * The view was changed by user interaction. * @since 1.71.0 */ viewChange : {} } }, renderer: SinglePlanningCalendarRenderer }); SinglePlanningCalendar.prototype.init = function() { var sOPCId = this.getId(); this._oRB = sap.ui.getCore().getLibraryResourceBundle("sap.m"); this._oDefaultView = new SinglePlanningCalendarWeekView({ key: "DEFAULT_INNER_WEEK_VIEW_CREATED_FROM_CONTROL", title: "" }); this.setAssociation("selectedView", this._oDefaultView); this.setAggregation("_header", this._createHeader()); this.setAggregation("_grid", new SinglePlanningCalendarGrid(sOPCId + "-Grid")); this.setAggregation("_mvgrid", new SinglePlanningCalendarMonthGrid(sOPCId + "-GridMV")); this._attachHeaderEvents(); this._attachGridEvents(); this._attachDelegates(); this.setStartDate(UI5Date.getInstance()); }; /** * Called before rendering starts. * * @private */ SinglePlanningCalendar.prototype.onBeforeRendering = function () { // We can apply/remove sticky classes even before the control is rendered. this._toggleStickyClasses(); if (this.getFirstDayOfWeek() !== -1 && this.getCalendarWeekNumbering()) { Log.warning("Both properties firstDayOfWeek and calendarWeekNumbering should not be used at the same time!"); } }; /** * Called when rendering is completed. * * @private */ SinglePlanningCalendar.prototype.onAfterRendering = function () { var oHeader = this._getHeader(); // Adjusting is done after rendering, because otherwise we won't have // info about how much offset is actually needed. this._adjustColumnHeadersTopOffset(); // Indicate if the actions toolbar is hidden this.toggleStyleClass("sapMSinglePCActionsHidden", !oHeader._getActionsToolbar().getVisible()); this._registerResizeHandler(HEADER_RESIZE_HANDLER_ID, oHeader, this._onHeaderResize.bind(this)); }; SinglePlanningCalendar.prototype.exit = function () { if (this._oDefaultView) { this._oDefaultView.destroy(); this._oDefaultView = null; } if (this._afterRenderFocusCell) { this.removeDelegate(this._afterRenderFocusCell); this._afterRenderFocusCell = null; } this._deRegisterResizeHandler(HEADER_RESIZE_HANDLER_ID); }; /** * Called when the navigation toolbar changes its width or height. * * @param oEvent The resize event * @returns {this} Reference to <code>this</code> for method chaining * @private */ SinglePlanningCalendar.prototype._onHeaderResize = function (oEvent) { if (oEvent.oldSize.height === oEvent.size.height) { // We need only height changes return this; } // If resizing happened due to the actions toolbar changing its visibility, // then update the corresponding class this.toggleStyleClass("sapMSinglePCActionsHidden", !this._getHeader()._getActionsToolbar().getVisible()); // There are 3 reasons why the header's height might have changed and we need to adjust // columnHeaders' offset for each of them. // - Actions toolbar showed up: columnHeaders need to go lower // - Actions toolbar got hidden: columnHeaders need to go higher // - Screen width became too small and some of the navigation toolbar's content went // on a second line: second line: columnHeaders need to go lower this._adjustColumnHeadersTopOffset(); return this; }; SinglePlanningCalendar.prototype.setTitle = function (sTitle) { this._getHeader().setTitle(sTitle); return this.setProperty("title", sTitle); }; SinglePlanningCalendar.prototype.setScaleFactor = function(iScaleFactor) { if (iScaleFactor < 1 || iScaleFactor > 6) { return; } this.setProperty("scaleFactor", parseInt(iScaleFactor)); this.getAggregation("_grid").setProperty("scaleFactor", parseInt(iScaleFactor)); return this; }; /** * Sets the start date of the grid. * @param {Date|module:sap/ui/core/date/UI5Date} oDate A date instance * @returns {this} Reference to <code>this</code> for method chaining * @public */ SinglePlanningCalendar.prototype.setStartDate = function (oDate) { this.setProperty("startDate", oDate); this._alignColumns(); return this; }; SinglePlanningCalendar.prototype.setStartHour = function (iHour) { this.getAggregation("_grid").setStartHour(iHour); this.setProperty("startHour", iHour); return this; }; SinglePlanningCalendar.prototype.setEndHour = function (iHour) { this.getAggregation("_grid").setEndHour(iHour); this.setProperty("endHour", iHour); return this; }; SinglePlanningCalendar.prototype.setFullDay = function (bFullDay) { this.getAggregation("_grid").setFullDay(bFullDay); this.setProperty("fullDay", bFullDay); return this; }; SinglePlanningCalendar.prototype.setEnableAppointmentsDragAndDrop = function (bEnabled) { this.getAggregation("_grid").setEnableAppointmentsDragAndDrop(bEnabled); this.getAggregation("_mvgrid").setEnableAppointmentsDragAndDrop(bEnabled); return this.setProperty("enableAppointmentsDragAndDrop", bEnabled, true); }; SinglePlanningCalendar.prototype.setEnableAppointmentsResize = function(bEnabled) { this.getAggregation("_grid").setEnableAppointmentsResize(bEnabled); return this.setProperty("enableAppointmentsResize", bEnabled, true); }; SinglePlanningCalendar.prototype.setEnableAppointmentsCreate = function(bEnabled) { this.getAggregation("_grid").setEnableAppointmentsCreate(bEnabled); return this.setProperty("enableAppointmentsCreate", bEnabled, true); }; SinglePlanningCalendar.prototype.setDateSelectionMode = function (sDateSelectionMode) { this.getAggregation("_mvgrid").setDateSelectionMode(sDateSelectionMode); this.getAggregation("_grid").setDateSelectionMode(sDateSelectionMode); return this.setProperty("dateSelectionMode", sDateSelectionMode); }; /** * Applies or removes sticky classes based on <code>stickyMode</code>'s value. * * @returns {this} Reference to <code>this</code> for method chaining * @private */ SinglePlanningCalendar.prototype._toggleStickyClasses = function () { var sStickyMode = this.getStickyMode(); this.toggleStyleClass("sapMSinglePCStickyAll", sStickyMode === PlanningCalendarStickyMode.All); this.toggleStyleClass("sapMSinglePCStickyNavBarAndColHeaders", sStickyMode === PlanningCalendarStickyMode.NavBarAndColHeaders); return this; }; /** * Removes the selected dates of the grid. * @returns {object} An array of the removed DateRange objects * @public */ SinglePlanningCalendar.prototype.removeAllSelectedDates = function () { return this._getCurrentGrid().removeAllSelectedDates(); }; /** * Gets the selected dates of the grid. * @returns {object} An array of DateRange objects * @public */ SinglePlanningCalendar.prototype.getSelectedDates = function () { return this._getCurrentGrid().getAggregation("selectedDates"); }; /** * Adds a selected date to the grid. * @param {object} oSelectedDate A DateRange object * @returns {this} Reference to <code>this</code> for method chaining * @public */ SinglePlanningCalendar.prototype.addSelectedDate = function (oSelectedDate) { this._getCurrentGrid().addAggregation("selectedDates", oSelectedDate); return this; }; /** * Makes sure that the column headers are offset in such a way, that they are positioned right * after the navigation toolbar. * * @returns {this} Reference to <code>this</code> for method chaining * @private */ SinglePlanningCalendar.prototype._adjustColumnHeadersTopOffset = function () { var sStickyMode = this.getStickyMode(), oGrid = this.getAggregation("_grid"), oColumnHeaders = oGrid && oGrid._getColumnHeaders(), iTop; // Make sure that the columnHeaders are rendered if (!oColumnHeaders || !oColumnHeaders.getDomRef()) { return this; } switch (sStickyMode) { case PlanningCalendarStickyMode.All: // Since the whole header will be visible, columnHeaders should be offset by its whole height. iTop = this._getHeader().$().outerHeight(); break; case PlanningCalendarStickyMode.NavBarAndColHeaders: // Since the action toolbar will be hidden, columnHeaders should be // with top position the height of the navigation toolbar iTop = this._getHeader()._getNavigationToolbar().$().outerHeight(); break; default: // Reset to default, if not in sticky mode iTop = "auto"; break; } oColumnHeaders.$().css("top", iTop); oColumnHeaders._setTopPosition(iTop); return this; }; SinglePlanningCalendar.prototype.addView = function (oView) { var oViewsButton, oHeader = this._getHeader(), sSegmentedButtonItemId = oView.getId() + SEGMENTEDBUTTONITEM__SUFFIX, oSegmentedButtonItem; if (!oView) { return this; } if (this._isViewKeyExisting(oView.getKey())) { Log.error("There is an existing view with the same key.", this); return this; } this.addAggregation("views", oView); oViewsButton = oHeader._getOrCreateViewSwitch(); oSegmentedButtonItem = new SegmentedButtonItem(sSegmentedButtonItemId, { key: oView.getKey(), text: oView.getTitle() }); oViewsButton.addItem(oSegmentedButtonItem); this._observeViewTitle(oView); if (this._getSelectedView().getKey() === this._oDefaultView.getKey()) { this.setAssociation("selectedView", oView); } this._alignView(); if (this.getViews().length > MAX_NUMBER_OF_VIEWS_IN_SEGMENTED_BUTTON) { oHeader._convertViewSwitchToSelect(); } return this; }; SinglePlanningCalendar.prototype.insertView = function (oView, iPos) { var oViewsButton, oHeader = this._getHeader(), sSegmentedButtonItemId = oView.getId() + SEGMENTEDBUTTONITEM__SUFFIX, oSegmentedButtonItem; if (!oView) { return this; } if (this._isViewKeyExisting(oView.getKey())) { Log.error("There is an existing view with the same key.", this); return this; } this.insertAggregation("views", oView, iPos); oViewsButton = oHeader._getOrCreateViewSwitch(); oSegmentedButtonItem = new SegmentedButtonItem(sSegmentedButtonItemId, { key: oView.getKey(), text: oView.getTitle() }); oViewsButton.insertItem(oSegmentedButtonItem, iPos); this._observeViewTitle(oView); if (this._getSelectedView().getKey() === this._oDefaultView.getKey()) { this.setAssociation("selectedView", oView); } this._alignView(); if (this.getViews().length > MAX_NUMBER_OF_VIEWS_IN_SEGMENTED_BUTTON) { oHeader._convertViewSwitchToSelect(); } return this; }; SinglePlanningCalendar.prototype.removeView = function (oView) { if (!oView) { return this; } var oHeader = this._getHeader(), oViewsButton = oHeader._getOrCreateViewSwitch(), oViewsButtonItems = oViewsButton.getItems(), oCurrentlySelectedView = this._getSelectedView(), oViewToRemoveKey = oView.getKey(), oCurrentItem, i; if (this.getViews().length === 1) { this._disconnectAndDestroyViewsObserver(); } else { this._oViewsObserver.unobserve(oView, { properties: ["title"] }); } for (i = 0; i < oViewsButtonItems.length; i++) { oCurrentItem = oViewsButtonItems[i]; if (oCurrentItem.getKey() === oViewToRemoveKey) { oViewsButton.removeItem(oCurrentItem); break; } } this.removeAggregation("views", oView); // if the removed view is the selected one, either set the first view as selected // or if all views are removed point to the _oDefaultView if (oViewToRemoveKey === oCurrentlySelectedView.getKey()) { this.setAssociation("selectedView", this.getViews()[0] || this._oDefaultView); } this._alignView(); if (this.getViews().length <= MAX_NUMBER_OF_VIEWS_IN_SEGMENTED_BUTTON) { oHeader._convertViewSwitchToSegmentedButton(); } return this; }; SinglePlanningCalendar.prototype.removeAllViews = function () { var oViewsButton = this._getHeader()._getOrCreateViewSwitch(); this._disconnectAndDestroyViewsObserver(); oViewsButton.removeAllItems(); this.setAssociation("selectedView", this._oDefaultView); this._alignView(); return this.removeAllAggregation("views"); }; SinglePlanningCalendar.prototype.destroyViews = function () { var oViewsButton = this._getHeader()._getOrCreateViewSwitch(); this._disconnectAndDestroyViewsObserver(); oViewsButton.destroyItems(); this.setAssociation("selectedView", this._oDefaultView); this._alignView(); return this.destroyAggregation("views"); }; /** * Sets the text property of the SegmentedButton view item * * @param {object} oChanges the detected from the ManagedObjectObserver changes * @private */ SinglePlanningCalendar.prototype._viewsObserverCallbackFunction = function (oChanges) { sap.ui.getCore().byId(oChanges.object.getId() + SEGMENTEDBUTTONITEM__SUFFIX).setText(oChanges.current); }; /** * Returns the ManagedObjectObserver for the views * * @returns {sap.ui.base.ManagedObjectObserver} the views observer object * @private */ SinglePlanningCalendar.prototype._getViewsObserver = function () { if (!this._oViewsObserver) { this._oViewsObserver = new ManagedObjectObserver(this._viewsObserverCallbackFunction); } return this._oViewsObserver; }; /** * Observes the title property of the passed view * * @param {sap.m.SinglePlanningCalendarView} oView the view, which property will be observed * @private */ SinglePlanningCalendar.prototype._observeViewTitle = function (oView) { this._getViewsObserver().observe(oView, { properties: ["title"] }); }; /** * Disconnects and destroys the ManagedObjectObserver observing the used views * * @private */ SinglePlanningCalendar.prototype._disconnectAndDestroyViewsObserver = function () { if (this._oViewsObserver) { this._oViewsObserver.disconnect(); this._oViewsObserver.destroy(); this._oViewsObserver = null; } }; SinglePlanningCalendar.prototype.setSelectedView = function(vView) { // first check if vView is string (ID), or object with getKey method if (typeof vView === "string") { // it is string, try to find corresponding view vView = this._getViewById(vView); } else if (vView.isA("sap.m.SinglePlanningCalendarView") && !this._isViewKeyExisting(vView.getKey())) { // non-existing view vView = null; } if (!vView) { // view is missing Log.error("There is no such view.", this); return this; } // view is found this._setupNewView(vView); this._getHeader()._getOrCreateViewSwitch().setSelectedKey(vView.getKey()); return this; }; /** * Holds the selected appointments. If no appointments are selected, an empty array is returned. * * @returns {sap.ui.unified.CalendarAppointment[]} All selected appointments * @since 1.62 * @public */ SinglePlanningCalendar.prototype.getSelectedAppointments = function() { return this.getAggregation("_grid").getSelectedAppointments(); }; SinglePlanningCalendar.prototype.setLegend = function (vLegend) { var oLegendDestroyObserver, sLegend, oLegend; this.setAssociation("legend", vLegend); this.getAggregation("_grid").setAssociation("legend", vLegend); this.getAggregation("_mvgrid").setAssociation("legend", vLegend); sLegend = this.getLegend(); if (sLegend) { this.getAggregation("_grid")._sLegendId = sLegend; this.getAggregation("_mvgrid")._sLegendId = sLegend; oLegend = sap.ui.getCore().byId(sLegend); } if (oLegend) { //destroy of the associated legend should rerender the SPC oLegendDestroyObserver = new ManagedObjectObserver(function(oChanges) { this.invalidate(); }.bind(this)); oLegendDestroyObserver.observe(oLegend, { destroy: true }); } return this; }; SinglePlanningCalendar.prototype.setFirstDayOfWeek = function (iFirstDayOfWeek) { if (iFirstDayOfWeek < -1 || iFirstDayOfWeek > 6) { Log.error("" + iFirstDayOfWeek + " is not a valid value to the property firstDayOfWeek. Valid values are from -1 to 6."); return; } this.setProperty("firstDayOfWeek", iFirstDayOfWeek); this.getViews().forEach(function (oView) { oView.setFirstDayOfWeek(iFirstDayOfWeek); }); var oHeader = this._getHeader(), oPicker = oHeader.getAggregation("_calendarPicker") ? oHeader.getAggregation("_calendarPicker") : oHeader._oPopup.getContent()[0], oSelectedView = this._getSelectedView(), oStartDate = this.getStartDate() || UI5Date.getInstance(), oSPCStart = oSelectedView.calculateStartDate(UI5Date.getInstance(oStartDate.getTime())), oMonthGrid = this.getAggregation("_mvgrid"); this.setStartDate(oSPCStart); oMonthGrid.setFirstDayOfWeek(iFirstDayOfWeek); oPicker.setFirstDayOfWeek(iFirstDayOfWeek); return this; }; SinglePlanningCalendar.prototype.setCalendarWeekNumbering = function(sCalendarWeekNumbering) { this.setProperty("calendarWeekNumbering", sCalendarWeekNumbering); this.getViews().forEach(function (oView) { oView.setCalendarWeekNumbering(sCalendarWeekNumbering); }); var oHeader = this._getHeader(), oPicker = oHeader.getAggregation("_calendarPicker") ? oHeader.getAggregation("_calendarPicker") : oHeader._oPopup.getContent()[0], oMonthGrid = this.getAggregation("_mvgrid"); oMonthGrid.setCalendarWeekNumbering(this.getCalendarWeekNumbering()); oPicker.setCalendarWeekNumbering(this.getCalendarWeekNumbering()); this._alignColumns(); return this; }; /** * Switches the visibility of the SegmentedButton in the _header and aligns the columns in the grid after an * operation (add, insert, remove, removeAll, destroy) with the views is performed. * * @returns {this} Reference to <code>this</code> for method chaining * @private */ SinglePlanningCalendar.prototype._alignView = function () { this._switchViewButtonVisibility(); this._alignColumns(); return this; }; /** * Creates the header and adds proper <code>ariaLabelledBy</code> references on it's toolbars. * @returns {object} The created header * @private */ SinglePlanningCalendar.prototype._createHeader = function () { var oHeader = new PlanningCalendarHeader(this.getId() + "-Header"); oHeader.getAggregation("_actionsToolbar") .addAriaLabelledBy(InvisibleText.getStaticId("sap.m", "SPC_ACTIONS_TOOLBAR")); oHeader.getAggregation("_navigationToolbar") .addAriaLabelledBy(InvisibleText.getStaticId("sap.m", "SPC_NAVIGATION_TOOLBAR")); return oHeader; }; /** * Checks whether a view with given key already exists in the views aggregation. * * @param {string} sKey the key to be checked * @returns {boolean} true if view with given key exists * @private */ SinglePlanningCalendar.prototype._isViewKeyExisting = function (sKey) { return this.getViews().some(function (oView) { return oView.getKey() === sKey; }); }; /** * Finds the view object by given key. * @param {string} sKey The key of the view * @public * @since 1.75 * @returns {sap.m.SinglePlanningCalendarView|null} the view object which matched the given <code>sKey</code>, or <code>null</code> if there is no such view */ SinglePlanningCalendar.prototype.getViewByKey = function (sKey) { var aViews = this.getViews(), i; for (i = 0; i < aViews.length; i++) { if (aViews[i].getKey() === sKey) { return aViews[i]; } } return null; }; /** * Finds the view object by given ID * @param {string} sId The ID of the view * @private * @returns {sap.m.SinglePlanningCalendarView} the view object which matched the given <code>sId</code>, or null if there is no such view */ SinglePlanningCalendar.prototype._getViewById = function (sId) { var aViews = this.getViews(), i; for (i = 0; i < aViews.length; i++) { if (aViews[i].getId() === sId) { return aViews[i]; } } return null; }; /** * Getter for the associated as selectedView view. * @returns {object} The currently selected view object * @private */ SinglePlanningCalendar.prototype._getSelectedView = function () { var oSelectedView, aViews = this.getViews(), sCurrentViewKey = sap.ui.getCore().byId(this.getAssociation("selectedView")).getKey(); for (var i = 0; i < aViews.length; i++) { if (sCurrentViewKey === aViews[i].getKey()) { oSelectedView = aViews[i]; break; } } return oSelectedView || this._oDefaultView; }; /** * Switches the visibility of the button, controlling the views. * If the SinglePlanningCalendar has only one view added to its view aggregation, the button is not visible. * Otherwise, it is displayed in the _header. * * @returns {this} Reference to <code>this</code> for method chaining * @private */ SinglePlanningCalendar.prototype._switchViewButtonVisibility = function () { var oSegmentedButton = this._getHeader()._getOrCreateViewSwitch(), bVisible = oSegmentedButton.getItems().length > 1; oSegmentedButton.setProperty("visible", bVisible); return this; }; /** * Attaches handlers to the events in the _header aggregation. * * @returns {this} Reference to <code>this</code> for method chaining * @private */ SinglePlanningCalendar.prototype._attachHeaderEvents = function () { var oHeader = this._getHeader(); oHeader.attachEvent("viewChange", this._handleViewChange, this); oHeader.attachEvent("pressPrevious", this._handlePressArrow, this); oHeader.attachEvent("pressToday", this._handlePressToday, this); oHeader.attachEvent("pressNext", this._handlePressArrow, this); oHeader.attachEvent("dateSelect", this._handleCalendarPickerDateSelect, this); return this; }; /** * Attaches delegates to the events in the _grid aggregation. * * @private */ SinglePlanningCalendar.prototype._attachDelegates = function() { // After the grid renders apply the focus on the cell this._afterRenderFocusCell = { onAfterRendering: function() { if (this._sGridCellFocusSelector) { jQuery(this._sGridCellFocusSelector).trigger("focus"); this._sGridCellFocusSelector = null; } }.bind(this) }; this.getAggregation("_grid").addDelegate(this._afterRenderFocusCell); this.getAggregation("_mvgrid").addDelegate(this._afterRenderFocusCell); }; /** * Attaches handlers to the events in the _grid aggregation. * * @returns {this} Reference to <code>this</code> for method chaining * @private */ SinglePlanningCalendar.prototype._attachGridEvents = function () { var oGrid = this.getAggregation("_grid"), oGridMV = this.getAggregation("_mvgrid"); var fnHandleHeadersSelect = function(oEvent) { this.fireHeaderDateSelect({ date: oEvent.getSource()._oDate.toLocalJSDate() }); }; var fnHandleAppointmentSelect = function(oEvent) { this.fireAppointmentSelect({ appointment: oEvent.getParameter("appointment"), appointments: oEvent.getParameter("appointments") }); }; var fnHandleAppointmentDrop = function(oEvent) { this.fireAppointmentDrop({ appointment: oEvent.getParameter("appointment"), startDate: oEvent.getParameter("startDate"), endDate: oEvent.getParameter("endDate"), copy: oEvent.getParameter("copy") }); }; var fnHandleAppointmentResize = function(oEvent) { this.fireAppointmentResize({ appointment: oEvent.getParameter("appointment"), startDate: oEvent.getParameter("startDate"), endDate: oEvent.getParameter("endDate") }); }; var fnHandleAppointmentCreate = function(oEvent) { this.fireAppointmentCreate({ startDate: oEvent.getParameter("startDate"), endDate: oEvent.getParameter("endDate") }); }; var fnHandleCellPress = function(oEvent) { this.fireEvent("cellPress", { startDate: oEvent.getParameter("startDate"), endDate: oEvent.getParameter("endDate") }); }; var fnHandleMoreLinkPress = function(oEvent) { this.fireEvent("moreLinkPress", { date: oEvent.getParameter("date") }); }; var fnHandleBorderReached = function(oEvent) { var oGrid = this.getAggregation("_grid"), oFormat = oGrid._getDateFormatter(), iNavDelta = this._getSelectedView().getScrollEntityCount() - oGrid._getColumns() + 1, oCellStartDate = UI5Date.getInstance(oEvent.getParameter("startDate")), bFullDay = oEvent.getParameter("fullDay"), oNavDate = this.getStartDate(); if (oEvent.getParameter("next")) { oCellStartDate.setDate(oCellStartDate.getDate() + iNavDelta); oNavDate = UI5Date.getInstance(oNavDate.setDate(oNavDate.getDate() + this._getSelectedView().getScrollEntityCount())); this.setStartDate(oNavDate); } else { oCellStartDate.setDate(oCellStartDate.getDate() - iNavDelta); oNavDate = UI5Date.getInstance(oNavDate.setDate(oNavDate.getDate() - this._getSelectedView().getScrollEntityCount())); this.setStartDate(oNavDate); } this._sGridCellFocusSelector = bFullDay ? "[data-sap-start-date='" + oFormat.format(oCellStartDate) + "'].sapMSinglePCBlockersColumn" : "[data-sap-start-date='" + oFormat.format(oCellStartDate) + "'].sapMSinglePCRow"; }; var fnHandleBorderReachedMonthView = function(oEvent) { var oDate = UI5Date.getInstance(oEvent.getParameter("startDate")), oCalNextDate = CalendarDate.fromLocalJSDate(oDate), oNextDate; oCalNextDate.setDate(oCalNextDate.getDate() + oEvent.getParameter("offset")); oNextDate = oCalNextDate.toLocalJSDate(); this.setStartDate(oNextDate); this._sGridCellFocusSelector = "[sap-ui-date='" + oCalNextDate.valueOf() + "'].sapMSPCMonthDay"; }; oGrid._getColumnHeaders().attachEvent("select", fnHandleHeadersSelect, this); oGrid.attachEvent("appointmentSelect", fnHandleAppointmentSelect, this); oGridMV.attachEvent("appointmentSelect", fnHandleAppointmentSelect, this); oGrid.attachEvent("appointmentDrop", fnHandleAppointmentDrop, this); oGridMV.attachEvent("appointmentDrop", fnHandleAppointmentDrop, this); oGrid.attachEvent("appointmentResize", fnHandleAppointmentResize, this); oGrid.attachEvent("appointmentCreate", fnHandleAppointmentCreate, this); oGrid.attachEvent("cellPress", fnHandleCellPress, this); oGridMV.attachEvent("cellPress", fnHandleCellPress, this); oGridMV.attachEvent("moreLinkPress", fnHandleMoreLinkPress, this); oGrid.attachEvent("borderReached", fnHandleBorderReached, this); oGridMV.attachEvent("borderReached", fnHandleBorderReachedMonthView, this); return this; }; /** * Handler for the viewChange event in the _header aggregation. * @private */ SinglePlanningCalendar.prototype._handleViewChange = function (oEvent) { var sNewViewKey = oEvent.getParameter("item").getProperty("key"), oNewView = this.getViewByKey(sNewViewKey); this._setupNewView(oNewView); this.fireViewChange(); }; /** * Handler for the pressPrevious and pressNext events in the _header aggregation. * @param {Date} oEvent The triggered event * @private */ SinglePlanningCalendar.prototype._handlePressArrow = function (oEvent) { this._applyArrowsLogic(oEvent.getId() === "pressPrevious"); this._adjustColumnHeadersTopOffset(); }; /** * Handler for the pressToday event in the _header aggregation. * @private */ SinglePlanningCalendar.prototype._handlePressToday = function () { var oStartDate = this._getSelectedView().calculateStartDate(UI5Date.getInstance()); this.setStartDate(oStartDate); this.fireStartDateChange({ date: oStartDate }); this._adjustColumnHeadersTopOffset(); }; /** * Sets given view in the selectedView association and then prepares the calendar * for the new view. * @param {Object | string} vView The new view * @private */ SinglePlanningCalendar.prototype._setupNewView = function(vView) { var oPreviousGrid = this._getCurrentGrid(); this.setAssociation("selectedView", vView); this._transferAggregations(oPreviousGrid); this._alignColumns(); this._adjustColumnHeadersTopOffset(); }; SinglePlanningCalendar.prototype._transferAggregations = function(oPreviousGrid) { var oNextGrid = this._getCurrentGrid(), aApps, aSpecialDates, aSelectedDates, i; if (oPreviousGrid.getId() !== oNextGrid.getId()) { aApps = oPreviousGrid.removeAllAggregation("appointments", true); for (i = 0; i < aApps.length; i++) { oNextGrid.addAggregation("appointments", aApps[i], true); } aSpecialDates = oPreviousGrid.removeAllAggregation("specialDates", true); for (i = 0; i < aSpecialDates.length; i++) { oNextGrid.addAggregation("specialDates", aSpecialDates[i], true); } aSelectedDates = oPreviousGrid.removeAllAggregation("selectedDates", true); for (i = 0; i < aSelectedDates.length; i++) { oNextGrid.addAggregation("selectedDates", aSelectedDates[i], true); } } }; /** * Handler for the dateSelect event in the _header aggregation. * @private */ SinglePlanningCalendar.prototype._handleCalendarPickerDateSelect = function () { var oStartDate = this._getHeader().getStartDate(), oSPCStartDate; oSPCStartDate = this._getSelectedView().calculateStartDate(UI5Date.getInstance(oStartDate.getTime())); this.setStartDate(oSPCStartDate); if (!this._getSelectedView().isA("sap.m.SinglePlanningCalendarMonthView")) { this.getAggregation("_grid")._getColumnHeaders().setDate(oStartDate); } this.fireStartDateChange({ date: oSPCStartDate }); this._adjustColumnHeadersTopOffset(); }; /** * Updates the selection in the header's calendarPicker aggregation. * @private */ SinglePlanningCalendar.prototype._updateCalendarPickerSelection = function() { var oRangeDates = this._getFirstAndLastRangeDate(), oHeader = this._getHeader(), oCalPicker = oHeader.getAggregation("_calendarPicker") ? oHeader.getAggregation("_calendarPicker") : oHeader._oPopup.getContent()[0], oSelectedRange; oSelectedRange = new DateRange({ startDate: oRangeDates.oStartDate.toLocalJSDate(), endDate: oRangeDates.oEndDate.toLocalJSDate() }); oCalPicker.destroySelectedDates(); oCalPicker.addSelectedDate(oSelectedRange); }; /** * Creates and formats a string to be displayed in the picker button from the _header aggregation. * If no oLastDate is passed, this means that the SinglePlanningCalendar is showing Day view, so the string contains * info about the current date. Otherwise, the result string shows info about a date range. * @returns {string} The concatenated string to displayed * @private */ SinglePlanningCalendar.prototype._formatPickerText = function () { var oRangeDates = this._getFirstAndLastRangeDate(), oStartDate = oRangeDates.oStartDate.toLocalJSDate(), oEndDate = oRangeDates.oEndDate.toLocalJSDate(), oFormat, oResult; if (this._getSelectedView().isA("sap.m.SinglePlanningCalendarMonthView")) { oFormat = DateFormat.getDateInstance({format: "yMMMM"}); oResult = oFormat.format(oStartDate); } else { oFormat = DateFormat.getDateInstance({format: "yMMMMd"}); oResult = oFormat.format(oStartDate); if (oStartDate.getTime() !== oEndDate.getTime()) { oResult += " - " + oFormat.format(oEndDate); } } return oResult; }; /** * Logic for moving the selected time range in the control via the navigation arrows. * @param {boolean} bBackwards Whether the left arrow is pressed * @private */ SinglePlanningCalendar.prototype._applyArrowsLogic = function (bBackwards) { var oCalStartDate = CalendarDate.fromLocalJSDate(this.getStartDate() || UI5Date.getInstance()), iOffset = bBackwards ? -1 : 1, iNumberToAdd = this._getSelectedView().getScrollEntityCount(this.getStartDate(), iOffset), oStartDate; if (bBackwards) { iNumberToAdd *= -1; } oCalStartDate.setDate(oCalStartDate.getDate() + iNumberToAdd); oStartDate = oCalStartDate.toLocalJSDate(); this.setStartDate(oStartDate); this.fireStartDateChange({ date: oStartDate }); }; /** * Calculates the first and the last date of the range to be displayed. The size of the range depends on the * currently selected view. * @returns {object} Two properties containing the first and the last date from the range * @private */ SinglePlanningCalendar.prototype._getFirstAndLastRangeDate = function () { var oSelectedView = this._getSelectedView(), oStartDate = this._getHeader().getStartDate() || UI5Date.getInstance(), iDaysToAdd = oSelectedView.getEntityCount() - 1, oCalViewStartDate, oCalViewEndDate; oCalViewStartDate = CalendarDate.fromLocalJSDate(oSelectedView.calculateStartDate(UI5Date.getInstance(oStartDate.getTime()))); oCalViewEndDate = new CalendarDate(oCalViewStartDate); oCalViewEndDate.setDate(oCalViewStartDate.getDate() + iDaysToAdd); return { oStartDate: oCalViewStartDate, oEndDate: oCalViewEndDate }; }; /** * Responsible for aligning the columns due to startDate or view change. * @private */ SinglePlanningCalendar.prototype._alignColumns = function () { var oHeader = this._getHeader(), oGrid = this.getAggregation("_grid"), oGridMV = this.getAggregation("_mvgrid"), oView = this._getSelectedView(), oDate = this.getStartDate() || UI5Date.getInstance(), oViewStartDate = oView.calculateStartDate(UI5Date.getInstance(oDate.getTime())), oCalViewDate = CalendarDate.fromLocalJSDate(oViewStartDate); oHeader.setStartDate(oViewStartDate); oHeader.setPickerText(this._formatPickerText(oCalViewDate)); this._updateCalendarPickerSelection(); oGrid.setStartDate(oViewStartDate); oGridMV.setStartDate(oViewStartDate); oGrid._setColumns(oView.getEntityCount()); this._setColumnHeaderVisibility(); }; /** * Switches the visibility of the colu