@qooxdoo/framework
Version:
The JS Framework for Coders
386 lines (329 loc) • 10.3 kB
JavaScript
/* ************************************************************************
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:
* Tino Butz (tbtz)
* Christopher Zuendorf (czuendorf)
************************************************************************ */
/**
* A card layout.
*
* The card layout lays out widgets in a stack. Call show to display a widget.
* Only the widget which show method is called is displayed. All other widgets are excluded.
*
*
* *Example*
*
* Here is a little example of how to use the Card layout.
*
* <pre class="javascript">
* var layout = new qx.ui.mobile.layout.Card());
* var container = new qx.ui.mobile.container.Composite(layout);
*
* var label1 = new qx.ui.mobile.basic.Label("1");
* container.add(label1);
* var label2 = new qx.ui.mobile.basic.Label("2");
* container.add(label2);
*
* label2.show();
* </pre>
*
* @use(qx.event.handler.Transition)
*/
qx.Class.define("qx.ui.mobile.layout.Card", {
extend: qx.ui.mobile.layout.Abstract,
/*
*****************************************************************************
CONSTRUCTOR
*****************************************************************************
*/
construct() {
super();
this.__cardAnimation = new qx.ui.mobile.layout.CardAnimation();
},
/*
*****************************************************************************
EVENTS
*****************************************************************************
*/
events: {
/** Fired when the animation of a page transition starts */
animationStart: "qx.event.type.Data",
/** Fired when the animation of a page transition ends */
animationEnd: "qx.event.type.Data"
},
/*
*****************************************************************************
PROPERTIES
*****************************************************************************
*/
properties: {
/** The default animation to use for page transition */
defaultAnimation: {
check: "String",
init: "slide"
},
/** Flag which indicates, whether animation is needed, or widgets should only swap. */
showAnimation: {
check: "Boolean",
init: true
},
/** Transition duration of each animation. */
animationDuration: {
check: "Integer",
init: 350
}
},
/*
*****************************************************************************
MEMBERS
*****************************************************************************
*/
members: {
__nextWidget: null,
__currentWidget: null,
__inAnimation: null,
__animation: null,
__reverse: null,
__cardAnimation: null,
// overridden
_getCssClasses() {
return ["layout-card", "qx-vbox"];
},
// overridden
connectToChildWidget(widget) {
super.connectToChildWidget();
if (widget) {
widget.addCssClass("layout-card-item");
widget.addCssClass("qx-flex1");
widget.exclude();
}
},
// overridden
disconnectFromChildWidget(widget) {
super.disconnectFromChildWidget();
widget.removeCssClass("layout-card-item");
},
// overridden
updateLayout(widget, action, properties) {
if (action == "visible") {
this._showWidget(widget, properties);
}
super.updateLayout(widget, action, properties);
},
/**
* Setter for this.__cardAnimation.
* @param value {qx.ui.mobile.layout.CardAnimation} the new CardAnimation object.
*/
setCardAnimation(value) {
this.__cardAnimation = value;
},
/**
* Getter for this.__cardAnimation.
* @return {qx.ui.mobile.layout.CardAnimation} the current CardAnimation object.
*/
getCardAnimation() {
return this.__cardAnimation;
},
/**
* Shows the widget with the given properties.
*
* @param widget {qx.ui.mobile.core.Widget} The target widget
* @param properties {Map} The layout properties to set. Key / value pairs.
*/
_showWidget(widget, properties) {
if (this.__nextWidget == widget) {
return;
}
if (this.__inAnimation) {
this.__stopAnimation();
}
this.__nextWidget = widget;
if (
this.__currentWidget &&
this.getShowAnimation() &&
qx.core.Environment.get("css.transform.3d")
) {
properties = properties || {};
// both are explicit identity checks for null
if (
properties.animation === null ||
this.getCardAnimation().getMap()[properties.animation] === null
) {
this._swapWidget();
return;
}
this.__animation = properties.animation || this.getDefaultAnimation();
if (properties.action && properties.action === "back") {
this.__reverse = true;
} else {
properties.reverse =
properties.reverse === null ? false : properties.reverse;
this.__reverse = properties.reverse;
}
qx.bom.AnimationFrame.request(function () {
this.__startAnimation(widget);
}, this);
} else {
this._swapWidget();
}
},
/**
* Excludes the current widget and sets the next widget to the current widget.
*/
_swapWidget() {
if (this.__currentWidget) {
this.__currentWidget.removeCssClass("active");
this.__currentWidget.exclude();
}
this.__currentWidget = this.__nextWidget;
this.__currentWidget.addCssClass("active");
},
/**
* Fix size, only if widget has mixin MResize set,
* and nextWidget is set.
*
* @param widget {qx.ui.mobile.core.Widget} The target widget which should have a fixed size.
*/
_fixWidgetSize(widget) {
if (widget) {
var hasResizeMixin = qx.Class.hasMixin(
widget.constructor,
qx.ui.mobile.core.MResize
);
if (hasResizeMixin) {
// Size has to be fixed for animation.
widget.fixSize();
}
}
},
/**
* Releases recently fixed widget size (width/height). This is needed for allowing further
* flexbox layouting.
*
* @param widget {qx.ui.mobile.core.Widget} The target widget which should have a flexible size.
*/
_releaseWidgetSize(widget) {
if (widget) {
var hasResizeMixin = qx.Class.hasMixin(
widget.constructor,
qx.ui.mobile.core.MResize
);
if (hasResizeMixin) {
// Size has to be released after animation.
widget.releaseFixedSize();
}
}
},
/**
* Starts the animation for the page transition.
*
* @param widget {qx.ui.mobile.core.Widget} The target widget
*/
__startAnimation(widget) {
if (widget.isDisposed()) {
return;
}
// Fix size of current and next widget, then start animation.
this.__inAnimation = true;
this.fireDataEvent("animationStart", [this.__currentWidget, widget]);
var fromElement = this.__currentWidget.getContainerElement();
var toElement = widget.getContainerElement();
qx.event.Registration.addListener(
fromElement,
"animationEnd",
this._onAnimationEnd,
this
);
qx.event.Registration.addListener(
toElement,
"animationEnd",
this._onAnimationEnd,
this
);
var fromCssClasses = this.__getAnimationClasses("out");
var toCssClasses = this.__getAnimationClasses("in");
this._widget.addCssClass("animationParent");
var toElementAnimation = this.__cardAnimation.getAnimation(
this.__animation,
"in",
this.__reverse
);
var fromElementAnimation = this.__cardAnimation.getAnimation(
this.__animation,
"out",
this.__reverse
);
qx.bom.element.Class.addClasses(toElement, toCssClasses);
qx.bom.element.Class.addClasses(fromElement, fromCssClasses);
qx.bom.element.Animation.animate(toElement, toElementAnimation);
qx.bom.element.Animation.animate(fromElement, fromElementAnimation);
},
/**
* Event handler. Called when the animation of the page transition ends.
*
* @param evt {qx.event.type.Event} The causing event
*/
_onAnimationEnd(evt) {
this.__stopAnimation();
this.fireDataEvent("animationEnd", [
this.__currentWidget,
this.__nextWidget
]);
},
/**
* Stops the animation for the page transition.
*/
__stopAnimation() {
if (this.__inAnimation) {
var fromElement = this.__currentWidget.getContainerElement();
var toElement = this.__nextWidget.getContainerElement();
qx.event.Registration.removeListener(
fromElement,
"animationEnd",
this._onAnimationEnd,
this
);
qx.event.Registration.removeListener(
toElement,
"animationEnd",
this._onAnimationEnd,
this
);
qx.bom.element.Class.removeClasses(
fromElement,
this.__getAnimationClasses("out")
);
qx.bom.element.Class.removeClasses(
toElement,
this.__getAnimationClasses("in")
);
this._swapWidget();
this._widget.removeCssClass("animationParent");
this.__inAnimation = false;
}
},
/**
* Returns the animation CSS classes for a given direction. The direction
* can be <code>in</code> or <code>out</code>.
*
* @param direction {String} The direction of the animation. <code>in</code> or <code>out</code>.
* @return {String[]} The CSS classes for the set animation.
*/
__getAnimationClasses(direction) {
var classes = ["animationChild", this.__animation, direction];
if (this.__reverse) {
classes.push("reverse");
}
return classes;
}
},
destruct() {
this._disposeObjects("__cardAnimation");
}
});