Releases: mcasimir/mobile-angular-ui
v1.3.3
v1.3.2
v1.3.1
v1.3.0
Features
- added
ui-shared-state
and deprecatedui-state
so it does not clash with ui-router (9ad2f57) - activeLinks module supports html5Mode (d3cbbbf)
- updated fastclick to version 1.0.6 (03060e2)
- updated font-awesome to version 4.6.3 (9f7424c)
Fixes
1.2.0-beta.5
Migrating to Mobile Angular UI 1.2
All of the functionalities are now separed in 3 modules: core
, components
and gestures
.
Core UI functionalities
Core UI Functionalities are those any HTML UI built on Angular could use. They are not Mobile-specific nor depending on anything else that Angular itself, and you could use them with any css framework.
YieldTo/ContentFor
They are the same that 1.1 beside to be prefixed with ui-
so use: ui-yield-to
and ui-content-for
.
Toggle/Toggleable to SharedState and ui-*
Toggle has been rewritten from scratch for 1.2. In place of that there is a new service with it's own isolated context.
It is called SharedState
. It will act as a BUS between UI elements to share UI related state that is automatically disposed when all scopes requiring it are destroyed.
eg.
SharedState.initialize(requiringScope, 'myId');
SharedState.toggle('myId');
Using SharedState
directives is very close to attach an ng-init
in root element and interact through expression and ng-click, but it has handy features to avoid polluting controllers/scopes with ui-related stuffs.
SharedState
methods are exposed through ui-
directives. So you should never use it directly in your controller, and you can spare them to hold application logic.
Example: Tabs
<div class="tabs" ui-state="activeTab">
<ul class="nav nav-tabs">
<li ui-class="{'active': activeTab == 1)}">
<a ui-set="{'activeTab': 1}">Tab 1</a>
</li>
<li ui-class="{'active': activeTab == 2)}">
<a ui-set="{'activeTab': 2}">Tab 2</a>
</li>
<li ui-class="{'active': activeTab == 3)}">
<a ui-set="{'activeTab': 3}">Tab 3</a>
</li>
</ul>
<div ui-if="activeTab == 1">
Tab 1
</div>
<div ui-if="activeTab == 2">
Tab 2
</div>
<div ui-if="activeTab == 3">
Tab 3
</div>
</div>
NOTE: ui-toggle/set/turnOn/turnOff
responds to click/tap
without stopping propagation so you can use them along with ng-click too. You can also change events to respond to with ui-triggers
attribute.
Any SharedState
method is exposed through Ui
object in $rootScope
. So you could always do ng-click="Ui.turnOn('myVar')"
.
Since SharedState
is a service you can initialize/set statuses through controllers too:
app.controller('myController', function($scope, SharedState){
SharedState.initialize($scope, "activeTab", 3);
});
As well as you can use ui-default
for that:
<div class="tabs" ui-state="activeTab" ui-default="thisIsAnExpression(5 + 1 - 2)"></div>
Outer Clicks
ui-outer-click
and ui-outer-click-if
are directives that allow to specifiy a behaviour when click/tap events happen outside an element. This can be easily used to implement eg. close on outer click feature for a dropdown.
<div class="btn-group pull-right">
<a ui-turn-on='myDropdown' class='btn'>
<i class="fa fa-ellipsis-v"></i>
</a>
<ul
class="dropdown-menu"
ui-state="myDropdown"
ui-if="myDropdown"
ui-outer-click="ui.turnOff('myDropdown')"
ui-outer-click-if="ui.active('myDropdown')"
ui-turn-off="myDropdown">
<li><a>Action</a></li>
<li><a>Another action</a></li>
<li><a>Something else here</a></li>
<li class="divider"></li>
<li><a>Separated link</a></li>
</ul>
</div>
New Experimental Features
$swipe
A drop in replacement for ngTouch
's $swipe
. It is free from other ngTouch
features that does not play or duplicates fastclicks behaviour.
$drag
$drag
Service wraps $swipe
to extend its behavior moving target element through css transform according to the $swipe
coords thus creating a drag effect.
$drag interface is very close to $swipe
:
app.controller('MyController', function($drag, $element){
var unbindDrag = $drag.bind($element, {
// drag callbacks
// - rect is the current result of getBoundingClientRect() for bound element
// - cancelFn issue a "touchcancel" on element
// - resetFn restore the initial transform
// - undoFn undoes the current movement
// - swipeCoords are the coordinates exposed by the underlying $swipe service
start: function(rect, cancelFn, resetFn, swipeCoords){},
move: function(rect, cancelFn, resetFn, swipeCoords){},
end: function(rect, undoFn, resetFn, swipeCoords) {};
cancel: function(rect, resetFn){},
// constraints for the movement
// you can use a "static" object of the form:
// {top: .., lelf: .., bottom: .., rigth: ..}
// or pass a function that is called on each movement
// and return it in a dynamic way.
// This is useful if you have to constraint drag movement while bounduaries are
// changing over time.
constraint: function(){ return {top: y1, left: x1, bottom: y2, right: x2}; }, // or just {top: y1, left: x1, bottom: y2, right: x2}
// instantiates the Trasform according to touch movement (defaults to `t.translate(dx, dy);`)
// dx, dy are the distances of movement for x and y axis after constraints are applyied
transform: function(transform, dx, dy, currSwipeX, currSwipeY, startSwipeX, startSwipeY) {},
// changes the Transform before is applied to element (useful to add something like easing or accelleration)
adaptTransform: function(transform, dx, dy, currSwipeX, currSwipeY, startSwipeX, startSwipeY) {}
});
// This is automatically called when element is disposed so it is not necessary
// that you call this manually but if you have to detatch $drag service before
// this you could just call:
unbindDrag();
});
Main differences with $swipe
are:
- bound elements will move following swipe direction automatically
- coords param take into account css transform so you can easily detect collision with other elements.
- start, move, end callback receive a cancel funcion that can be used to cancel the motion and reset
the transform. - you can configure the transform behavior passing a transform function to options.
- you can constraint the motion through the constraint option (setting relative movement limits)
Example 1. Drag to dismiss
app.directive('dragToDismiss', function($drag, $parse, $timeout){
return {
restrict: 'A',
compile: function(elem, attrs) {
var dismissFn = $parse(attrs.dragToDismiss);
return function(scope, elem, attrs){
var dismiss = false;
$drag.bind(elem, {
constraint: {
minX: 0,
minY: 0,
maxY: 0
},
move: function(c) {
if( c.left >= c.width / 4) {
dismiss = true;
elem.addClass('dismiss');
} else {
dismiss = false;
elem.removeClass('dismiss');
}
},
cancel: function(){
elem.removeClass('dismiss');
},
end: function(c, undo, reset) {
if (dismiss) {
elem.addClass('dismitted');
$timeout(function() {
scope.$apply(function() {
dismissFn(scope);
});
}, 400);
} else {
reset();
}
}
});
};
}
};
});
Example 2. Touch enabled "deck of cards" carousel directive
app.directive('carousel', function(){
return {
restrict: 'C',
scope: {},
controller: function($scope) {
this.itemCount = 0;
this.activeItem = null;
this.addItem = function(){
var newId = this.itemCount++;
this.activeItem = this.itemCount == 1 ? newId : this.activeItem;
return newId;
};
this.next = function(){
this.activeItem = this.activeItem || 0;
this.activeItem = this.activeItem == this.itemCount - 1 ? 0 : this.activeItem + 1;
};
this.prev = function(){
this.activeItem = this.activeItem || 0;
this.activeItem = this.activeItem === 0 ? this.itemCount - 1 : this.activeItem - 1;
};
}
};
});
app.directive('carouselItem', function($drag) {
return {
restrict: 'C',
require: '^carousel',
scope: {},
transclude: true,
template: '<div class="item"><div ng-transclude></div></div>',
link: function(scope, elem, attrs, carousel) {
scope.carousel = carousel;
var id = carousel.addItem();
var zIndex = function(){
var res = 0;
if (id == carousel.activeItem){
res = 2000;
} else if (carousel.activeItem < id) {
res = 2000 - (id - carousel.activeItem);
} else {
res = 2000 - (carousel.itemCount - 1 - carousel.activeItem + id);
}
return res;
};
scope.$watch(function(){
return carousel.activeItem;
}, function(n, o){
elem[0].style['z-index']=zIndex();
});
$drag.bind(elem, {
constraint: { minY: 0, maxY: 0 },
adaptTransform: function(t, dx, dy, x, y, x0, y0) {
var maxAngle = 15;
var velocity = 0.02;
var r = t.getRotation();
var newRot = r + Math.round(dx * velocity);
newRot = Math.min(newRot, maxAngle);
newRot = Math.max(newRot, -maxAngle);
t.rotate(-r);
t.rotate(newRot);
},
move: function(c){
if(c.left >= c.width / 4 || c.left <= -(c.width / 4)) {
elem.addClass('dismiss');
} else {
elem.removeClass('dismiss');
}
},
cancel: function(){
elem.removeClass('dismiss');
},
end: function(c, undo, reset) {
elem.removeClass('dismiss');
if(c.left >= c.width / 4) {
scope.$apply(function() {
carousel.next();
});
} else if (c.left <= -(c.width / 4)) {
scope.$apply(function() {
carousel.next();
...
1.1.0-beta.21
Fixes #53: Android devices bad rendering box model with %-based translate3d. Switched to fixed sidebars width and media-queries.