mirror of https://github.com/nextcloud/contacts
Add mobile style from Mail app, ref #36
- Maximise the field width and align delete icons - Added back button - Don't load first contact when mobile layout - Hide the contact list when showing contact details
This commit is contained in:
parent
b20f0c5ab1
commit
4d85245bd4
|
@ -140,7 +140,7 @@ detailsitem input[type="date"],
|
|||
detailsitem textarea,
|
||||
.select-addressbook select,
|
||||
.add-field {
|
||||
width: 200px;
|
||||
width: 245px;
|
||||
}
|
||||
|
||||
detailsitem .icon-delete {
|
||||
|
@ -393,6 +393,19 @@ li.addressBook-share-item span.shareeIdentifier {
|
|||
opacity: .5;
|
||||
}
|
||||
|
||||
ul.addressBookList li {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
ul.addressBook-share-list {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
ul.addressBook-share-list li {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
#app-navigation .utils .action span {
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
@ -404,14 +417,10 @@ li.addressBook-share-item span.shareeIdentifier {
|
|||
opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* SELECT2 styling - MOVE TO CORE FOR 9.1! */
|
||||
|
||||
detailsitem .select2-container {
|
||||
width: 200px;
|
||||
width: 245px;
|
||||
}
|
||||
.select2-container-multi .select2-choices {
|
||||
border-color: #ddd !important;
|
||||
|
@ -445,3 +454,63 @@ detailsitem .select2-container {
|
|||
.select2-results .select2-result-label span {
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
/* Mobile width < 768px */
|
||||
@media only screen and (max-width: 768px) {
|
||||
|
||||
/* full width for message list on mobile */
|
||||
.app-content-list {
|
||||
width: 100%;
|
||||
background: white;
|
||||
position: relative;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
/* overlay message detail on top of message list */
|
||||
.app-content-detail {
|
||||
background: #fff;
|
||||
width: 100%;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
box-shadow: 0 0 100px rgba(100, 100, 100, .9);
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.wrapper-show:not(.mobile-show),
|
||||
.contacts-list:not(.mobile-show) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
#app-navigation-toggle.showdetails {
|
||||
transform: translate(-50px, 0);
|
||||
}
|
||||
|
||||
#app-navigation-toggle-back {
|
||||
position: fixed;
|
||||
display: inline-block !important;
|
||||
top: 45px;
|
||||
left: 0;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
z-index: 149;
|
||||
background-color: rgba(255, 255, 255, .7);
|
||||
cursor: pointer;
|
||||
opacity: .6;
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
/* end of media query */
|
||||
}
|
||||
|
||||
.contact-details-wrapper {
|
||||
position: relative;
|
||||
background: white;
|
||||
}
|
||||
.wrapper-show {
|
||||
z-index: 101;
|
||||
}
|
||||
#app-navigation-toggle-back {
|
||||
display: none;
|
||||
}
|
|
@ -1,8 +1,19 @@
|
|||
angular.module('contactsApp')
|
||||
.controller('contactdetailsCtrl', function(ContactService, AddressBookService, vCardPropertiesService, $routeParams, $scope) {
|
||||
.controller('contactdetailsCtrl', function(ContactService, AddressBookService, vCardPropertiesService, $route, $routeParams, $scope) {
|
||||
|
||||
var ctrl = this;
|
||||
|
||||
ctrl.loading = true;
|
||||
ctrl.show = false;
|
||||
|
||||
ctrl.clearContact = function() {
|
||||
$route.updateParams({
|
||||
gid: $routeParams.gid,
|
||||
uid: undefined
|
||||
});
|
||||
ctrl.show = false;
|
||||
ctrl.contact = undefined;
|
||||
};
|
||||
|
||||
ctrl.uid = $routeParams.uid;
|
||||
ctrl.t = {
|
||||
|
@ -35,11 +46,16 @@ angular.module('contactsApp')
|
|||
|
||||
ctrl.changeContact = function(uid) {
|
||||
if (typeof uid === 'undefined') {
|
||||
ctrl.show = false;
|
||||
$('#app-navigation-toggle').removeClass('showdetails');
|
||||
return;
|
||||
}
|
||||
ContactService.getById(uid).then(function(contact) {
|
||||
ctrl.contact = contact;
|
||||
ctrl.photo = ctrl.contact.photo();
|
||||
ctrl.show = true;
|
||||
$('#app-navigation-toggle').addClass('showdetails');
|
||||
|
||||
ctrl.addressBook = _.find(ctrl.addressBooks, function(book) {
|
||||
return book.displayName === ctrl.contact.addressBookId;
|
||||
});
|
||||
|
|
|
@ -6,6 +6,7 @@ angular.module('contactsApp')
|
|||
|
||||
ctrl.contactList = [];
|
||||
ctrl.searchTerm = '';
|
||||
ctrl.show = true;
|
||||
|
||||
ctrl.t = {
|
||||
addContact : t('contacts', 'Add contact'),
|
||||
|
@ -69,7 +70,8 @@ angular.module('contactsApp')
|
|||
ContactService.getAll().then(function(contacts) {
|
||||
$scope.$apply(function() {
|
||||
ctrl.contacts = contacts;
|
||||
if (!_.isEmpty(ctrl.contacts)) {
|
||||
// If desktop version, load first contact (see css for min-width media query)
|
||||
if (!_.isEmpty(ctrl.contacts) && $(window).width() > 768) {
|
||||
ctrl.setSelectedId(_.sortBy(contacts, function(contact) {
|
||||
return contact.fullName();
|
||||
})[0].uid());
|
||||
|
@ -78,7 +80,13 @@ angular.module('contactsApp')
|
|||
});
|
||||
});
|
||||
|
||||
$scope.$watch('ctrl.routeParams.uid', function(newValue) {
|
||||
$scope.$watch('ctrl.routeParams.uid', function(newValue, oldValue) {
|
||||
// Used for mobile view to clear the url
|
||||
if(typeof oldValue != 'undefined' && typeof newValue == 'undefined') {
|
||||
// no contact selected
|
||||
ctrl.show = true;
|
||||
return;
|
||||
}
|
||||
if(newValue === undefined) {
|
||||
// we might have to wait until ng-repeat filled the contactList
|
||||
if(ctrl.contactList && ctrl.contactList.length > 0) {
|
||||
|
@ -98,6 +106,9 @@ angular.module('contactsApp')
|
|||
unbindWatch(); // unbind as we only want one update
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// displaying contact details
|
||||
ctrl.show = false;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -106,7 +117,7 @@ angular.module('contactsApp')
|
|||
ctrl.contactList = [];
|
||||
// watch for next contactList update
|
||||
var unbindWatch = $scope.$watch('ctrl.contactList', function() {
|
||||
if(ctrl.contactList && ctrl.contactList.length > 0) {
|
||||
if(ctrl.contactList && ctrl.contactList.length > 0 && $(window).width() > 768) {
|
||||
$route.updateParams({
|
||||
gid: $routeParams.gid,
|
||||
uid: ctrl.contactList[0].uid()
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
<a class="app-content-list-item-link" ng-click="ctrl.openContact()">
|
||||
<img class="app-content-list-item-icon contact__icon" ng-show="ctrl.contact.photo()!==undefined" data-ng-src="data:image/png;base64,{{ctrl.contact.photo()}}" />
|
||||
<div class="app-content-list-item-icon contact__icon" ng-show="ctrl.contact.photo()===undefined" ng-style="{'background-color': (ctrl.contact.uid() | contactColor) }">{{ ctrl.contact.fullName() | firstCharacter }}</div>
|
||||
|
||||
<div class="app-content-list-item-star icon-star" data-starred="false"></div>
|
||||
|
||||
<div class="app-content-list-item-line-one" ng-class="{'no-line-two':!ctrl.contact.email()}">{{ctrl.contact.fullName()}}</div>
|
||||
<div class="app-content-list-item-line-two">{{ctrl.contact.email()}}</div>
|
||||
<img class="app-content-list-item-icon contact__icon" ng-show="ctrl.contact.photo()!==undefined" data-ng-src="data:image/png;base64,{{ctrl.contact.photo()}}" />
|
||||
<div class="app-content-list-item-icon contact__icon" ng-show="ctrl.contact.photo()===undefined" ng-style="{'background-color': (ctrl.contact.uid() | contactColor) }">{{ ctrl.contact.fullName() | firstCharacter }}</div>
|
||||
<div class="app-content-list-item-star icon-star" data-starred="false"></div>
|
||||
<div class="app-content-list-item-line-one" ng-class="{'no-line-two':!ctrl.contact.email()}">{{ctrl.contact.fullName()}}</div>
|
||||
<div class="app-content-list-item-line-two">{{ctrl.contact.email()}}</div>
|
||||
</a>
|
||||
|
|
|
@ -1,35 +1,38 @@
|
|||
<div ng-if="ctrl.contact===undefined && !ctrl.loading">
|
||||
<div id="emptycontent" class="">
|
||||
<div class="icon-contacts-dark"></div>
|
||||
<h2>{{ctrl.t.noContacts}}</h2>
|
||||
<div class="contact-details-wrapper wrapper-show" ng-class="{'mobile-show':ctrl.show}">
|
||||
<div id="app-navigation-toggle-back" class="details-back icon-download" ng-click="ctrl.clearContact()"></div>
|
||||
<div ng-if="ctrl.contact===undefined && !ctrl.loading">
|
||||
<div id="emptycontent" class="">
|
||||
<div class="icon-contacts-dark"></div>
|
||||
<h2>{{ctrl.t.noContacts}}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="ctrl.contact!==undefined">
|
||||
<header class="contactdetails__header" ng-style="{'background-color': (ctrl.contact.uid() | contactColor)}">
|
||||
<img ng-if="ctrl.photo!==undefined" class="contactdetails__logo avatar" data-ng-src="data:image/png;base64,{{ctrl.photo}}" />
|
||||
<h2>
|
||||
<input type="text" id="details-fullName" class="contactdetails__name" placeholder="{{ctrl.t.placeholderName}}" autocomplete="off"
|
||||
name="email" ng-model="ctrl.contact.fullName" ng-model-options="{ getterSetter: true, debounce: 500 }" ng-change="ctrl.updateContact()" value="" />
|
||||
</h2>
|
||||
<div>
|
||||
<input type="text" id="details-org" class="contactdetails__org" placeholder="{{ctrl.t.placeholderOrg}}" autocomplete="off"
|
||||
name="email" ng-model="ctrl.contact.org" ng-model-options="{ getterSetter: true, debounce: 500 }" ng-change="ctrl.updateContact()" value="" />
|
||||
<input type="text" id="details-title" class="contactdetails__title" placeholder="{{ctrl.t.placeholderTitle}}" autocomplete="off"
|
||||
name="email" ng-model="ctrl.contact.title" ng-model-options="{ getterSetter: true, debounce: 500 }" ng-change="ctrl.updateContact()" value="" />
|
||||
</div>
|
||||
<button ng-click="ctrl.deleteContact()" class="icon-delete-white" title="Delete"></button>
|
||||
</header>
|
||||
<section>
|
||||
<div ng-repeat="prop in ctrl.contact.props | toArray | orderDetailItems:'$key'">
|
||||
<detailsItem ng-repeat="propData in prop" name="prop.$key" data="propData" model="ctrl" index="$index" ng-class="[ 'details-item-' + prop.$key ]"></detailsItem>
|
||||
</div>
|
||||
<div class="select-addressbook" ng-if="ctrl.addressBooks.length > 1">
|
||||
<select ng-model="ctrl.addressBook" ng-change="ctrl.changeAddressBook(ctrl.addressBook)" ng-options="book.displayName for book in ctrl.addressBooks">
|
||||
</select>
|
||||
</div>
|
||||
<select class="add-field" ng-model="ctrl.field" ng-change="ctrl.addField(ctrl.field)">
|
||||
<option value=''>{{ctrl.t.selectField}}</option>
|
||||
<option ng-repeat="field in ctrl.fieldDefinitions | fieldFilter: ctrl.contact | orderBy : 'name'" value="{{field.id}}">{{field.name}}</option>
|
||||
</select>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="ctrl.contact!==undefined">
|
||||
<header class="contactdetails__header" ng-style="{'background-color': (ctrl.contact.uid() | contactColor)}">
|
||||
<img ng-if="ctrl.photo!==undefined" class="contactdetails__logo avatar" data-ng-src="data:image/png;base64,{{ctrl.photo}}" />
|
||||
<h2>
|
||||
<input type="text" id="details-fullName" class="contactdetails__name" placeholder="{{ctrl.t.placeholderName}}" autocomplete="off"
|
||||
name="email" ng-model="ctrl.contact.fullName" ng-model-options="{ getterSetter: true, debounce: 500 }" ng-change="ctrl.updateContact()" value="" />
|
||||
</h2>
|
||||
<div>
|
||||
<input type="text" id="details-org" class="contactdetails__org" placeholder="{{ctrl.t.placeholderOrg}}" autocomplete="off"
|
||||
name="email" ng-model="ctrl.contact.org" ng-model-options="{ getterSetter: true, debounce: 500 }" ng-change="ctrl.updateContact()" value="" />
|
||||
<input type="text" id="details-title" class="contactdetails__title" placeholder="{{ctrl.t.placeholderTitle}}" autocomplete="off"
|
||||
name="email" ng-model="ctrl.contact.title" ng-model-options="{ getterSetter: true, debounce: 500 }" ng-change="ctrl.updateContact()" value="" />
|
||||
</div>
|
||||
<button ng-click="ctrl.deleteContact()" class="icon-delete-white" title="Delete"></button>
|
||||
</header>
|
||||
<section>
|
||||
<div ng-repeat="prop in ctrl.contact.props | toArray | orderDetailItems:'$key'" ng-class="prop.length > 1 ? '' : 'last-details'">
|
||||
<detailsItem ng-repeat="propData in prop" name="prop.$key" data="propData" model="ctrl" index="$index" ng-class="[ 'details-item-' + prop.$key ]"></detailsItem>
|
||||
</div>
|
||||
<div class="select-addressbook" ng-if="ctrl.addressBooks.length > 1">
|
||||
<select ng-model="ctrl.addressBook" ng-change="ctrl.changeAddressBook(ctrl.addressBook)" ng-options="book.displayName for book in ctrl.addressBooks">
|
||||
</select>
|
||||
</div>
|
||||
<select class="add-field" ng-model="ctrl.field" ng-change="ctrl.addField(ctrl.field)">
|
||||
<option value=''>{{ctrl.t.selectField}}</option>
|
||||
<option ng-repeat="field in ctrl.fieldDefinitions | fieldFilter: ctrl.contact | orderBy : 'name'" value="{{field.id}}">{{field.name}}</option>
|
||||
</select>
|
||||
</section>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div style="height: 90%" ng-class="{loading: ctrl.loading}">
|
||||
<div style="height: 90%" class="contacts-list" ng-class="{loading: ctrl.loading, 'mobile-show': ctrl.show}">
|
||||
<button ng-show="!ctrl.loading" class="app-content-list-button" type="button" name="button" ng-click="ctrl.createContact()">{{ctrl.t.addContact}}</button>
|
||||
<div class="app-content-list-item"
|
||||
ng-repeat="contact in ctrl.contactList = (ctrl.contacts | contactGroupFilter:ctrl.routeParams.gid | orderBy:'fullName()' | filter:query) track by contact.uid()"
|
||||
|
|
Loading…
Reference in New Issue