UNPKG

@qooxdoo/framework

Version:

The JS Framework for Coders

772 lines (625 loc) 21.5 kB
/* ************************************************************************ qooxdoo - the new era of web development http://qooxdoo.org Copyright: 2006 STZ-IDA, Germany, http://www.stz-ida.de License: MIT: https://opensource.org/licenses/MIT See the LICENSE file in the project's top-level directory for details. Authors: * Til Schneider (til132) * Martin Wittemann (martinwittemann) ************************************************************************ */ /** * A *date chooser* is a small calendar including a navigation bar to switch the shown * month. It includes a column for the calendar week and shows one month. Selecting * a date is as easy as tapping on it. * * To be conform with all form widgets, the {@link qx.ui.form.IForm} interface * is implemented. * * The following example creates and adds a date chooser to the root element. * A listener alerts the user if a new date is selected. * * <pre class='javascript'> * var chooser = new qx.ui.control.DateChooser(); * this.getRoot().add(chooser, { left : 20, top: 20}); * * chooser.addListener("changeValue", function(e) { * alert(e.getData()); * }); * </pre> * * Additionally to a selection event an execute event is available which is * fired by doubletap or tapping the space / enter key. With this event you * can for example save the selection and close the date chooser. * * @childControl navigation-bar {qx.ui.container.Composite} container for the navigation bar controls * @childControl last-year-button-tooltip {qx.ui.tooltip.ToolTip} tooltip for the last year button * @childControl last-year-button {qx.ui.form.Button} button to jump to the last year * @childControl last-month-button-tooltip {qx.ui.tooltip.ToolTip} tooltip for the last month button * @childControl last-month-button {qx.ui.form.Button} button to jump to the last month * @childControl next-month-button-tooltip {qx.ui.tooltip.ToolTip} tooltip for the next month button * @childControl next-month-button {qx.ui.form.Button} button to jump to the next month * @childControl next-year-button-tooltip {qx.ui.tooltip.ToolTip} tooltip for the next year button * @childControl next-year-button {qx.ui.form.Button} button to jump to the next year * @childControl month-year-label {qx.ui.basic.Label} shows the current month and year * @childControl week {qx.ui.basic.Label} week label (used multiple times) * @childControl weekday {qx.ui.basic.Label} weekday label (used multiple times) * @childControl day {qx.ui.basic.Label} day label (used multiple times) * @childControl date-pane {qx.ui.container.Composite} the pane used to position the week, weekday and day labels * */ qx.Class.define("qx.ui.control.DateChooser", { extend : qx.ui.core.Widget, include : [ qx.ui.core.MExecutable, qx.ui.form.MForm ], implement : [ qx.ui.form.IExecutable, qx.ui.form.IForm, qx.ui.form.IDateForm ], /* ***************************************************************************** CONSTRUCTOR ***************************************************************************** */ /** * @param date {Date ? null} The initial date to show. If <code>null</code> * the current day (today) is shown. */ construct : function(date) { this.base(arguments); // set the layout var layout = new qx.ui.layout.VBox(); this._setLayout(layout); // create the child controls this._createChildControl("navigation-bar"); this._createChildControl("date-pane"); // Support for key events this.addListener("keypress", this._onKeyPress); // initialize format - moved from statics{} to constructor due to [BUG #7149] var DateChooser = qx.ui.control.DateChooser; if (!DateChooser.MONTH_YEAR_FORMAT) { DateChooser.MONTH_YEAR_FORMAT = qx.locale.Date.getDateTimeFormat("yyyyMMMM", "MMMM yyyy"); } // Show the right date var shownDate = (date != null) ? date : new Date(); this.showMonth(shownDate.getMonth(), shownDate.getFullYear()); // listen for locale changes if (qx.core.Environment.get("qx.dynlocale")) { qx.locale.Manager.getInstance().addListener("changeLocale", this._updateDatePane, this); } // register pointer up and down handler this.addListener("pointerdown", this._onPointerUpDown, this); this.addListener("pointerup", this._onPointerUpDown, this); }, /* ***************************************************************************** STATICS ***************************************************************************** */ statics : { /** * @type {string} The format for the date year label at the top center. */ MONTH_YEAR_FORMAT : null, /** * @type {string} The format for the weekday labels (the headers of the date table). */ WEEKDAY_FORMAT : "EE", /** * @type {string} The format for the week numbers (the labels of the left column). */ WEEK_FORMAT : "ww" }, /* ***************************************************************************** PROPERTIES ***************************************************************************** */ properties : { // overridden appearance : { refine : true, init : "datechooser" }, // overridden width : { refine : true, init : 200 }, // overridden height : { refine : true, init : 150 }, /** The currently shown month. 0 = january, 1 = february, and so on. */ shownMonth : { check : "Integer", init : null, nullable : true, event : "changeShownMonth" }, /** The currently shown year. */ shownYear : { check : "Integer", init : null, nullable : true, event : "changeShownYear" }, /** The date value of the widget. */ value : { check : "Date", init : null, nullable : true, event : "changeValue", apply : "_applyValue" } }, /* ***************************************************************************** MEMBERS ***************************************************************************** */ members : { __weekdayLabelArr : null, __dayLabelArr : null, __weekLabelArr : null, // overridden /** * @lint ignoreReferenceField(_forwardStates) */ _forwardStates : { invalid : true }, /* --------------------------------------------------------------------------- WIDGET INTERNALS --------------------------------------------------------------------------- */ // overridden _createChildControlImpl : function(id, hash) { var control; switch(id) { // NAVIGATION BAR STUFF case "navigation-bar": control = new qx.ui.container.Composite(new qx.ui.layout.HBox()); // Add the navigation bar elements control.add(this.getChildControl("last-year-button")); control.add(this.getChildControl("last-month-button")); control.add(this.getChildControl("month-year-label"), {flex: 1}); control.add(this.getChildControl("next-month-button")); control.add(this.getChildControl("next-year-button")); this._add(control); break; case "last-year-button-tooltip": control = new qx.ui.tooltip.ToolTip(this.tr("Last year")); break; case "last-year-button": control = new qx.ui.toolbar.Button(); control.addState("lastYear"); control.setFocusable(false); control.setToolTip(this.getChildControl("last-year-button-tooltip")); control.addListener("tap", this._onNavButtonTap, this); break; case "last-month-button-tooltip": control = new qx.ui.tooltip.ToolTip(this.tr("Last month")); break; case "last-month-button": control = new qx.ui.toolbar.Button(); control.addState("lastMonth"); control.setFocusable(false); control.setToolTip(this.getChildControl("last-month-button-tooltip")); control.addListener("tap", this._onNavButtonTap, this); break; case "next-month-button-tooltip": control = new qx.ui.tooltip.ToolTip(this.tr("Next month")); break; case "next-month-button": control = new qx.ui.toolbar.Button(); control.addState("nextMonth"); control.setFocusable(false); control.setToolTip(this.getChildControl("next-month-button-tooltip")); control.addListener("tap", this._onNavButtonTap, this); break; case "next-year-button-tooltip": control = new qx.ui.tooltip.ToolTip(this.tr("Next year")); break; case "next-year-button": control = new qx.ui.toolbar.Button(); control.addState("nextYear"); control.setFocusable(false); control.setToolTip(this.getChildControl("next-year-button-tooltip")); control.addListener("tap", this._onNavButtonTap, this); break; case "month-year-label": control = new qx.ui.basic.Label(); control.setAllowGrowX(true); control.setAnonymous(true); break; case "week": control = new qx.ui.basic.Label(); control.setAllowGrowX(true); control.setAllowGrowY(true); control.setSelectable(false); control.setAnonymous(true); control.setCursor("default"); break; case "weekday": control = new qx.ui.basic.Label(); control.setAllowGrowX(true); control.setAllowGrowY(true); control.setSelectable(false); control.setAnonymous(true); control.setCursor("default"); break; case "day": control = new qx.ui.basic.Label(); control.setAllowGrowX(true); control.setAllowGrowY(true); control.setCursor("default"); control.addListener("pointerdown", this._onDayTap, this); control.addListener("dbltap", this._onDayDblTap, this); break; case "date-pane": var controlLayout = new qx.ui.layout.Grid(); control = new qx.ui.container.Composite(controlLayout); for (var i = 0; i < 8; i++) { controlLayout.setColumnFlex(i, 1); } for (var i = 0; i < 7; i++) { controlLayout.setRowFlex(i, 1); } // Create the weekdays // Add an empty label as spacer for the week numbers var label = this.getChildControl("week#0"); label.addState("header"); control.add(label, {column: 0, row: 0}); this.__weekdayLabelArr = []; for (var i=0; i<7; i++) { label = this.getChildControl("weekday#" + i); control.add(label, {column: i + 1, row: 0}); this.__weekdayLabelArr.push(label); } // Add the days this.__dayLabelArr = []; this.__weekLabelArr = []; for (var y = 0; y < 6; y++) { // Add the week label var label = this.getChildControl("week#" + (y+1)); control.add(label, {column: 0, row: y + 1}); this.__weekLabelArr.push(label); // Add the day labels for (var x = 0; x < 7; x++) { var label = this.getChildControl("day#" + ((y*7)+x)); control.add(label, {column:x + 1, row:y + 1}); this.__dayLabelArr.push(label); } } this._add(control); break; } return control || this.base(arguments, id); }, // apply methods _applyValue : function(value, old) { if ((value != null) && (this.getShownMonth() != value.getMonth() || this.getShownYear() != value.getFullYear())) { // The new date is in another month -> Show that month this.showMonth(value.getMonth(), value.getFullYear()); } else { // The new date is in the current month -> Just change the states var newDay = (value == null) ? -1 : value.getDate(); for (var i=0; i<6*7; i++) { var dayLabel = this.__dayLabelArr[i]; if (dayLabel.hasState("otherMonth")) { if (dayLabel.hasState("selected")) { dayLabel.removeState("selected"); } } else { var day = parseInt(dayLabel.getValue(), 10); if (day == newDay) { dayLabel.addState("selected"); } else if (dayLabel.hasState("selected")) { dayLabel.removeState("selected"); } } } } }, /* --------------------------------------------------------------------------- EVENT HANDLER --------------------------------------------------------------------------- */ /** * Handler which stops the propagation of the tap event if * the navigation bar or calendar headers will be tapped. * * @param e {qx.event.type.Pointer} The pointer up / down event */ _onPointerUpDown : function(e) { var target = e.getTarget(); if (target == this.getChildControl("navigation-bar") || target == this.getChildControl("date-pane")) { e.stopPropagation(); return; } }, /** * Event handler. Called when a navigation button has been tapped. * * @param evt {qx.event.type.Data} The data event. */ _onNavButtonTap : function(evt) { var year = this.getShownYear(); var month = this.getShownMonth(); switch(evt.getCurrentTarget()) { case this.getChildControl("last-year-button"): year--; break; case this.getChildControl("last-month-button"): month--; if (month < 0) { month = 11; year--; } break; case this.getChildControl("next-month-button"): month++; if (month >= 12) { month = 0; year++; } break; case this.getChildControl("next-year-button"): year++; break; } this.showMonth(month, year); }, /** * Event handler. Called when a day has been tapped. * * @param evt {qx.event.type.Data} The event. */ _onDayTap : function(evt) { var time = evt.getCurrentTarget().dateTime; this.setValue(new Date(time)); }, /** * Event handler. Called when a day has been double-tapped. */ _onDayDblTap : function() { this.execute(); }, /** * Event handler. Called when a key was pressed. * * @param evt {qx.event.type.Data} The event. */ _onKeyPress : function(evt) { var dayIncrement = null; var monthIncrement = null; var yearIncrement = null; if (evt.getModifiers() == 0) { switch(evt.getKeyIdentifier()) { case "Left": dayIncrement = -1; break; case "Right": dayIncrement = 1; break; case "Up": dayIncrement = -7; break; case "Down": dayIncrement = 7; break; case "PageUp": monthIncrement = -1; break; case "PageDown": monthIncrement = 1; break; case "Escape": if (this.getValue() != null) { this.setValue(null); return; } break; case "Enter": case "Space": if (this.getValue() != null) { this.execute(); } return; } } else if (evt.isShiftPressed()) { switch(evt.getKeyIdentifier()) { case "PageUp": yearIncrement = -1; break; case "PageDown": yearIncrement = 1; break; } } if (dayIncrement != null || monthIncrement != null || yearIncrement != null) { var date = this.getValue(); if (date != null) { date = new Date(date.getTime()); } if (date == null) { date = new Date(); } else { if (dayIncrement != null){date.setDate(date.getDate() + dayIncrement);} if (monthIncrement != null){date.setMonth(date.getMonth() + monthIncrement);} if (yearIncrement != null){date.setFullYear(date.getFullYear() + yearIncrement);} } this.setValue(date); } }, /** * Shows a certain month. * * @param month {Integer ? null} the month to show (0 = january). If not set * the month will remain the same. * @param year {Integer ? null} the year to show. If not set the year will * remain the same. */ showMonth : function(month, year) { if ((month != null && month != this.getShownMonth()) || (year != null && year != this.getShownYear())) { if (month != null) { this.setShownMonth(month); } if (year != null) { this.setShownYear(year); } this._updateDatePane(); } }, /** * Event handler. Used to handle the key events. * * @param e {qx.event.type.Data} The event. */ handleKeyPress : function(e) { this._onKeyPress(e); }, /** * Updates the date pane. */ _updateDatePane : function() { var DateChooser = qx.ui.control.DateChooser; var today = new Date(); var todayYear = today.getFullYear(); var todayMonth = today.getMonth(); var todayDayOfMonth = today.getDate(); var selDate = this.getValue(); var selYear = (selDate == null) ? -1 : selDate.getFullYear(); var selMonth = (selDate == null) ? -1 : selDate.getMonth(); var selDayOfMonth = (selDate == null) ? -1 : selDate.getDate(); var shownMonth = this.getShownMonth(); var shownYear = this.getShownYear(); var startOfWeek = qx.locale.Date.getWeekStart(); // Create a help date that points to the first of the current month var helpDate = new Date(this.getShownYear(), this.getShownMonth(), 1); var monthYearFormat = new qx.util.format.DateFormat(DateChooser.MONTH_YEAR_FORMAT); this.getChildControl("month-year-label").setValue(monthYearFormat.format(helpDate)); // Show the day names var firstDayOfWeek = helpDate.getDay(); var firstSundayInMonth = 1 + ((7 - firstDayOfWeek) % 7); var weekDayFormat = new qx.util.format.DateFormat(DateChooser.WEEKDAY_FORMAT); for (var i=0; i<7; i++) { var day = (i + startOfWeek) % 7; var dayLabel = this.__weekdayLabelArr[i]; helpDate.setDate(firstSundayInMonth + day); dayLabel.setValue(weekDayFormat.format(helpDate)); if (qx.locale.Date.isWeekend(day)) { dayLabel.addState("weekend"); } else { dayLabel.removeState("weekend"); } } // Show the days helpDate = new Date(shownYear, shownMonth, 1, 12, 0, 0); var nrDaysOfLastMonth = (7 + firstDayOfWeek - startOfWeek) % 7; helpDate.setDate(helpDate.getDate() - nrDaysOfLastMonth); var weekFormat = new qx.util.format.DateFormat(DateChooser.WEEK_FORMAT); for (var week=0; week<6; week++) { this.__weekLabelArr[week].setValue(weekFormat.format(helpDate)); for (var i=0; i<7; i++) { var dayLabel = this.__dayLabelArr[week * 7 + i]; var year = helpDate.getFullYear(); var month = helpDate.getMonth(); var dayOfMonth = helpDate.getDate(); var isSelectedDate = (selYear == year && selMonth == month && selDayOfMonth == dayOfMonth); if (isSelectedDate) { dayLabel.addState("selected"); } else { dayLabel.removeState("selected"); } if (month != shownMonth) { dayLabel.addState("otherMonth"); } else { dayLabel.removeState("otherMonth"); } var isToday = (year == todayYear && month == todayMonth && dayOfMonth == todayDayOfMonth); if (isToday) { dayLabel.addState("today"); } else { dayLabel.removeState("today"); } dayLabel.setValue("" + dayOfMonth); dayLabel.dateTime = helpDate.getTime(); // Go to the next day helpDate.setDate(helpDate.getDate() + 1); } } monthYearFormat.dispose(); weekDayFormat.dispose(); weekFormat.dispose(); } }, /* ***************************************************************************** DESTRUCTOR ***************************************************************************** */ destruct : function() { if (qx.core.Environment.get("qx.dynlocale")) { qx.locale.Manager.getInstance().removeListener("changeLocale", this._updateDatePane, this); } this.__weekdayLabelArr = this.__dayLabelArr = this.__weekLabelArr = null; } });