(html,js) Reorder and filter calendars list
parent
6cb513f30b
commit
2963654800
1
NEWS
1
NEWS
|
@ -6,6 +6,7 @@ New features
|
||||||
- [core] new user-based rate-limiting support for all SOGo requests (#3188)
|
- [core] new user-based rate-limiting support for all SOGo requests (#3188)
|
||||||
- [web] toolbar of all-day events can be expanded to display all events
|
- [web] toolbar of all-day events can be expanded to display all events
|
||||||
- [web] added AngularJS's XSRF support (#3246)
|
- [web] added AngularJS's XSRF support (#3246)
|
||||||
|
- [web] calendars list can be reordered and filtered
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
- [web] updated Angular Material to version 1.0.6
|
- [web] updated Angular Material to version 1.0.6
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
xmlns:label="OGo:label"
|
xmlns:label="OGo:label"
|
||||||
className="UIxPageFrame"
|
className="UIxPageFrame"
|
||||||
title="title"
|
title="title"
|
||||||
const:jsFiles="Common.js, Preferences.services.js, Contacts.services.js, Mailer.services.js, vendor/angular-file-upload.min.js, Scheduler.js, Scheduler.services.js">
|
const:jsFiles="Common.js, vendor/ng-sortable.js, Preferences.services.js, Contacts.services.js, Mailer.services.js, vendor/angular-file-upload.min.js, Scheduler.js, Scheduler.services.js">
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var firstDayOfWeek = <var:string value="firstDayOfWeek"/>;
|
var firstDayOfWeek = <var:string value="firstDayOfWeek"/>;
|
||||||
var dayStartHour = <var:string value="dayStartHour"/>;
|
var dayStartHour = <var:string value="dayStartHour"/>;
|
||||||
|
@ -37,9 +37,10 @@
|
||||||
ng-class="{ 'sg-close': leftIsClose }">
|
ng-class="{ 'sg-close': leftIsClose }">
|
||||||
<var:component className="UIxSidenavToolbarTemplate" />
|
<var:component className="UIxSidenavToolbarTemplate" />
|
||||||
<md-content class="md-flex" md-scroll-y="md-scroll-y"
|
<md-content class="md-flex" md-scroll-y="md-scroll-y"
|
||||||
|
ng-class="{'sg-list-sortable': !app.sortableCalendars.disabled}"
|
||||||
md-colors="::{ backgroundColor: 'default-background-300' }">
|
md-colors="::{ backgroundColor: 'default-background-300' }">
|
||||||
<!-- User's calendars -->
|
<!-- User's calendars -->
|
||||||
<section class="sg-section-list">
|
<section>
|
||||||
<md-subheader class="sg-md-subheader--icon-button"
|
<md-subheader class="sg-md-subheader--icon-button"
|
||||||
md-colors="::{background: 'default-background-300'}">
|
md-colors="::{background: 'default-background-300'}">
|
||||||
<div layout="row" layout-align="space-between center">
|
<div layout="row" layout-align="space-between center">
|
||||||
|
@ -51,8 +52,8 @@
|
||||||
</md-button>
|
</md-button>
|
||||||
</div>
|
</div>
|
||||||
</md-subheader>
|
</md-subheader>
|
||||||
<md-list>
|
<md-list ng-sortable="app.sortableCalendars">
|
||||||
<md-list-item ng-repeat="calendar in app.service.$calendars"
|
<md-list-item ng-repeat="calendar in app.service.$calendars | filter:app.filter"
|
||||||
ng-dblclick="app.editFolder(calendar)">
|
ng-dblclick="app.editFolder(calendar)">
|
||||||
<md-checkbox ng-model="calendar.active"
|
<md-checkbox ng-model="calendar.active"
|
||||||
ng-class="calendar.getClassName('checkbox')"
|
ng-class="calendar.getClassName('checkbox')"
|
||||||
|
@ -73,7 +74,8 @@
|
||||||
sg-enter="app.renameFolder(calendar)"
|
sg-enter="app.renameFolder(calendar)"
|
||||||
sg-escape="app.revertEditing(calendar)"/>
|
sg-escape="app.revertEditing(calendar)"/>
|
||||||
</md-input-container>
|
</md-input-container>
|
||||||
<md-menu class="md-secondary" label:aria-label="Options">
|
<md-menu class="md-secondary" label:aria-label="Options"
|
||||||
|
md-colors="::{color: 'accent-400'}">
|
||||||
<md-button class="md-icon-button" label:aria-label="Options"
|
<md-button class="md-icon-button" label:aria-label="Options"
|
||||||
ng-click="$mdOpenMenu()"
|
ng-click="$mdOpenMenu()"
|
||||||
md-menu-origin="md-menu-origin">
|
md-menu-origin="md-menu-origin">
|
||||||
|
@ -135,7 +137,7 @@
|
||||||
</md-list>
|
</md-list>
|
||||||
</section>
|
</section>
|
||||||
<!-- Subscriptions -->
|
<!-- Subscriptions -->
|
||||||
<section class="sg-section-list">
|
<section>
|
||||||
<md-subheader class="sg-md-subheader--icon-button"
|
<md-subheader class="sg-md-subheader--icon-button"
|
||||||
md-colors="::{background: 'default-background-300'}">
|
md-colors="::{background: 'default-background-300'}">
|
||||||
<div layout="row" layout-align="space-between center">
|
<div layout="row" layout-align="space-between center">
|
||||||
|
@ -148,8 +150,8 @@
|
||||||
</md-button>
|
</md-button>
|
||||||
</div>
|
</div>
|
||||||
</md-subheader>
|
</md-subheader>
|
||||||
<md-list>
|
<md-list ng-sortable="app.sortableSubscriptions">
|
||||||
<md-list-item ng-repeat="calendar in app.service.$subscriptions"
|
<md-list-item ng-repeat="calendar in app.service.$subscriptions | filter:app.filter"
|
||||||
ng-dblclick="app.editFolder(calendar)">
|
ng-dblclick="app.editFolder(calendar)">
|
||||||
<md-checkbox ng-model="calendar.active"
|
<md-checkbox ng-model="calendar.active"
|
||||||
ng-class="calendar.getClassName('checkbox')"
|
ng-class="calendar.getClassName('checkbox')"
|
||||||
|
@ -170,7 +172,8 @@
|
||||||
sg-enter="app.renameFolder(calendar)"
|
sg-enter="app.renameFolder(calendar)"
|
||||||
sg-escape="app.revertEditing(calendar)"/>
|
sg-escape="app.revertEditing(calendar)"/>
|
||||||
</md-input-container>
|
</md-input-container>
|
||||||
<md-menu class="md-secondary" label:aria-label="Options">
|
<md-menu class="md-secondary" label:aria-label="Options"
|
||||||
|
md-colors="::{color: 'accent-400'}">
|
||||||
<md-button class="md-icon-button" label:aria-label="Options"
|
<md-button class="md-icon-button" label:aria-label="Options"
|
||||||
ng-click="$mdOpenMenu()"
|
ng-click="$mdOpenMenu()"
|
||||||
md-menu-origin="md-menu-origin">
|
md-menu-origin="md-menu-origin">
|
||||||
|
@ -204,7 +207,7 @@
|
||||||
</md-list>
|
</md-list>
|
||||||
</section>
|
</section>
|
||||||
<!-- Web Calendars -->
|
<!-- Web Calendars -->
|
||||||
<section class="sg-section-list">
|
<section>
|
||||||
<md-subheader class="sg-md-subheader--icon-button"
|
<md-subheader class="sg-md-subheader--icon-button"
|
||||||
md-colors="::{background: 'default-background-300'}">
|
md-colors="::{background: 'default-background-300'}">
|
||||||
<div layout="row" layout-align="space-between center">
|
<div layout="row" layout-align="space-between center">
|
||||||
|
@ -216,8 +219,8 @@
|
||||||
</md-button>
|
</md-button>
|
||||||
</div>
|
</div>
|
||||||
</md-subheader>
|
</md-subheader>
|
||||||
<md-list>
|
<md-list ng-sortable="app.sortableWebCalendars">
|
||||||
<md-list-item ng-repeat="calendar in app.service.$webcalendars"
|
<md-list-item ng-repeat="calendar in app.service.$webcalendars | filter:app.filter"
|
||||||
ng-dblclick="app.editFolder(calendar)">
|
ng-dblclick="app.editFolder(calendar)">
|
||||||
<md-checkbox ng-model="calendar.active"
|
<md-checkbox ng-model="calendar.active"
|
||||||
ng-class="calendar.getClassName('checkbox')"
|
ng-class="calendar.getClassName('checkbox')"
|
||||||
|
@ -235,7 +238,8 @@
|
||||||
sg-enter="app.renameFolder(calendar)"
|
sg-enter="app.renameFolder(calendar)"
|
||||||
sg-escape="app.revertEditing(calendar)"/>
|
sg-escape="app.revertEditing(calendar)"/>
|
||||||
</md-input-container>
|
</md-input-container>
|
||||||
<md-menu class="md-secondary" label:aria-label="Options">
|
<md-menu class="md-secondary" label:aria-label="Options"
|
||||||
|
md-colors="::{color: 'accent-400'}">
|
||||||
<md-button class="md-icon-button" label:aria-label="Options"
|
<md-button class="md-icon-button" label:aria-label="Options"
|
||||||
ng-click="$mdOpenMenu()"
|
ng-click="$mdOpenMenu()"
|
||||||
md-menu-origin="md-menu-origin">
|
md-menu-origin="md-menu-origin">
|
||||||
|
@ -269,6 +273,27 @@
|
||||||
</md-list>
|
</md-list>
|
||||||
</section>
|
</section>
|
||||||
</md-content>
|
</md-content>
|
||||||
|
<md-toolbar class="sg-toolbar-multiple">
|
||||||
|
<div class="md-toolbar-tools sg-toolbar-tools--dense"
|
||||||
|
md-colors="::{backgroundColor: 'background-500'}"
|
||||||
|
ng-show="app.sortableCalendars.disabled">
|
||||||
|
<md-input-container class="md-flex" md-no-float="md-no-float">
|
||||||
|
<md-icon>search</md-icon>
|
||||||
|
<input ng-model="app.filter.name" type="search" label:placeholder="Filter"/>
|
||||||
|
</md-input-container>
|
||||||
|
<md-button class="md-icon-button"
|
||||||
|
ng-click="app.toggleSortableMode()">
|
||||||
|
<md-icon>reorder</md-icon>
|
||||||
|
</md-button>
|
||||||
|
</div>
|
||||||
|
<div class="md-toolbar-tools sg-toolbar-tools--dense ng-hide" layout-align="center center"
|
||||||
|
md-colors="::{backgroundColor: 'accent-600'}"
|
||||||
|
ng-hide="app.sortableCalendars.disabled">
|
||||||
|
<md-button ng-click="app.resetSort()"><var:string label:value="Reset"/></md-button>
|
||||||
|
<div class="md-flex"><!-- spacer --></div>
|
||||||
|
<md-button ng-click="app.toggleSortableMode()"><var:string label:value="Done"/></md-button>
|
||||||
|
</div>
|
||||||
|
</md-toolbar>
|
||||||
</md-sidenav>
|
</md-sidenav>
|
||||||
|
|
||||||
<!-- Main section -->
|
<!-- Main section -->
|
||||||
|
|
|
@ -99,6 +99,12 @@
|
||||||
});
|
});
|
||||||
i = sibling ? _.indexOf(_.map(list, 'id'), sibling.id) : 1;
|
i = sibling ? _.indexOf(_.map(list, 'id'), sibling.id) : 1;
|
||||||
list.splice(i, 0, calendar);
|
list.splice(i, 0, calendar);
|
||||||
|
|
||||||
|
this.$Preferences.ready().then(function() {
|
||||||
|
if (Calendar.$Preferences.settings.Calendar.FoldersOrder)
|
||||||
|
// Save list order
|
||||||
|
Calendar.saveFoldersOrder(_.flatMap(Calendar.$findAll(), 'id'));
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -129,8 +135,8 @@
|
||||||
this.$calendars = [];
|
this.$calendars = [];
|
||||||
this.$subscriptions = [];
|
this.$subscriptions = [];
|
||||||
this.$webcalendars = [];
|
this.$webcalendars = [];
|
||||||
Calendar.$$resource.fetch('calendarslist').then(function(data) {
|
return Calendar.$$resource.fetch('calendarslist').then(function(data) {
|
||||||
Calendar.$findAll(data.calendars, writable);
|
return Calendar.$findAll(data.calendars, writable);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,6 +267,23 @@
|
||||||
return Calendar.$q.all(promises);
|
return Calendar.$q.all(promises);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @function saveFoldersOrder
|
||||||
|
* @desc Save to the user's settings the current calendars order.
|
||||||
|
* @param {string[]} folders - the folders IDs
|
||||||
|
* @returns a promise of the HTTP operation
|
||||||
|
*/
|
||||||
|
Calendar.saveFoldersOrder = function(folders) {
|
||||||
|
return this.$$resource.post(null, 'saveFoldersOrder', { folders: folders }).then(function() {
|
||||||
|
Calendar.$Preferences.settings.Calendar.FoldersOrder = folders;
|
||||||
|
if (!folders)
|
||||||
|
// Calendars order was reset; reload list
|
||||||
|
return Calendar.$$resource.fetch('calendarslist').then(function(data) {
|
||||||
|
return Calendar.$findAll(data.calendars);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @function init
|
* @function init
|
||||||
* @memberof Calendar.prototype
|
* @memberof Calendar.prototype
|
||||||
|
|
|
@ -28,6 +28,22 @@
|
||||||
vm.subscribeToFolder = subscribeToFolder;
|
vm.subscribeToFolder = subscribeToFolder;
|
||||||
vm.today = today;
|
vm.today = today;
|
||||||
|
|
||||||
|
vm.filter = { name: '' };
|
||||||
|
vm.toggleSortableMode = toggleSortableMode;
|
||||||
|
vm.resetSort = resetSort;
|
||||||
|
vm.sortableCalendars = {
|
||||||
|
disabled: true,
|
||||||
|
animation: 150,
|
||||||
|
draggable: 'md-list-item',
|
||||||
|
handle: '.md-menu',
|
||||||
|
ghostClass: 'sg-sortable-ghost',
|
||||||
|
chosenClass: 'sg-sortable-chosen',
|
||||||
|
setData: sortable_setData,
|
||||||
|
onEnd: sortable_onEnd
|
||||||
|
};
|
||||||
|
vm.sortableSubscriptions = angular.copy(vm.sortableCalendars);
|
||||||
|
vm.sortableWebCalendars = angular.copy(vm.sortableCalendars);
|
||||||
|
|
||||||
Preferences.ready().then(function() {
|
Preferences.ready().then(function() {
|
||||||
vm.categories = _.map(Preferences.defaults.SOGoCalendarCategories, function(name) {
|
vm.categories = _.map(Preferences.defaults.SOGoCalendarCategories, function(name) {
|
||||||
return { id: name.asCSSIdentifier(),
|
return { id: name.asCSSIdentifier(),
|
||||||
|
@ -65,7 +81,7 @@
|
||||||
promises.push(calendar.$setActivation());
|
promises.push(calendar.$setActivation());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (commonList.length > 0)
|
if (promises.length > 0 || commonList.length != newList.length || commonList.length != oldList.length)
|
||||||
Calendar.$q.all(promises).then(function() {
|
Calendar.$q.all(promises).then(function() {
|
||||||
$rootScope.$emit('calendars:list');
|
$rootScope.$emit('calendars:list');
|
||||||
});
|
});
|
||||||
|
@ -73,6 +89,25 @@
|
||||||
true // compare for object equality
|
true // compare for object equality
|
||||||
);
|
);
|
||||||
|
|
||||||
|
function sortable_setData(dataTransfer, dragEl) {
|
||||||
|
dataTransfer.clearData();
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortable_onEnd() {
|
||||||
|
Calendar.saveFoldersOrder(_.flatMap(Calendar.$findAll(), 'id'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleSortableMode() {
|
||||||
|
vm.sortableCalendars.disabled = !vm.sortableCalendars.disabled;
|
||||||
|
vm.sortableSubscriptions.disabled = !vm.sortableSubscriptions.disabled;
|
||||||
|
vm.sortableWebCalendars.disabled = !vm.sortableWebCalendars.disabled;
|
||||||
|
vm.filter.name = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetSort() {
|
||||||
|
Calendar.saveFoldersOrder();
|
||||||
|
}
|
||||||
|
|
||||||
function newCalendar(ev) {
|
function newCalendar(ev) {
|
||||||
Dialog.prompt(l('New calendar'), l('Name of the Calendar'))
|
Dialog.prompt(l('New calendar'), l('Name of the Calendar'))
|
||||||
.then(function(name) {
|
.then(function(name) {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
(function() {
|
(function() {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
angular.module('SOGo.SchedulerUI', ['ui.router', 'angularFileUpload', 'SOGo.Common', 'SOGo.PreferencesUI', 'SOGo.ContactsUI', 'SOGo.MailerUI'])
|
angular.module('SOGo.SchedulerUI', ['ui.router', 'angularFileUpload', 'SOGo.Common', 'SOGo.PreferencesUI', 'SOGo.ContactsUI', 'SOGo.MailerUI', 'ng-sortable'])
|
||||||
.config(configure)
|
.config(configure)
|
||||||
.run(runBlock);
|
.run(runBlock);
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,7 @@ md-list-item {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove padding of input fields in the sidenav for better transitions between read and edit mode of a folder
|
// Remove padding of input fields in the sidenav for better transitions between read and edit mode of a folder
|
||||||
md-input-container {
|
md-input-container:not(.md-icon-left) {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
.md-input {
|
.md-input {
|
||||||
|
@ -231,12 +231,25 @@ div.md-tile-left {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&-sortable-chosen {
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
&-sortable-ghost {
|
&-sortable-ghost {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-sortable-chosen {
|
&-list-sortable {
|
||||||
background-color: white;
|
._md-secondary-container > .md-menu {
|
||||||
|
.md-button {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
&:before {
|
||||||
|
@extend .material-icons;
|
||||||
|
content: "\e8fe";
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -112,6 +112,23 @@ hgroup {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sg-toolbar-search {
|
// Animate transitions from one toolbar to the other
|
||||||
padding: $toolbar-padding 0;
|
.sg-toolbar-multiple {
|
||||||
|
overflow: hidden;
|
||||||
|
.md-toolbar-tools {
|
||||||
|
&.ng-hide {
|
||||||
|
transform: translateY(100%);
|
||||||
|
transition: transform 0ms;
|
||||||
|
}
|
||||||
|
transform: translateY(0%);
|
||||||
|
transition: transform 240ms;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sg-toolbar-tools--dense {
|
||||||
|
height: $bl * 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
//.sg-toolbar-search {
|
||||||
|
// padding: $toolbar-padding 0;
|
||||||
|
//}
|
||||||
|
|
Loading…
Reference in New Issue