UNPKG

@qooxdoo/framework

Version:

The JS Framework for Coders

610 lines (495 loc) 15.6 kB
/* ************************************************************************ qooxdoo - the new era of web development http://qooxdoo.org Copyright: 2004-2008 1&1 Internet AG, Germany, http://www.1und1.de License: MIT: https://opensource.org/licenses/MIT See the LICENSE file in the project's top-level directory for details. Authors: * Martin Wittemann (martinwittemann) ************************************************************************ */ /** * A *date field* is like a combo box with the date as popup. As button to * open the calendar a calendar icon is shown at the right to the textfield. * * To be conform with all form widgets, the {@link qx.ui.form.IForm} interface * is implemented. * * The following example creates a date field and sets the current * date as selected. * * <pre class='javascript'> * var dateField = new qx.ui.form.DateField(); * this.getRoot().add(dateField, {top: 20, left: 20}); * dateField.setValue(new Date()); * </pre> * * @childControl list {qx.ui.control.DateChooser} date chooser component * @childControl popup {qx.ui.popup.Popup} popup which shows the list control * @childControl textfield {qx.ui.form.TextField} text field for manual date entry * @childControl button {qx.ui.form.Button} button that opens the list control */ qx.Class.define("qx.ui.form.DateField", { extend : qx.ui.core.Widget, include : [ qx.ui.core.MRemoteChildrenHandling, qx.ui.form.MForm ], implement : [ qx.ui.form.IForm, qx.ui.form.IDateForm ], /* ***************************************************************************** CONSTRUCTOR ***************************************************************************** */ construct : function() { this.base(arguments); // set the layout var layout = new qx.ui.layout.HBox(); this._setLayout(layout); layout.setAlignY("middle"); // text field var textField = this._createChildControl("textfield"); this._createChildControl("button"); // register listeners this.addListener("tap", this._onTap, this); this.addListener("blur", this._onBlur, this); // forward the focusin and focusout events to the textfield. The textfield // is not focusable so the events need to be forwarded manually. this.addListener("focusin", function(e) { textField.fireNonBubblingEvent("focusin", qx.event.type.Focus); }, this); this.addListener("focusout", function(e) { textField.fireNonBubblingEvent("focusout", qx.event.type.Focus); }, this); // initializes the DateField with the default format this._setDefaultDateFormat(); // adds a locale change listener this._addLocaleChangeListener(); }, /* ***************************************************************************** EVENTS ***************************************************************************** */ events : { /** Whenever the value is changed this event is fired * * Event data: The new text value of the field. */ "changeValue" : "qx.event.type.Data" }, /* ***************************************************************************** PROPERTIES ***************************************************************************** */ properties : { /** The formatter, which converts the selected date to a string. **/ dateFormat : { check : "qx.util.format.DateFormat", apply : "_applyDateFormat" }, /** * String value which will be shown as a hint if the field is all of: * unset, unfocused and enabled. Set to null to not show a placeholder * text. */ placeholder : { check : "String", nullable : true, apply : "_applyPlaceholder" }, // overridden appearance : { refine : true, init : "datefield" }, // overridden focusable : { refine : true, init : true }, // overridden width : { refine : true, init : 120 } }, /* ***************************************************************************** MEMBERS ***************************************************************************** */ statics : { __dateFormat : null, __formatter : null, /** * Get the shared default date formatter * * @return {qx.util.format.DateFormat} The shared date formatter */ getDefaultDateFormatter : function() { var format = qx.locale.Date.getDateFormat("medium").toString(); if (format == this.__dateFormat) { return this.__formatter; } if (this.__formatter) { this.__formatter.dispose(); } this.__formatter = new qx.util.format.DateFormat(format, qx.locale.Manager.getInstance().getLocale()); this.__dateFormat = format; return this.__formatter; } }, /* ***************************************************************************** MEMBERS ***************************************************************************** */ members : { __localeListenerId : null, /** * @lint ignoreReferenceField(_forwardStates) */ _forwardStates : { focused : true, invalid : true }, /* --------------------------------------------------------------------------- PROTECTED METHODS --------------------------------------------------------------------------- */ /** * Sets the default date format which is returned by * {@link #getDefaultDateFormatter}. You can override this method to * define your own default format. */ _setDefaultDateFormat : function() { this.setDateFormat(qx.ui.form.DateField.getDefaultDateFormatter()); }, /** * Checks for "qx.dynlocale" and adds a listener to the locale changes. * On every change, {@link #_setDefaultDateFormat} is called to reinitialize * the format. You can easily override that method to prevent that behavior. */ _addLocaleChangeListener : function() { // listen for locale changes if (qx.core.Environment.get("qx.dynlocale")) { this.__localeListenerId = qx.locale.Manager.getInstance().addListener("changeLocale", function() { this._setDefaultDateFormat(); }, this); } }, /* --------------------------------------------------------------------------- PUBLIC METHODS --------------------------------------------------------------------------- */ /** * This method sets the date, which will be formatted according to * #dateFormat to the date field. It will also select the date in the * calendar popup. * * @param value {Date} The date to set. */ setValue : function(value) { // set the date to the textfield var textField = this.getChildControl("textfield"); textField.setValue(this.getDateFormat().format(value)); // set the date in the datechooser var dateChooser = this.getChildControl("list"); dateChooser.setValue(value); }, /** * Returns the current set date, parsed from the input-field * corresponding to the {@link #dateFormat}. * If the given text could not be parsed, <code>null</code> will be returned. * * @return {Date} The currently set date. */ getValue : function() { // get the value of the textfield var textfieldValue = this.getChildControl("textfield").getValue(); // return the parsed date try { if (textfieldValue == null || textfieldValue.length == 0) { return null; } return this.getDateFormat().parse(textfieldValue); } catch (ex) { return null; } }, /** * Resets the DateField. The textfield will be empty and the datechooser * will also have no selection. */ resetValue: function() { // set the date to the textfield var textField = this.getChildControl("textfield"); textField.setValue(""); // set the date in the datechooser var dateChooser = this.getChildControl("list"); dateChooser.setValue(null); }, /* --------------------------------------------------------------------------- LIST STUFF --------------------------------------------------------------------------- */ /** * Shows the date chooser popup. */ open : function() { var popup = this.getChildControl("popup"); popup.placeToWidget(this, true); popup.show(); }, /** * Hides the date chooser popup. */ close : function() { this.getChildControl("popup").hide(); }, /** * Toggles the date chooser popup visibility. */ toggle : function() { var isListOpen = this.getChildControl("popup").isVisible(); if (isListOpen) { this.close(); } else { this.open(); } }, /* --------------------------------------------------------------------------- PROPERTY APPLY METHODS --------------------------------------------------------------------------- */ // property apply routine _applyDateFormat : function(value, old) { // if old is undefined or null do nothing if (!old) { return; } // get the date with the old date format try { var textfield = this.getChildControl("textfield"); var dateStr = textfield.getValue(); if (dateStr != null) { var currentDate = old.parse(dateStr); textfield.setValue(value.format(currentDate)); } } catch (ex) { // do nothing if the former date could not be parsed } }, // property apply routine _applyPlaceholder : function(value, old) { this.getChildControl("textfield").setPlaceholder(value); }, /* --------------------------------------------------------------------------- WIDGET API --------------------------------------------------------------------------- */ // overridden _createChildControlImpl : function(id, hash) { var control; switch(id) { case "textfield": control = new qx.ui.form.TextField(); control.setFocusable(false); control.addState("inner"); control.addListener("changeValue", this._onTextFieldChangeValue, this); control.addListener("blur", this.close, this); this._add(control, {flex: 1}); break; case "button": control = new qx.ui.form.Button(); control.setFocusable(false); control.setKeepActive(true); control.addState("inner"); control.addListener("execute", this.toggle, this); this._add(control); break; case "list": control = new qx.ui.control.DateChooser(); control.setFocusable(false); control.setKeepFocus(true); control.addListener("execute", this._onChangeDate, this); break; case "popup": control = new qx.ui.popup.Popup(new qx.ui.layout.VBox); control.setAutoHide(false); control.add(this.getChildControl("list")); control.addListener("pointerup", this._onChangeDate, this); control.addListener("changeVisibility", this._onPopupChangeVisibility, this); break; } return control || this.base(arguments, id); }, /* --------------------------------------------------------------------------- EVENT LISTENERS --------------------------------------------------------------------------- */ /** * Handler method which handles the tap on the calender popup. * * @param e {qx.event.type.Pointer} The pointer event. */ _onChangeDate : function(e) { var textField = this.getChildControl("textfield"); var selectedDate = this.getChildControl("list").getValue(); textField.setValue(this.getDateFormat().format(selectedDate)); this.close(); }, /** * Toggles the popup's visibility. * * @param e {qx.event.type.Pointer} Pointer tap event */ _onTap : function(e) { this.close(); }, /** * Handler for the blur event of the current widget. * * @param e {qx.event.type.Focus} The blur event. */ _onBlur : function(e) { this.close(); }, /** * Handler method which handles the key press. It forwards all key event * to the opened date chooser except the escape key event. Escape closes * the popup. * If the list is cloned, all key events will not be processed further. * * @param e {qx.event.type.KeySequence} Keypress event */ _onKeyPress : function(e) { // get the key identifier var iden = e.getKeyIdentifier(); if (iden == "Down" && e.isAltPressed()) { this.toggle(); e.stopPropagation(); return; } // if the popup is closed, ignore all var popup = this.getChildControl("popup"); if (popup.getVisibility() == "hidden") { return; } // hide the list always on escape if (iden == "Escape") { this.close(); e.stopPropagation(); return; } // Stop navigation keys when popup is open if (iden === "Left" || iden === "Right" || iden === "Down" || iden === "Up") { e.preventDefault(); } // forward the rest of the events to the date chooser this.getChildControl("list").handleKeyPress(e); }, /** * Redirects changeVisibility event from the list to this widget. * * @param e {qx.event.type.Data} Property change event */ _onPopupChangeVisibility : function(e) { e.getData() == "visible" ? this.addState("popupOpen") : this.removeState("popupOpen"); // Synchronize the chooser with the current value on every // opening of the popup. This is needed when the value has been // modified and not saved yet (e.g. no blur) var popup = this.getChildControl("popup"); if (popup.isVisible()) { var chooser = this.getChildControl("list"); var date = this.getValue(); chooser.setValue(date); } }, /** * Reacts on value changes of the text field and syncs the * value to the combobox. * * @param e {qx.event.type.Data} Change event */ _onTextFieldChangeValue : function(e) { // Apply to popup var date = this.getValue(); if (date != null) { var list = this.getChildControl("list"); list.setValue(date); } // Fire event this.fireDataEvent("changeValue", this.getValue()); }, /** * Checks if the textfield of the DateField is empty. * * @return {Boolean} True, if the textfield of the DateField is empty. */ isEmpty: function() { var value = this.getChildControl("textfield").getValue(); return value == null || value == ""; }, // overridden focus : function() { this.base(arguments); this.getChildControl("textfield").getFocusElement().focus(); } }, destruct : function() { // listen for locale changes if (qx.core.Environment.get("qx.dynlocale")) { if (this.__localeListenerId) { qx.locale.Manager.getInstance().removeListenerById(this.__localeListenerId); } } } });