(js) Improve search toolbar in Calendar module

- new distinct search toolbar
- restored filtering options
- honor user's settings
This commit is contained in:
Francis Lachapelle 2015-07-08 14:46:07 -04:00
parent 4d4458a183
commit 9514103031
7 changed files with 175 additions and 42 deletions

View file

@ -8,9 +8,9 @@
xmlns:label="OGo:label" xmlns:label="OGo:label"
className="UIxPageFrame" className="UIxPageFrame"
title="title" title="title"
const:userDefaultsKeys="SOGoRefreshViewCheck, SOGoCalendarCategoriesColors, SOGoDefaultCalendar" const:userDefaultsKeys="SOGoRefreshViewCheck, SOGoCalendarCategoriesColors, SOGoDefaultCalendar, SOGoTimeFormat"
const:userSettingsKeys="Calendar, ShowCompletedTasks" const:userSettingsKeys="Calendar, ShowCompletedTasks"
const:jsFiles="Scheduler.app.js, Scheduler.js, Common.js, Contacts.js"> const:jsFiles="Scheduler.app.js, Scheduler.js, Common.js, Contacts.js, Mailer.js, Preferences.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"/>;
@ -314,7 +314,7 @@
ng-controller="CalendarListController as list"> ng-controller="CalendarListController as list">
<md-toolbar layout="column" layout-align="space-between start" class="md-tall toolbar-main"> <md-toolbar layout="column" layout-align="space-between start" class="md-tall toolbar-main">
<div class="md-toolbar-tools md-toolbar-tools-top sg-padded" layout="row" layout-align="space-between start"> <div class="md-toolbar-tools md-toolbar-tools-top sg-padded" layout="row" layout-align="space-between start">
<var:component className="UIxTopnavToolbarTemplate" /> <!-- <var:component className="UIxTopnavToolbarTemplate" /> -->
<div class="sg-toolbar-group-2"> <div class="sg-toolbar-group-2">
<md-button class="sg-icon-button" label:aria-label="Search"> <md-button class="sg-icon-button" label:aria-label="Search">
<md-icon>search</md-icon> <md-icon>search</md-icon>
@ -322,19 +322,103 @@
</div> </div>
</div><!-- .md-toolbar-tools --> </div><!-- .md-toolbar-tools -->
<div class="md-toolbar-tools md-toolbar-tools-bottom" layout="row" layout-align="space-between center"> <div class="md-toolbar-tools md-toolbar-tools-bottom" layout="row" layout-align="space-between center">
<div class="view-list sg-padded" layout="row" layout-align="space-between center" <!-- filter mode -->
sg-search="list.component.$filter(list.componentType, { value: searchText, search: searchField })"> <div class="view-list sg-padded--right" layout="row" layout-align="space-between center"
<md-input-container class="sg-search-field-container"> ng-hide="list.mode.search">
<label style="color: white"><md-icon>search</md-icon><var:string label:value="Search"/></label>
<input name="folderSearch" type="search" style="color: white"/>
</md-input-container>
<div class="sg-toolbar-group"> <div class="sg-toolbar-group">
<md-select> <md-button class="sg-icon-button" label:aria-label="Search"
<md-option value="title_Category_Location"><var:string label:value="Title, category or location"/></md-option> ng-click="list.mode.search = true">
<md-option value="entireContent"><var:string label:value="Entire content"/></md-option> <md-icon>search</md-icon>
</md-select> </md-button>
</div>
<div class="sg-toolbar-group-last">
<md-menu>
<md-button class="sg-icon-button" label:aria-label="Search"
ng-click="$mdOpenMenu()">
<md-icon>filter_list</md-icon>
</md-button>
<md-menu-content>
<md-menu-item>
<md-button ng-click="list.filter('view_all')">
<md-icon ng-class="{ 'icon-check': list.component.$query.filterpopup == 'view_all' }">
<!-- selected --></md-icon> <var:string label:value="view_all"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="list.filter('view_today')">
<md-icon ng-class="{ 'icon-check': list.component.$query.filterpopup == 'view_today' }">
<!-- selected --></md-icon> <var:string label:value="view_today"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="list.filter('view_next7')">
<md-icon ng-class="{ 'icon-check': list.component.$query.filterpopup == 'view_next7' }">
<!-- selected --></md-icon> <var:string label:value="view_next7"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="list.filter('view_next14')">
<md-icon ng-class="{ 'icon-check': list.component.$query.filterpopup == 'view_next14' }">
<!-- selected --></md-icon> <var:string label:value="view_next14"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="list.filter('view_next31')">
<md-icon ng-class="{ 'icon-check': list.component.$query.filterpopup == 'view_next31' }">
<!-- selected --></md-icon><var:string label:value="view_next31"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="list.filter('view_thismonth')">
<md-icon ng-class="{ 'icon-check': list.component.$query.filterpopup == 'view_thismonth' }">
<!-- selected --></md-icon><var:string label:value="view_thismonth"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="list.filter('view_future')">
<md-icon ng-class="{ 'icon-check': list.component.$query.filterpopup == 'view_future' }">
<!-- selected --></md-icon> <var:string label:value="view_future"/>
</md-button>
</md-menu-item>
<md-menu-item>
<md-button ng-click="list.filter('view_selectedday')">
<md-icon ng-class="{ 'icon-check': list.component.$query.filterpopup == 'view_selectedday' }">
<!-- selected --></md-icon><var:string label:value="view_selectedday"/>
</md-button>
</md-menu-item>
<md-menu-divider ng-if="list.componentType == 'tasks'"><!-- divider --></md-menu-divider>
<md-menu-item ng-if="list.componentType == 'tasks'">
<md-button ng-click="list.filter()">
<md-checkbox
ng-model="list.component.$query['show-completed']"
ng-true-value="1"
ng-false-value="0"><var:string label:value="Show completed tasks"/></md-checkbox>
</md-button>
</md-menu-item>
</md-menu-content>
</md-menu>
</div> </div>
</div> </div>
<!-- search mode -->
<div class="view-list sg-padded--right sg-toolbar-search" layout="row" layout-align="space-between center"
ng-show="list.mode.search"
sg-search="list.component.$filter(list.componentType, { value: searchText, search: searchField })">
<md-button class="sg-icon-button"
sg-search-cancel="list.cancelSearch()"
label:aria-label="Back">
<md-icon>arrow_back</md-icon>
</md-button>
<md-input-container>
<input name="folderSearch" type="search" />
</md-input-container>
<md-input-container>
<md-select>
<md-option value="title_Category_Location" selected="selected"><var:string label:value="Title, category or location"/></md-option>
<md-option value="entireContent"><var:string label:value="Entire content"/></md-option>
</md-select>
</md-input-container>
</div>
<!-- day/week/month views -->
<div class="sg-padded"> <div class="sg-padded">
<a class="sg-icon-button md-button" <a class="sg-icon-button md-button"
label:aria-label="Day" label:aria-label="Day"

