UNPKG

@qooxdoo/framework

Version:

The JS Framework for Coders

591 lines (510 loc) 15.9 kB
/* ************************************************************************ qooxdoo - the new era of web development http://qooxdoo.org Copyright: 2004-2011 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: * Gabriel Munteanu (gabios) * Christopher Zuendorf (czuendorf) ************************************************************************ */ /** * The popup represents a widget that gets shown above other widgets, * usually to present more info/details regarding an item in the application. * * There are 3 usages for now: * * <pre class='javascript'> * var widget = new qx.ui.mobile.form.Button("Error!"); * var popup = new qx.ui.mobile.dialog.Popup(widget); * popup.show(); * </pre> * Here we show a popup consisting of a single buttons alerting the user * that an error has occurred. * It will be centered to the screen. * <pre class='javascript'> * var label = new qx.ui.mobile.basic.Label("Item1"); * var widget = new qx.ui.mobile.form.Button("Error!"); * var popup = new qx.ui.mobile.dialog.Popup(widget, label); * popup.show(); * widget.addListener("tap", function(){ * popup.hide(); * }); * * </pre> * * In this case everything is as above, except that the popup will get shown next to "label" * so that the user can understand that the info presented is about the "Item1" * we also add a tap listener to the button that will hide out popup. * * Once created, the instance is reused between show/hide calls. * * <pre class='javascript'> * var widget = new qx.ui.mobile.form.Button("Error!"); * var popup = new qx.ui.mobile.dialog.Popup(widget); * popup.placeTo(25,100); * popup.show(); * </pre> * * Same as the first example, but this time the popup will be shown at the 25,100 coordinates. * * */ qx.Class.define("qx.ui.mobile.dialog.Popup", { extend: qx.ui.mobile.core.Widget, statics: { ROOT: null }, /** * @param widget {qx.ui.mobile.core.Widget} the widget that will be shown in the popup * @param anchor {qx.ui.mobile.core.Widget?} optional parameter, a widget to attach this popup to */ construct(widget, anchor) { super(); this.exclude(); if (qx.ui.mobile.dialog.Popup.ROOT == null) { qx.ui.mobile.dialog.Popup.ROOT = qx.core.Init.getApplication().getRoot(); } qx.ui.mobile.dialog.Popup.ROOT.add(this); this.__anchor = anchor; if (widget) { this._initializeChild(widget); } }, properties: { // overridden defaultCssClass: { refine: true, init: "popup" }, /** * The label/caption/text of the qx.ui.mobile.basic.Atom instance */ title: { apply: "_applyTitle", nullable: true, check: "String", event: "changeTitle" }, /** * Any URI String supported by qx.ui.mobile.basic.Image to display an icon */ icon: { check: "String", apply: "_applyIcon", nullable: true, event: "changeIcon" }, /** * Whether the popup should be displayed modal. */ modal: { init: false, check: "Boolean", nullable: false }, /** * Indicates whether the a modal popup should disappear when user taps/clicks on Blocker. */ hideOnBlockerTap: { check: "Boolean", init: false } }, members: { __isShown: false, __isPlaced: false, __childrenContainer: null, __percentageTop: null, __anchor: null, __widget: null, __titleWidget: null, __lastPopupDimension: null, /** * Event handler. Called whenever the position of the popup should be updated. */ _updatePosition() { // Traverse single anchor classes for removal, for preventing 'domupdated' event if no CSS classes changed. var anchorClasses = ["top", "bottom", "left", "right", "anchor"]; for (var i = 0; i < anchorClasses.length; i++) { this.removeCssClass(anchorClasses[i]); } if (this.__anchor) { this.addCssClass("anchor"); var rootHeight = qx.ui.mobile.dialog.Popup.ROOT.getHeight(); var rootWidth = qx.ui.mobile.dialog.Popup.ROOT.getWidth(); var rootPosition = qx.bom.element.Location.get( qx.ui.mobile.dialog.Popup.ROOT.getContainerElement() ); var anchorPosition = qx.bom.element.Location.get( this.__anchor.getContainerElement() ); var popupDimension = qx.bom.element.Dimension.getSize( this.getContainerElement() ); this.__lastPopupDimension = popupDimension; var computedPopupPosition = qx.util.placement.Placement.compute( popupDimension, { width: rootPosition.left + rootWidth, height: rootPosition.top + rootHeight }, anchorPosition, { left: 0, right: 0, top: 0, bottom: 0 }, "bottom-left", "keep-align", "keep-align" ); // Reset Anchor. this._resetPosition(); var isTop = anchorPosition.top > computedPopupPosition.top; var isLeft = anchorPosition.left > computedPopupPosition.left; computedPopupPosition.top = computedPopupPosition.top - rootPosition.top; computedPopupPosition.left = computedPopupPosition.left - rootPosition.left; var isOutsideViewPort = computedPopupPosition.top < 0 || computedPopupPosition.left < 0 || computedPopupPosition.left + popupDimension.width > rootWidth || computedPopupPosition.top + popupDimension.height > rootHeight; if (isOutsideViewPort) { this._positionToCenter(); } else { if (isTop) { this.addCssClass("bottom"); } else { this.addCssClass("top"); } if (isLeft) { this.addCssClass("right"); } else { this.addCssClass("left"); } this.placeTo(computedPopupPosition.left, computedPopupPosition.top); } } else if (this.__childrenContainer && !this.__isPlaced) { // No Anchor and not placed to point manually this._positionToCenter(); } }, /** * This method shows the popup. * First it updates the position, then registers the event handlers, and shows it. */ show() { if (!this.__isShown) { qx.core.Init.getApplication().fireEvent("popup"); this.__registerEventListener(); // Needs to be added to screen, before rendering position, for calculating // objects height. super.show(); // Now render position. this._updatePosition(); } this.__isShown = true; if (this.getModal() === true) { qx.ui.mobile.core.Blocker.getInstance().show(); if (this.getHideOnBlockerTap()) { qx.ui.mobile.core.Blocker.getInstance().addListener( "tap", this.hide, this ); } } }, /** * Hides the popup. */ hide() { if (this.__isShown) { this.__unregisterEventListener(); this.exclude(); } this.__isShown = false; if (this.getModal()) { qx.ui.mobile.core.Blocker.getInstance().hide(); } qx.ui.mobile.core.Blocker.getInstance().removeListener( "tap", this.hide, this ); }, /** * Hides the popup after a given time delay. * @param delay {Integer} time delay in ms. */ hideWithDelay(delay) { if (delay) { qx.lang.Function.delay(this.hide, delay, this); } else { this.hide(); } }, /** * Returns the shown state of this popup. * @return {Boolean} whether the popup is shown or not. */ isShown() { return this.__isShown; }, /** * Toggles the visibility of this popup. */ toggleVisibility() { if (this.__isShown == true) { this.hide(); } else { this.show(); } }, /** * This method positions the popup widget at the coordinates specified. * @param left {Integer} - the value the will be set to container's left style property * @param top {Integer} - the value the will be set to container's top style property */ placeTo(left, top) { this.__isPlaced = true; this._setStyle("left", left + "px"); this._setStyle("top", top + "px"); }, /** * Tracks the user tap on root and hides the widget if <code>pointerdown</code> event * occurs outside of the widgets bounds. * @param evt {qx.event.type.Pointer} the pointer event. */ _trackUserTap(evt) { var clientX = evt.getViewportLeft(); var clientY = evt.getViewportTop(); var popupLocation = qx.bom.element.Location.get( this.getContainerElement() ); var isOutsideWidget = clientX < popupLocation.left || clientX > popupLocation.left + this.__lastPopupDimension.width || clientY > popupLocation.top + this.__lastPopupDimension.height || clientY < popupLocation.top; if (isOutsideWidget) { this.hide(); } }, /** * Centers this widget to window's center position. */ _positionToCenter() { var container = this.getContainerElement(); container.style.position = "absolute"; container.style.marginLeft = -parseInt(container.offsetWidth / 2) + "px"; container.style.marginTop = -parseInt(container.offsetHeight / 2) + "px"; container.style.left = "50%"; container.style.top = "50%"; }, /** * Resets the position of this element (left, top, margins...) */ _resetPosition() { var container = this.getContainerElement(); container.style.left = "0px"; container.style.top = "0px"; container.style.marginLeft = null; container.style.marginTop = null; }, /** * Registers all needed event listeners */ __registerEventListener() { qx.core.Init.getApplication().addListener("stop", this.hide, this); qx.core.Init.getApplication().addListener("popup", this.hide, this); qx.event.Registration.addListener( window, "resize", this._updatePosition, this ); if (this.__anchor) { this.__anchor.addCssClass("anchor-target"); qx.ui.mobile.dialog.Popup.ROOT.addListener( "pointerdown", this._trackUserTap, this ); } }, /** * Unregisters all needed event listeners */ __unregisterEventListener() { qx.core.Init.getApplication().removeListener("stop", this.hide, this); qx.core.Init.getApplication().removeListener("popup", this.hide, this); qx.event.Registration.removeListener( window, "resize", this._updatePosition, this ); if (this.__anchor) { this.__anchor.removeCssClass("anchor-target"); qx.ui.mobile.dialog.Popup.ROOT.removeListener( "pointerdown", this._trackUserTap, this ); } }, /** * This method creates the container where the popup's widget will be placed * and adds it to the popup. * @param widget {qx.ui.mobile.core.Widget} - what to show in the popup * */ _initializeChild(widget) { if (this.__childrenContainer == null) { this.__childrenContainer = new qx.ui.mobile.container.Composite( new qx.ui.mobile.layout.VBox() ); this.__childrenContainer.setDefaultCssClass("popup-content"); this._add(this.__childrenContainer); } if (this._createTitleWidget()) { this.__childrenContainer.remove(this._createTitleWidget()); this.__childrenContainer.add(this._createTitleWidget()); } this.__childrenContainer.add(widget, { flex: 1 }); widget.addListener("domupdated", this._updatePosition, this); this.__widget = widget; }, /** * Creates the title atom widget. * * @return {qx.ui.mobile.basic.Atom} The title atom widget. */ _createTitleWidget() { if (this.__titleWidget) { return this.__titleWidget; } if (this.getTitle() || this.getIcon()) { this.__titleWidget = new qx.ui.mobile.basic.Atom( this.getTitle(), this.getIcon() ); this.__titleWidget.addCssClass("popup-title"); return this.__titleWidget; } else { return null; } }, // property apply _applyTitle(value, old) { if (value) { if (this.__titleWidget) { this.__titleWidget.setLabel(value); } else { this.__titleWidget = new qx.ui.mobile.basic.Atom( value, this.getIcon() ); this.__titleWidget.addCssClass("popup-title"); if (this.__widget) { this.__childrenContainer.addBefore( this._createTitleWidget(), this.__widget ); } else { if (this.__childrenContainer) { this.__childrenContainer.add(this._createTitleWidget()); } } } } }, // property apply _applyIcon(value, old) { if (value) { if (this.__titleWidget) { this.__titleWidget.setIcon(value); } else { this.__titleWidget = new qx.ui.mobile.basic.Atom( this.getTitle(), value ); this.__titleWidget.addCssClass("popup-title"); if (this.__widget) { this.__childrenContainer.addBefore( this._createTitleWidget(), this.__widget ); } else { if (this.__childrenContainer) { this.__childrenContainer.add(this._createTitleWidget()); } } } } }, /** * Adds the widget that will be shown in this popup. This method can be used in the case when you have removed the widget from the popup * or you haven't passed it in the constructor. * @param widget {qx.ui.mobile.core.Widget} - what to show in the popup */ add(widget) { this.removeWidget(); this._initializeChild(widget); }, /** * A widget to attach this popup to. * * @param widget {qx.ui.mobile.core.Widget} The anchor widget. */ setAnchor(widget) { this.__anchor = widget; this._updatePosition(); }, /** * Returns the title widget. * * @return {qx.ui.mobile.basic.Atom} The title widget. */ getTitleWidget() { return this.__titleWidget; }, /** * This method removes the widget shown in the popup. * @return {qx.ui.mobile.core.Widget|null} The removed widget or <code>null</code> * if the popup doesn't have an attached widget */ removeWidget() { if (this.__widget) { this.__widget.removeListener("domupdated", this._updatePosition, this); this.__childrenContainer.remove(this.__widget); return this.__widget; } else { if (qx.core.Environment.get("qx.debug")) { qx.log.Logger.debug(this, "this popup has no widget attached yet"); } return null; } } }, destruct() { this.__unregisterEventListener(); this._disposeObjects("__childrenContainer"); this.__isShown = this.__isPlaced = this.__percentageTop = this._anchor = this.__widget = this.__lastPopupDimension = null; } });