Android Address Book with AngularJs and MongoDB ('-' * 47)
A Pen by Daniele Moraschi on CodePen.
Android Address Book with AngularJs and MongoDB ('-' * 47)
A Pen by Daniele Moraschi on CodePen.
<style> | |
@import url(http://weloveiconfonts.com/api/?family=entypo); | |
[class*="entypo-"]:before { | |
font-family: 'entypo', sans-serif; | |
} | |
</style> | |
<div ng-app="android-addressbook"> | |
<div ng-controller="AddressBook.Init"> | |
<div class="row main-app"> | |
<div ng-view></div> | |
</div> | |
</div> | |
<script type="text/ng-template" id="list.html"> | |
<ul class="fixed top full bar topbar tabs two"> | |
<li class="icon active"> | |
<a href='#/contacts'><span class="entypo-user"></span></a> | |
</li> | |
<li class="icon"> | |
<a href='#/contacts/starred'><span class="entypo-star"></span></a> | |
</li> | |
</ul> | |
<div class="content" id="wrapper"> | |
<div class="lists" id="scroller"> | |
<div ng:repeat="group in groups"> | |
<div class="title fleft full">{{ group.label }}</div> | |
<ul class="list single-fill"> | |
<li class="list-item" ng:repeat="contact in group.contacts"> | |
<a class="item" href='#/contact/view/{{ contact._id.$oid }}'> | |
<span class="fleft name">{{ contact.firstName + ' ' + contact.lastName }}</span> | |
<span class="fright image"><img width="60" ng-src="{{ ProfileImage('60x60', contact) }}"/></span> | |
</a> | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div> | |
<ul class="fixed bottom full bar bottombar"> | |
<li class="icon icn left"> | |
<a href='#/contacts/search'><span class="entypo-search"></span></a> | |
</li> | |
<li class="icon icn center"> | |
<a href='#/contact/add'><span class="entypo-user-add"></span></a> | |
</li> | |
</ul> | |
</script> | |
<script type="text/ng-template" id="starred.html"> | |
<ul class="fixed top full bar topbar tabs two"> | |
<li class="icon"> | |
<a href='#/contacts'><span class="entypo-user"></span></a> | |
</li> | |
<li class="icon active"> | |
<a href='#/contacts/starred'><span class="entypo-star"></span></a> | |
</li> | |
</ul> | |
<div class="content" id="wrapper"> | |
<div id="scroller" class="fleft"> | |
<div class="lists full"> | |
<ul class="list single-fill"> | |
<li class="list-item half" ng:repeat="contact in starred"> | |
<a class="item profile-image" href='#/contact/view/{{ contact._id.$oid }}'> | |
<img width="100%" ng-src="{{ ProfileImage('480x480', contact) }}"/> | |
<span class="absolute bottom caption"> | |
<span class="text">{{ contact.firstName + ' ' + contact.lastName }}</span> | |
<span class="overlay"></span> | |
</span> | |
</a> | |
</li> | |
</ul> | |
</div> | |
<div class="lists"> | |
<div class="title fleft full">Frequently viewed</div> | |
<ul class="list single-fill"> | |
<li class="list-item" ng:repeat="contact in contacts"> | |
<a class="item" href='#/contact/view/{{ contact._id.$oid }}'> | |
<span class="fleft name">{{ contact.firstName + ' ' + contact.lastName }}</span> | |
<span class="fright image"><img width="60" ng-src="{{ ProfileImage('60x60', contact) }}"/></span> | |
</a> | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div> | |
<ul class="fixed bottom full bar bottombar"> | |
<li class="icon icn left"> | |
<a href='#/contacts/search'><span class="entypo-search"></span></a> | |
</li> | |
<li class="icon icn center"> | |
<a href='#/contact/add'><span class="entypo-user-add"></span></a> | |
</li> | |
</ul> | |
</script> | |
<script type="text/ng-template" id="search.html"> | |
<ul class="fixed top full bar topbar"> | |
<li class="icon icn left"> | |
<a href='javascript:void(0)' ng-click="Back()"><span class="entypo-left-open-big"></span></a> | |
</li> | |
<li class="contact-name icn full form"> | |
<div class="form-item"> | |
<input placeholder="Find contacts" type="text" id="searchterm" name="searchterm" ng-model="searchterm"/> | |
<span class="form-item-decorator"></span> | |
<span class="cancel entypo-cancel-circled" ng-show="searchterm" ng-click="searchterm=''"></span> | |
</div> | |
</li> | |
</ul> | |
<div class="content no-bottombar" id="wrapper"> | |
<div class="lists" id="scroller"> | |
<div ng:repeat="group in groups"> | |
<div class="title fleft full">{{ group.label }}</div> | |
<ul class="list single-fill"> | |
<li class="list-item" ng:repeat="contact in group.contacts | filter:searchterm"> | |
<a class="item" href='#/contact/view/{{ contact._id.$oid }}'> | |
<span class="fleft name">{{ contact.firstName + ' ' + contact.lastName }}</span> | |
<span class="fright image"><img width="60" ng-src="{{ ProfileImage('60x60', contact) }}"/></span> | |
</a> | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div> | |
</script> | |
<script type="text/ng-template" id="view.html"> | |
<ul class="fixed top full bar topbar"> | |
<li class="icon icn left"> | |
<a href='javascript:void(0)' ng-click="Back()"><span class="entypo-left-open-big"></span></a> | |
</li> | |
<li class="contact-name icn full" ng-bind="FullName()"></li> | |
<li class="icon icn right sec"> | |
<a href='javascript:void(0)'><span ng-click="StarUnStar()" ng-class="{'entypo-star': contact.starred, 'entypo-star-empty': !contact.starred}"></span></a> | |
</li> | |
<li class="icon icn right" ng-click="_submenu()" ng-class="{'show-submenu': submenu}"> | |
<a href='javascript:void(0)'><span class="entypo-pencil"></span></a> | |
<ul class="submenu"> | |
<li> | |
<a href='#/contact/edit/{{ contact._id.$oid }}'>edit</a> | |
</li> | |
<li> | |
<a href='javascript:void(0)' ng-click="DeleteContact()">delete</a> | |
</li> | |
</ul> | |
</li> | |
</ul> | |
<div class="content no-bottombar" id="wrapper"> | |
<div class="lists fleft" id="scroller"> | |
<div class="profile-image" ng-click="_showImage()" ng-class="{open: selected}"> | |
<img ng-src="{{ ProfileImage('480x480') }}"/> | |
</div> | |
<div ng-show="contact.phones.length"> | |
<div class="title fleft full">Phone</div> | |
<ul class="list"> | |
<li class="list-item" ng:repeat="item in contact.phones"> | |
<a class="item" href="tel:{{ item.value }}"> | |
<span class="value">{{ item.value }}</span> | |
<span class="label">{{ item.type }}</span> | |
</a> | |
</li> | |
</ul> | |
</div> | |
<div ng-show="contact.emails.length"> | |
<div class="title fleft full">Email</div> | |
<ul class="list"> | |
<li class="list-item" ng:repeat="item in contact.emails"> | |
<a class="item" target="_black" href="mailto:{{ item.value }}"> | |
<span class="value">{{ item.value }}</span> | |
<span class="label">{{ item.type }}</span> | |
</a> | |
</li> | |
</ul> | |
</div> | |
<div ng-show="contact.addresses.length"> | |
<div class="title fleft full">Address</div> | |
<ul class="list"> | |
<li class="list-item" ng:repeat="item in contact.addresses"> | |
<a class="item" target="_black" href="https://maps.google.com/maps?q={{ item.value }}"> | |
<span class="value">{{ item.value }}</span> | |
<span class="label">{{ item.type }}</span> | |
</a> | |
</li> | |
</ul> | |
</div> | |
<div ng-show="contact.birthday"> | |
<div class="title fleft full">Birthday</div> | |
<ul class="list"> | |
<li class="list-item"> | |
<span class="item"> | |
<span class="value">{{ contact.birthday }}</span> | |
</span> | |
</li> | |
</ul> | |
</div> | |
<div ng-show="contact.websites.length"> | |
<div class="title fleft full">Websites</div> | |
<ul class="list"> | |
<li class="list-item" ng:repeat="item in contact.websites"> | |
<a class="item" target="_black" href="{{ item.value }}"> | |
<span class="value">{{ item.value }}</span> | |
<span class="label">{{ item.type }}</span> | |
</a> | |
</li> | |
</ul> | |
</div> | |
<div ng-show="contact.notes"> | |
<div class="title fleft full">Notes</div> | |
<ul class="list"> | |
<li class="list-item"> | |
<span class="item"> | |
<span class="value">{{ contact.notes }}</span> | |
</span> | |
</li> | |
</ul> | |
</div> | |
</div> | |
</div> | |
</script> | |
<script type="text/ng-template" id="edit.html"> | |
<ul class="fixed top full bar topbar"> | |
<li class="icon icn left"> | |
<a href='javascript:void(0)' ng-click="SaveContact()"><span ng-class="{'entypo-dot': contactForm.name.$invalid, 'entypo-check': !contactForm.name.$invalid}"></span></a> | |
</li> | |
<li class="contact-name icn full" ng-bind="FullName()"></li> | |
<li class="icon icn right" ng-show="contact._id.$oid"> | |
<a ng-href="#/contact/view/{{ contact._id.$oid }}"><span class="entypo-cancel"></span></a> | |
</li> | |
<li class="icon icn right" ng-show="!contact._id.$oid"> | |
<a href='javascript:void(0)' ng-click="Back()"><span class="entypo-cancel"></span></a> | |
</li> | |
</ul> | |
<div class="content no-bottombar" id="wrapper"> | |
<div class="lists fleft" id="scroller"> | |
<form class="form" name="contactForm"> | |
<div class="title fleft full">Name</div> | |
<ul class="list"> | |
<li class="list-item"> | |
<div class="item form-item"> | |
<input type="text" name="name" maxlength="25" ng-model="contact.firstName" required /> | |
<span class="form-item-decorator"></span> | |
</div> | |
</li> | |
<li class="list-item"> | |
<div class="item form-item"> | |
<input type="text" maxlength="25" ng-model="contact.lastName"/> | |
<span class="form-item-decorator"></span> | |
</div> | |
</li> | |
</ul> | |
<div class="title fleft full">Picture URL</div> | |
<ul class="list"> | |
<li class="list-item"> | |
<div class="item form-item field-left img"> | |
<input type="text" ng-model="contact.picture"/> | |
<span class="form-item-decorator"></span> | |
</div> | |
<div class="item form-item field-right img"> | |
<img width="60" ng-src="{{ ProfileImage('60x60') }}"/> | |
</div> | |
</li> | |
</ul> | |
<div class="title fleft full">Phone</div> | |
<ul class="list"> | |
<li class="list-item" ng:repeat="item in contact.phones"> | |
<div class="item form-item field-left"> | |
<input type="text" ng-model="item.value"/> | |
<span class="form-item-decorator"></span> | |
</div> | |
<div class="item form-item field-right"> | |
<input type="text" ng-model="item.type"/> | |
<span class="form-item-decorator"></span> | |
</div> | |
<div class="item form-item field-right-right action"> | |
<button class="delete" ng-click="DiscardField('phones', $index)"><span class="entypo-cancel"></span></button> | |
</div> | |
</li> | |
<li class="list-item action"> | |
<button class="save" ng-click="AddField('phones')">Add new</button> | |
</li> | |
</ul> | |
<div class="title fleft full">Email</div> | |
<ul class="list"> | |
<li class="list-item" ng:repeat="item in contact.emails"> | |
<div class="item form-item field-left"> | |
<input type="text" ng-model="item.value"/> | |
<span class="form-item-decorator"></span> | |
</div> | |
<div class="item form-item field-right"> | |
<input type="text" ng-model="item.type"/> | |
<span class="form-item-decorator"></span> | |
</div> | |
<div class="item form-item field-right-right action"> | |
<button class="delete" ng-click="DiscardField('emails', $index)"><span class="entypo-cancel"></span></button> | |
</div> | |
</li> | |
<li class="list-item action"> | |
<button class="save" ng-click="AddField('emails')">Add new</button> | |
</li> | |
</ul> | |
<div class="title fleft full">Address</div> | |
<ul class="list"> | |
<li class="list-item" ng:repeat="item in contact.addresses"> | |
<div class="item form-item field-left"> | |
<input type="text" ng-model="item.value"/> | |
<span class="form-item-decorator"></span> | |
</div> | |
<div class="item form-item field-right"> | |
<input type="text" ng-model="item.type"/> | |
<span class="form-item-decorator"></span> | |
</div> | |
<div class="item form-item field-right-right action"> | |
<button class="delete" ng-click="DiscardField('addresses', $index)"><span class="entypo-cancel"></span></button> | |
</div> | |
</li> | |
<li class="list-item action"> | |
<button class="save" ng-click="AddField('addresses')">Add new</button> | |
</li> | |
</ul> | |
<div class="title fleft full">Birthday</div> | |
<ul class="list"> | |
<li class="list-item"> | |
<div class="item form-item"> | |
<input type="date" ng-model="contact.birthday"/> | |
<span class="form-item-decorator"></span> | |
</div> | |
</li> | |
</ul> | |
<div class="title fleft full">Websites</div> | |
<ul class="list"> | |
<li class="list-item" ng:repeat="item in contact.websites"> | |
<div class="item form-item field-left"> | |
<input type="url" ng-model="item.value"/> | |
<span class="form-item-decorator"></span> | |
</div> | |
<div class="item form-item field-right"> | |
<input type="text" ng-model="item.type"/> | |
<span class="form-item-decorator"></span> | |
</div> | |
<div class="item form-item field-right-right action"> | |
<button class="delete" ng-click="DiscardField('websites', $index)"><span class="entypo-cancel"></span></button> | |
</div> | |
</li> | |
<li class="list-item action"> | |
<button class="save" ng-click="AddField('websites')">Add new</button> | |
</li> | |
</ul> | |
<div class="title fleft full">Notes</div> | |
<ul class="list"> | |
<li class="list-item"> | |
<div class="item form-item"> | |
<textarea ng-model="contact.notes"></textarea> | |
<span class="form-item-decorator"></span> | |
</div> | |
</li> | |
</ul> | |
</form> | |
</div> | |
</div> | |
</script> | |
</div> |
'use strict'; | |
/* | |
Android Address Book replica with AngularJs | |
=========================================== | |
GitHub project: https://github.com/danielemoraschi/android-addressbook | |
Touch scrolling by iScroll: http://cubiq.org/iscroll-4 | |
Fake contacts list by: http://www.generatedata.com/ | |
DB reset every 2h | |
Best in Mobile / Chrome / Safari | |
Released under the MIT License: | |
http://www.opensource.org/licenses/mit-license.php | |
*/ | |
var AddressBook = (function() { | |
var iscroll, current_route, | |
_init = function($scope) { | |
iscroll = null; | |
current_route = '/contacts'; | |
}, | |
_iScroll = function() { | |
iscroll && iscroll.destroy(); | |
iscroll = new iScroll('wrapper', { hScroll: false }); | |
setTimeout(function() { | |
iscroll.refresh(); | |
}, 0); | |
}, | |
_detail_ctrl = function($scope, $location, $routeParams, Utils, Contacts) { | |
var self = this; | |
$scope.selected = false; | |
$scope.submenu = false; | |
$scope.contact = { | |
starred: false, | |
firstName: "", | |
lastName: "", | |
birthday: "", | |
picture: "", | |
phones: [], | |
emails: [], | |
addresses: [], | |
websites: [], | |
notes: "" | |
}; | |
$scope._showImage = function() { | |
$scope.selected = !$scope.selected; | |
} | |
$scope._submenu = function() { | |
$scope.submenu = !$scope.submenu; | |
} | |
$scope.Back = function() { | |
$location.path(current_route); | |
} | |
$scope.ProfileImage = function(dim) { | |
return ($scope.contact && $scope.contact.picture) || "https://raw.github.com/danielemoraschi/android-addressbook/master/imgs/ic_contact_picture_"+dim+".png"; | |
} | |
$scope.FullName = function(dim) { | |
return ($scope.contact.firstName && $scope.contact.firstName.trim()) | |
? $scope.contact.firstName + ' ' + $scope.contact.lastName | |
: ($scope.contact._id ? 'No name' : 'New contact'); | |
} | |
$scope.StarUnStar = function () { | |
$scope.contact.starred = !$scope.contact.starred; | |
$scope.contact.update(); | |
} | |
$scope.AddField = function(type) { | |
$scope.contact[type] || ($scope.contact[type] = []); | |
$scope.contact[type].push({ | |
type: '', | |
value: '' | |
}); | |
} | |
$scope.DiscardField = function(type, index) { | |
if($scope.contact[type] && $scope.contact[type][index]) { | |
$scope.contact[type].splice(index,1); | |
} | |
} | |
$scope.SaveContact = function () { | |
if($scope.contact.firstName && $scope.contact.firstName.trim()) { | |
var arrays = {'phones': [], 'emails': [], 'addresses': []}; | |
angular.forEach(arrays, function(v, k) { | |
angular.forEach($scope.contact[k], function(val, key) { | |
if(val.value.trim()) { | |
arrays[k].push(val); | |
} | |
}); | |
$scope.contact[k] = arrays[k]; | |
}); | |
if($scope.contact._id) { | |
$scope.contact.update(function() { | |
$location.path('/contact/view/' + $scope.contact._id.$oid); | |
}); | |
} | |
else { | |
Contacts.save($scope.contact, function(contact) { | |
$location.path('/contact/edit/' + contact._id.$oid); | |
}); | |
} | |
} | |
} | |
$scope.DeleteContact = function () { | |
if($scope.contact._id.$oid) { | |
var c = confirm("Delete this contact?") | |
if (c==true) { | |
self.original.delete(function() { | |
$location.path('/contacts'); | |
}); | |
} | |
} | |
} | |
if($routeParams.id) { | |
Contacts.get({id: $routeParams.id}, function(contact) { | |
self.original = contact; | |
if(!self.original.views) { | |
self.original.views = 0; | |
} | |
self.original.views++; | |
$scope.contact = new Contacts(self.original); | |
$scope.contact.update(); | |
_iScroll(); | |
}); | |
} else { | |
_iScroll(); | |
} | |
}, | |
_list_ctrl = function($scope, $location, $routeParams, Utils, Contacts) { | |
var i, ch, self = this; | |
$scope.orderProp = 'firstName'; | |
$scope.groups = {}; | |
$scope.contacts = {}; | |
$scope.starred = {}; | |
$scope.searchterm = ''; | |
$scope.ProfileImage = function(dim, contact) { | |
return contact.picture ? contact.picture.replace("480x480", dim) : "https://raw.github.com/danielemoraschi/android-addressbook/master/imgs/ic_contact_picture_"+dim+".png"; | |
} | |
$scope.Back = function() { | |
$location.path(current_route); | |
} | |
switch($location.$$url) { | |
case "/contacts/starred": | |
current_route = $location.$$url; | |
$scope.starred = Contacts.query({q: '{"starred":true}'}, function() { | |
$scope.contacts = Contacts.query({q: '{"views":{"$gt":0}}', l: 10}, function() { | |
_iScroll(); | |
}); | |
}); | |
break; | |
case "/contacts/search": | |
$scope.contacts = Contacts.query(function() { | |
$scope.groups = [{ | |
label: 'All contacts', | |
contacts: $scope.contacts | |
}]; | |
_iScroll(); | |
}); | |
break; | |
default: | |
current_route = $location.$$url; | |
$scope.contacts = Contacts.query(function() { | |
Utils.groupify($scope.contacts, $scope.groups); | |
_iScroll(); | |
}); | |
break; | |
} | |
}; | |
return { | |
Init: _init, | |
DetailCtrl: _detail_ctrl, | |
ListCtrl: _list_ctrl | |
} | |
})(); | |
angular.module('mongolab', ['ngResource']). | |
factory('Contacts', function($resource) { | |
var Contacts = $resource( | |
'https://api.mongolab.com/api/1/databases/addressbook/collections/contacts/:id', | |
{ apiKey: 'RO27EEbdFsJfycTn_JUiAnr3qIcsgyxS' }, | |
{ update: { method: 'PUT' } } | |
); | |
Contacts.prototype.update = function(cb) { | |
return Contacts.update({id: this._id.$oid}, | |
angular.extend({}, this, {_id:undefined}), cb); | |
}; | |
Contacts.prototype.delete = function(cb) { | |
return Contacts.remove({id: this._id.$oid}, cb); | |
}; | |
return Contacts; | |
}); | |
angular.module('helpers', []). | |
factory('Utils', function() { | |
return { | |
groupify : function(source, into) { | |
var i, ch; | |
for (i = source.length - 1; i >= 0; i--) { | |
ch = source[i].firstName.charAt(0); | |
into[ch] || (into[ch] = { | |
label: ch, | |
contacts: [] | |
}); | |
into[ch].contacts.push(source[i]); | |
}; | |
} | |
} | |
}); | |
angular.module('android-addressbook', ['mongolab', 'helpers']). | |
config(['$routeProvider', function($routeProvider, $locationProvider) { | |
$routeProvider. | |
when('/contacts', {templateUrl: 'list.html', controller: AddressBook.ListCtrl}). | |
when('/contacts/starred', {templateUrl: 'starred.html', controller: AddressBook.ListCtrl}). | |
when('/contacts/search', {templateUrl: 'search.html', controller: AddressBook.ListCtrl}). | |
when('/contact/add', {templateUrl: 'edit.html', controller: AddressBook.DetailCtrl}). | |
when('/contact/view/:id', {templateUrl: 'view.html', controller: AddressBook.DetailCtrl}). | |
when('/contact/edit/:id', {templateUrl: 'edit.html', controller: AddressBook.DetailCtrl}). | |
otherwise({redirectTo: '/contacts'}); | |
}]); |
@width: 480px; | |
@base-fontsize: 16px; | |
@icons-fontsize: 20px; | |
@main-color: #51B4E3; | |
@light-color: #A6D6EA; | |
@bkg-color: #eee; | |
@bottombar-bkg: #555; | |
@text-color: #000; | |
@icons-color: #fff; | |
@list-bottomborder-color: #ddd; | |
@labels-color: #6F6F6F; | |
@topbar-height: 50px; | |
@bottombar-height: 50px; | |
@tabs-height: @topbar-height; | |
@starred-caption-height: 40px; | |
@profile-image-height: 180px; | |
@titles-height: 30px; | |
@list-padding: 20px; | |
@list-item-height: 60px; | |
@list-item-lineheight: 20px; | |
@list-item-padding: 10px; | |
@in-width: @width - (@list-padding * 2); | |
@submenu-minwidth: 200px; | |
@submenu-bkg: #eee; | |
@submenu-shadow: #999; | |
@submenu-bordercolor: #ccc; | |
@submenu-item-height: 40px; | |
@submenu-item-padding: 0 20px; | |
.opacity(@v) { | |
-webkit-opacity: @v; | |
-moz-opacity: @v; | |
-o-opacity: @v; | |
opacity: @v; | |
} | |
.box-shadow(@x, @y, @h, @color) { | |
-webkit-box-shadow: @x @y @h @color; | |
-moz-box-shadow: @x @y @h @color; | |
-o-box-shadow: @x @y @h @color; | |
box-shadow: @x @y @h @color; | |
} | |
.border-box() { | |
-webkit-box-sizing: border-box; | |
-moz-box-sizing: border-box; | |
-o-box-sizing: border-box; | |
box-sizing: border-box; | |
} | |
/*****/ | |
.fixed { | |
position: fixed; | |
} | |
.absolute { | |
position: absolute; | |
} | |
ul, ol, | |
.zero { | |
margin: 0; | |
padding: 0; | |
list-style-type: none; | |
} | |
.full { | |
width: 100%; | |
} | |
.fixed.full, | |
.absolute.full { | |
width: @width; | |
} | |
.top { | |
top: 0; | |
} | |
.bottom { | |
bottom: 0; | |
} | |
.fleft { | |
float: left; | |
} | |
.fright { | |
float: right; | |
} | |
.overlay { | |
background: #000; | |
position: absolute; | |
top: 0; bottom: 0; | |
left: 0; right: 0; | |
.opacity(0.6); | |
} | |
/*****/ | |
body, | |
html { | |
height: 100%; | |
} | |
body { | |
margin: 0; | |
padding: 0; | |
font: @base-fontsize Helvetica, sans-serif; | |
color: @main-color; | |
background: #000 url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAJ0lEQVQIW2M0Njb+f+/ePQYYYBQUFPyvpKTEABMEC4BkYYJwAZggADGyDjN7QaaVAAAAAElFTkSuQmCC); | |
} | |
a { | |
text-decoration: none; | |
color: @text-color; | |
} | |
#scroller { | |
width: 100%; | |
.border-box; | |
} | |
.row { | |
position: relative; | |
margin: auto; | |
width: @width; | |
} | |
.content { | |
z-index: 1; | |
position: fixed; | |
background: @bkg-color; | |
top: @topbar-height; | |
bottom: @bottombar-height; | |
width: @width; | |
} | |
.content.no-bottombar { | |
bottom: 0; | |
} | |
.topbar { | |
z-index: 2; | |
height: @topbar-height; | |
background: @main-color; | |
.box-shadow(0, 2px, 10px, #888); | |
} | |
.bottombar { | |
z-index: 2; | |
height: @bottombar-height; | |
background: @bottombar-bkg; | |
} | |
.title { | |
font-size: @base-fontsize - 2%; | |
color: @main-color; | |
border-bottom: 2px solid @main-color; | |
height: @titles-height; | |
line-height: @titles-height; | |
text-transform: uppercase; | |
} | |
/*****/ | |
.lists { | |
float: left; | |
width: @in-width; | |
padding: (@list-padding / 2) @list-padding (@list-padding / 2) @list-padding; | |
} | |
.lists.full { | |
width: @width; | |
padding: 0; | |
} | |
.list, | |
.list-item, | |
.list-item .item { | |
float: left; | |
width: 100%; | |
position: relative; | |
} | |
.list-item .item { | |
display: block; | |
line-height: @list-item-lineheight; | |
padding: @list-item-padding 0; | |
border-top: 1px solid @list-bottomborder-color; | |
} | |
.list.single-fill .list-item .item { | |
position: relative; | |
padding: 0; | |
height: @list-item-height; | |
line-height: @list-item-height; | |
} | |
.list.single-fill .list-item.half { | |
width: 50%; | |
overflow: hidden; | |
} | |
.list.single-fill .list-item.half .item { | |
overflow: hidden; | |
height: @profile-image-height; | |
border-top: none; | |
} | |
.list.single-fill .list-item.half .item.profile-image { | |
left: 0; | |
top: 0; | |
} | |
.list.single-fill .list-item.half .item.profile-image img { | |
margin-top: -15%; | |
} | |
.list.single-fill .caption { | |
height: @starred-caption-height; | |
width: 100%; | |
} | |
.caption .text{ | |
position: absolute; | |
top: 0; bottom: 0; | |
left: 0; right: 0; | |
line-height: @starred-caption-height; | |
padding: 0 20px; | |
width: 100%; | |
color: #fff; | |
z-index: 2; | |
.border-box; | |
} | |
.list-item:first-child .item { | |
color: @text-color; | |
border-top: none; | |
} | |
.list-item span.fleft { | |
width: @in-width - @list-item-height; | |
} | |
.list-item span.fright { | |
width: @list-item-height; | |
height: @list-item-height; | |
overflow: hidden; | |
} | |
.list-item span.fright img { | |
float: right; | |
} | |
.list-item .item .label { | |
display: block; | |
text-transform: uppercase; | |
font-size: @base-fontsize - 2%; | |
color: #666; | |
} | |
/*****/ | |
.tabs > li > a, | |
.tabs > li { | |
float: left; | |
} | |
.tabs > li { | |
width: 50%; | |
height: @tabs-height - 5px; | |
} | |
.tabs.three > li { | |
width: 33.33%; | |
} | |
.tabs > li:hover, | |
.tabs > li.active { | |
border-bottom: 5px solid @icons-color; | |
} | |
.tabs > li > a { | |
width: 100%; | |
height: @tabs-height - 20%; | |
line-height: @tabs-height - 20%; | |
margin-top: (@tabs-height - (@tabs-height - 20%)) / 2; | |
text-align: center; | |
border-right: 1px solid @light-color; | |
} | |
.tabs > li:last-child > a { | |
border-right: none; | |
} | |
/*****/ | |
.icon > a { | |
color: @icons-color; | |
font-size: @icons-fontsize; | |
} | |
.bar .icn, | |
.bar .icn > a { | |
display: block; | |
width: @bottombar-height; | |
height: @bottombar-height; | |
line-height: @bottombar-height; | |
text-align: center; | |
} | |
.bar .icn { | |
position: absolute; | |
z-index: 2; | |
} | |
.bar .icn.left { | |
left: 0; | |
} | |
.bar .icn.right { | |
right: 0; | |
} | |
.bar .icn.right.sec { | |
right: 60px; | |
} | |
.bar .icn.center { | |
left: 50%; | |
margin-left: -(@bottombar-height / 2); | |
} | |
.bar .icn.full { | |
z-index: 1; | |
float: left; | |
padding-left: 60px; | |
width: auto; | |
} | |
.bar .icn.full.centred { | |
padding-left: 0; | |
float: none; | |
width: 100%; | |
} | |
.bar .icn.action, | |
.bar .icn.action a { | |
width: auto; | |
font-size: @base-fontsize; | |
} | |
.bar .icn.action a span { | |
font-size: @base-fontsize + 6%; | |
} | |
.bar .icn.action a { | |
padding: 0 10px; | |
} | |
.bar .icn.action a span{ | |
padding-right: 10px; | |
} | |
.contact-name { | |
color: @icons-color; | |
font-size: @base-fontsize + 2%; | |
font-weight: bold; | |
} | |
/*****/ | |
.submenu { | |
display: none; | |
position: absolute; | |
text-align: left; | |
min-width: @submenu-minwidth; | |
background: @submenu-bkg; | |
right: @list-padding / 2; | |
.box-shadow(0, 2px, 5px, @submenu-shadow); | |
} | |
.show-submenu .submenu { | |
display: block; | |
} | |
.submenu li a { | |
height: @submenu-item-height; | |
line-height: @submenu-item-height; | |
padding: @submenu-item-padding; | |
display: block; | |
text-transform: capitalize; | |
} | |
.submenu li { | |
border-bottom: 1px solid @submenu-bordercolor; | |
} | |
.submenu li:last-child a { | |
border-bottom:none; | |
} | |
/*****/ | |
.profile-image { | |
position: relative; | |
overflow: hidden; | |
height: @profile-image-height; | |
width: @width; | |
left: -@list-padding; | |
top: -(@list-padding / 2); | |
} | |
.profile-image img { | |
position: absolute; | |
margin-top: -40%; | |
width: 100%; | |
} | |
.profile-image.open { | |
height:auto; | |
} | |
.profile-image.open img { | |
position: static; | |
margin-top: 0; | |
} | |
/******/ | |
.form .title { | |
padding-left: 10px; | |
.border-box; | |
} | |
.form .list-item:last-child { | |
margin-bottom: 20px; | |
} | |
.form .form-item { | |
position: relative; | |
height: auto; | |
min-height: auto; | |
border-top: none; | |
width: 100%; | |
padding: 10px; | |
.border-box; | |
} | |
.form .list-item button { | |
border: none; | |
background: none; | |
margin: 0; | |
text-transform: uppercase; | |
font-size: @base-fontsize - 2%; | |
color: #666; | |
} | |
.form .list-item.action button { | |
padding: 5px 10px 0; | |
margin: 10px 0 0; | |
} | |
.form .form-item.action { | |
text-align: center; | |
} | |
.form .form-item.action button { | |
font-size: @base-fontsize + 10%; | |
} | |
.form .form-item input, | |
.form .form-item textarea { | |
width: 100%; | |
border: none; | |
background: none; | |
margin: 0; | |
padding: 7px 0 3px; | |
} | |
.form .form-item .form-item-decorator { | |
width: 99%; | |
height: 5px; | |
position: absolute; | |
left: 0; | |
bottom: 0; | |
border-bottom: 1px solid @list-bottomborder-color; | |
border-left: 1px solid @list-bottomborder-color; | |
border-right: 1px solid @list-bottomborder-color; | |
} | |
.form .form-item input:focus, | |
.form .form-item textarea:focus { | |
outline: none | |
} | |
form .form-item input:focus + .form-item-decorator, | |
form .form-item textarea:focus + .form-item-decorator { | |
border-color: @main-color; | |
} | |
form .form-item input.ng-invalid + .form-item-decorator { | |
border-color: red; | |
} | |
.form .form-item.field-left { | |
width: 62%; | |
} | |
.form .form-item.field-right { | |
width: 23%; | |
} | |
.form .form-item.field-right-right { | |
width: 15%; | |
padding: 10px 0 | |
} | |
.form .form-item.field-left.img { | |
width: 70%; | |
} | |
.form .form-item.field-right.img { | |
width: 30%; | |
padding: (@list-padding / 2) 0; | |
} | |
.form .form-item.field-right img { | |
float: right; | |
padding: 0 @list-padding; | |
} | |
.bar .icn.full.contact-name { | |
text-align: left; | |
max-width: 50%; | |
overflow: hidden; | |
} | |
.bar .icn.full.contact-name.form { | |
width: @width - @list-padding; | |
max-width: @width - @list-padding; | |
.border-box; | |
} | |
.contact-name.form .form-item { | |
height: @topbar-height - (@list-padding / 2); | |
padding-top: 0; | |
} | |
.contact-name.form .form-item .cancel { | |
position: absolute; | |
right: @list-padding / 2; | |
top: 0; | |
} | |
.contact-name.form .form-item input { | |
color: @icons-color; | |
} | |
.contact-name.form .form-item ::-webkit-input-placeholder { | |
color: @icons-color; | |
} | |
.contact-name.form .form-item :-moz-placeholder { | |
color: @icons-color; | |
} | |
.contact-name.form .form-item ::-moz-placeholder { | |
color: @icons-color; | |
} | |
.contact-name.form .form-item placeholder { | |
color: @icons-color; | |
} | |
/*********************/ | |
@media only screen and (max-device-width : 480px) { | |
.fixed.full, | |
.absolute.full, | |
.row, | |
.content, | |
.lists.full { | |
width: 100%; | |
} | |
.profile-image { | |
width: 108%; | |
} | |
.lists { | |
padding: 2% 4% 2% 4%; | |
width: 92%; | |
} | |
.lists { | |
width: 92%; | |
} | |
.list-item span.fleft { | |
width: 80%; | |
} | |
.list-item span.fright { | |
width: 20%; | |
} | |
.submenu { | |
right: 20%; | |
} | |
.profile-image { | |
left: -4%; | |
margin-top: -2%; | |
} | |
.form .form-item.field-right img { | |
padding: 0 2%; | |
} | |
.list.single-fill .list-item.half .item.profile-image img { | |
margin-top: -5%; | |
} | |
.list.single-fill .list-item.half .item.profile-image img { | |
margin-top: -5%; | |
} | |
.bar .icn.full.contact-name.form { | |
width: 96%; | |
} | |
.contact-name.form .form-item .cancel { | |
right: 2%; | |
} | |
} |