View file

@ -5,8 +5,10 @@
'use strict'; 'use strict';
angular.module('SOGo.ContactsUI', []); angular.module('SOGo.ContactsUI', []);
angular.module('SOGo.MailerUI', []);
angular.module('SOGo.PreferencesUI', []);
angular.module('SOGo.SchedulerUI', ['ngSanitize', 'ui.router', 'ct.ui.router.extras.sticky', 'ct.ui.router.extras.previous', 'vs-repeat', 'SOGo.Common', 'SOGo.ContactsUI']) angular.module('SOGo.SchedulerUI', ['ngSanitize', 'ui.router', 'ct.ui.router.extras.sticky', 'ct.ui.router.extras.previous', 'vs-repeat', 'SOGo.Common', 'SOGo.ContactsUI', 'SOGo.MailerUI', 'SOGo.PreferencesUI'])
.constant('sgSettings', { .constant('sgSettings', {
baseURL: ApplicationBaseURL, baseURL: ApplicationBaseURL,

View file

@ -6,18 +6,28 @@
/** /**
* @ngInject * @ngInject
*/ */
CalendarListController.$inject = ['$scope', '$rootScope', '$timeout', '$state', 'sgFocus', 'encodeUriFilter', 'Dialog', 'sgSettings', 'Calendar', 'Component', '$mdSidenav']; CalendarListController.$inject = ['$scope', '$rootScope', '$timeout', '$state', 'sgFocus', 'encodeUriFilter', 'Dialog', 'sgSettings', 'Preferences', 'Calendar', 'Component', '$mdSidenav'];
function CalendarListController($scope, $rootScope, $timeout, $state, focus, encodeUriFilter, Dialog, Settings, Calendar, Component, $mdSidenav) { function CalendarListController($scope, $rootScope, $timeout, $state, focus, encodeUriFilter, Dialog, Settings, Preferences, Calendar, Component, $mdSidenav) {
var vm = this; var vm = this;
vm.component = Component; vm.component = Component;
vm.componentType = null; vm.componentType = 'events';
vm.selectedList = 0;
vm.selectComponentType = selectComponentType; vm.selectComponentType = selectComponentType;
vm.newComponent = newComponent; vm.newComponent = newComponent;
// TODO: should reflect last state userSettings -> Calendar -> SelectedList vm.filter = filter;
vm.selectedList = 0; vm.cancelSearch = cancelSearch;
vm.selectComponentType('tasks'); vm.mode = { search: false };
vm.selectComponentType('events');
// Select list based on user's settings
Preferences.ready().then(function() {
var type = 'events';
if (Preferences.settings.Calendar.SelectedList == 'tasksListView') {
vm.selectedList = 1;
type = 'tasks';
}
vm.selectComponentType(type, { reload: true });
});
// Switch between components tabs // Switch between components tabs
function selectComponentType(type, options) { function selectComponentType(type, options) {
@ -38,6 +48,18 @@
$state.go('calendars.newComponent', { calendarId: 'personal', componentType: type }); $state.go('calendars.newComponent', { calendarId: 'personal', componentType: type });
} }
function filter(filterpopup) {
if (filterpopup)
Component.$query.filterpopup = filterpopup;
Component.$filter(vm.componentType, { value: '' });
}
function cancelSearch() {
vm.mode.search = false;
filter();
}
// Refresh current list when the list of calendars is modified // Refresh current list when the list of calendars is modified
$scope.$on('calendars:list', function() { $scope.$on('calendars:list', function() {
Component.$filter(vm.componentType); Component.$filter(vm.componentType);

View file

@ -31,15 +31,20 @@
* @desc The factory we'll use to register with Angular * @desc The factory we'll use to register with Angular
* @returns the Component constructor * @returns the Component constructor
*/ */
Component.$factory = ['$q', '$timeout', '$log', 'sgSettings', 'Resource', function($q, $timeout, $log, Settings, Resource) { Component.$factory = ['$q', '$timeout', '$log', 'sgSettings', 'Preferences', 'Resource', function($q, $timeout, $log, Settings, Preferences, Resource) {
angular.extend(Component, { angular.extend(Component, {
$q: $q, $q: $q,
$timeout: $timeout, $timeout: $timeout,
$log: $log, $log: $log,
$Preferences: Preferences,
$$resource: new Resource(Settings.baseURL, Settings.activeUser), $$resource: new Resource(Settings.baseURL, Settings.activeUser),
$categories: window.UserDefaults.SOGoCalendarCategoriesColors $categories: window.UserDefaults.SOGoCalendarCategoriesColors,
$query: { search: 'title_Category_Location' }
});
Preferences.ready().then(function() {
Component.$query.filterpopup = Preferences.settings.CalendarDefaultFilter;
Component.$query['show-completed'] = parseInt(Preferences.settings.ShowCompletedTasks);
}); });
if (window.UserDefaults && window.UserDefaults.SOGoTimeFormat) if (window.UserDefaults && window.UserDefaults.SOGoTimeFormat)
Component.timeFormat = window.UserDefaults.SOGoTimeFormat; Component.timeFormat = window.UserDefaults.SOGoTimeFormat;
else else
@ -70,19 +75,32 @@
month = now.getMonth() + 1, month = now.getMonth() + 1,
year = now.getFullYear(), year = now.getFullYear(),
defaultParams = { defaultParams = {
search: 'title_Category_Location',
day: '' + year + (month < 10?'0':'') + month + (day < 10?'0':'') + day, day: '' + year + (month < 10?'0':'') + month + (day < 10?'0':'') + day,
filterpopup: 'view_thismonth'
}; };
if (angular.isUndefined(this.$filterOptions)) return this.$Preferences.ready().then(function() {
this.$filterOptions = defaultParams; var futureComponentData, dirty = false, otherType;
if (options)
angular.extend(this.$filterOptions, options);
var futureComponentData = this.$$resource.fetch(null, type + 'list', this.$filterOptions); angular.extend(_this.$query, defaultParams);
if (options) {
_.find(_.allKeys(options), function(key) {
dirty = (options[key] != Component.$query[key]);
return dirty;
})
angular.extend(_this.$query, options);
}
return this.$unwrapCollection(type, futureComponentData); futureComponentData = _this.$$resource.fetch(null, type + 'list', _this.$query);
// Invalidate cached results of other type if $query has changed
otherType = (type == 'tasks')? '$events' : '$tasks';
if (dirty) {
delete Component[otherType];
Component.$log.debug('force reload of ' + otherType);
}
return _this.$unwrapCollection(type, futureComponentData);
});
}; };
/** /**
@ -206,11 +224,10 @@
*/ */
Component.$unwrapCollection = function(type, futureComponentData) { Component.$unwrapCollection = function(type, futureComponentData) {
var _this = this, var _this = this,
deferred = Component.$q.defer(),
components = []; components = [];
futureComponentData.then(function(data) { return futureComponentData.then(function(data) {
Component.$timeout(function() { return Component.$timeout(function() {
var fields = _.invoke(data.fields, 'toLowerCase'); var fields = _.invoke(data.fields, 'toLowerCase');
// Instanciate Component objects // Instanciate Component objects
@ -225,13 +242,9 @@
// Save the list of components to the object model // Save the list of components to the object model
Component['$' + type] = components; Component['$' + type] = components;
deferred.resolve(components); return components;
}); });
}, function(data) {
deferred.reject();
}); });
return deferred.promise;
}; };
/** /**

View file

@ -49,6 +49,9 @@ md-icon {
// Define CSS styles to use ng-class with md-icon // Define CSS styles to use ng-class with md-icon
md-icon { md-icon {
&.icon-check:before {
content: "\e5ca";
}
// &.icon_public:before { // &.icon_public:before {
// content: "\e80b"; // content: "\e80b";
// } // }

View file

@ -64,7 +64,7 @@ hgroup {
.sg-toolbar-group { .sg-toolbar-group {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: flex-end; align-items: center;
justify-content: space-between; justify-content: space-between;
&-1 { &-1 {
order: 1; order: 1;
@ -79,4 +79,10 @@ hgroup {
} }
} }
.sg-toolbar-search {
background-color: sg-color($sogoPaper, 100);
color: rgba(0,0,0,0.54);
md-icon {
color: rgba(0,0,0,0.54);
}
}

View file

@ -11,6 +11,9 @@ main {
.sg-padded { .sg-padded {
padding-left: $mg; padding-left: $mg;
padding-right: $mg; padding-right: $mg;
&--right {
padding-right: $mg;
}
} }
.sg-logo { .sg-logo {