Add colorpicker to change calendar color, fixes #317"

This commit is contained in:
Raimund Schlüßler 2016-08-07 00:23:15 +02:00
Родитель 0ff41bc7e9
Коммит 400d0af4b7
7 изменённых файлов: 433 добавлений и 30 удалений

Просмотреть файл

@ -67,6 +67,7 @@
}
#app-navigation li.edit .app-navigation-entry-edit {
display: inline-block;
height: auto;
}
#app-navigation li .app-navigation-entry-edit {
display: none;
@ -1488,3 +1489,57 @@ ol[dnd-list] .dndPlaceholder {
.dropzone-visible > ol[dnd-list] {
min-height: 50px;
}
/* ColorPicker overrides. */
#app-navigation > ul ul.colorpicker-list {
display: inline-flex;
}
.colorpicker {
display: block;
height: 30px;
height: auto;
padding-bottom: 3px;
padding-top: 3px;
}
.colorpicker .colorpicker-list {
display: flex;
flex-wrap: wrap;
width: 100%;
height: 100%;
text-align: center;
justify-content: center;
align-items: center;
}
.colorpicker .colorpicker-list li {
height: 24px;
width: 24px !important;
}
.colorpicker .colorpicker-list li.selected {
border: 1px solid #333;
}
.colorpicker .colorpicker-list li.randomcolour {
background-image: url('../../img/random.svg');
background-repeat: no-repeat;
background-position: center center;
}
#app-navigation .app-navigation-entry-edit input.icon-close {
width: 36px;
height: 38px;
float: left;
margin: 3px 0px;
border-left: 0px none;
background-color: rgba(240, 240, 240, 0.9);
}
#app-navigation .app-navigation-entry-edit input.icon-close:hover {
background-color: white;
}
#app-navigation .app-navigation-entry-edit input[type="text"] {
width: calc(100% - 72px);
}

Просмотреть файл

@ -20,10 +20,10 @@
*/
angular.module('Tasks').controller('AppController', [
'$scope', 'ListsBusinessLayer', '$route', 'Status', '$timeout', '$location', '$routeParams', 'Loading', 'SettingsModel', 'Persistence', function($scope, ListsBusinessLayer, $route, status, $timeout, $location, $routeParams, Loading, SettingsModel, Persistence) {
'$scope', '$rootScope', 'ListsBusinessLayer', '$route', 'Status', '$timeout', '$location', '$routeParams', 'Loading', 'SettingsModel', 'Persistence', function($scope, $rootScope, ListsBusinessLayer, $route, status, $timeout, $location, $routeParams, Loading, SettingsModel, Persistence) {
'use strict';
var AppController = (function() {
function AppController(_$scope, _$listsbusinesslayer, _$route, _$status, _$timeout, _$location, _$routeparams, _Loading, _$settingsmodel, _persistence) {
function AppController(_$scope, $rootScope, _$listsbusinesslayer, _$route, _$status, _$timeout, _$location, _$routeparams, _Loading, _$settingsmodel, _persistence) {
this._$scope = _$scope;
this._$listsbusinesslayer = _$listsbusinesslayer;
this._$route = _$route;
@ -50,6 +50,9 @@ angular.module('Tasks').controller('AppController', [
this._$scope.closeAll = function($event) {
if ($($event.target).closest('.close-all').length || $($event.currentTarget).is($($event.target).closest('.handler'))) {
if (!angular.isUndefined(_$scope.route.calendarID)) {
if (_$scope.route.listparameter == 'name') {
$rootScope.$broadcast('cancelEditCalendar', _$scope.route.calendarID);
}
_$location.path('/calendars/' + _$scope.route.calendarID);
} else if (!angular.isUndefined(_$scope.route.collectionID)) {
_$location.path('/collections/' + _$scope.route.collectionID);
@ -75,6 +78,6 @@ angular.module('Tasks').controller('AppController', [
}
return AppController;
})();
return new AppController($scope, ListsBusinessLayer, $route, status, $timeout, $location, $routeParams, Loading, SettingsModel, Persistence);
return new AppController($scope, $rootScope, ListsBusinessLayer, $route, status, $timeout, $location, $routeParams, Loading, SettingsModel, Persistence);
}
]);

Просмотреть файл

@ -20,16 +20,16 @@
*/
angular.module('Tasks').controller('ListController', [
'$scope', '$window', '$routeParams', 'ListsModel', 'TasksBusinessLayer', 'CollectionsModel', 'ListsBusinessLayer', '$location',
'$scope', '$rootScope', '$window', '$routeParams', 'ListsModel', 'TasksBusinessLayer', 'CollectionsModel', 'ListsBusinessLayer', '$location',
'SearchBusinessLayer', 'CalendarService', 'TasksModel',
function($scope, $window, $routeParams, ListsModel, TasksBusinessLayer, CollectionsModel, ListsBusinessLayer, $location,
function($scope, $rootScope, $window, $routeParams, ListsModel, TasksBusinessLayer, CollectionsModel, ListsBusinessLayer, $location,
SearchBusinessLayer, CalendarService, TasksModel) {
'use strict';
var ListController;
ListController = (function() {
function ListController(_$scope, _$window, _$routeParams, _$listsmodel, _$tasksbusinesslayer, _$collectionsmodel, _$listsbusinesslayer, $location,
function ListController(_$scope, $rootScope, _$window, _$routeParams, _$listsmodel, _$tasksbusinesslayer, _$collectionsmodel, _$listsbusinesslayer, $location,
_$searchbusinesslayer, _$calendarservice, _$tasksmodel) {
this._$scope = _$scope;
this._$window = _$window;
this._$routeParams = _$routeParams;
@ -86,21 +86,30 @@ angular.module('Tasks').controller('ListController', [
}
};
this._$scope.startRename = function(calendar) {
this._$scope.startEdit = function(calendar) {
_$scope.status.addingList = false;
calendar.prepareUpdate();
return $location.path('/calendars/' + _$scope.route.calendarID + '/edit/name');
};
this._$scope.cancelRename = function(event,calendar) {
this._$scope.checkKey = function(event,calendar) {
if (event.keyCode === 27) {
event.preventDefault();
calendar.resetToPreviousState();
$location.path('/calendars/' + _$scope.route.calendarID);
_$scope.cancelEdit(calendar);
}
};
this._$scope.rename = function(calendar) {
$rootScope.$on('cancelEditCalendar', function(s, calendarUri) {
var calendar = _$listsmodel.getByUri(calendarUri);
_$scope.cancelEdit(calendar);
});
this._$scope.cancelEdit = function(calendar) {
calendar.resetToPreviousState();
$location.path('/calendars/' + _$scope.route.calendarID);
};
this._$scope.saveEdit = function(calendar) {
var name = calendar.displayname;
if (name) {
if (!_$listsmodel.isNameAlreadyTaken(calendar.displayname, calendar.uri)) {
@ -189,7 +198,7 @@ angular.module('Tasks').controller('ListController', [
return ListController;
})();
return new ListController($scope, $window, $routeParams, ListsModel, TasksBusinessLayer, CollectionsModel, ListsBusinessLayer, $location,
return new ListController($scope, $rootScope, $window, $routeParams, ListsModel, TasksBusinessLayer, CollectionsModel, ListsBusinessLayer, $location,
SearchBusinessLayer, CalendarService, TasksModel);
}
]);

Просмотреть файл

@ -0,0 +1,167 @@
/**
* ownCloud - Tasks App
*
* @author Raghu Nayyar
* @author Georg Ehrke
* @copyright 2016 Raghu Nayyar <beingminimal@gmail.com>
* @copyright 2016 Georg Ehrke <oc.list@georgehrke.com>
* @copyright 2016 John Molakvoæ <fremulon@protonmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
/* https://github.com/kayellpeee/hsl_rgb_converter
* expected hue range: [0, 360)
* expected saturation range: [0, 1]
* expected lightness range: [0, 1]
*/
var hslToRgb = function(hue, saturation, lightness) {
'use strict';
// based on algorithm from http://en.wikipedia.org/wiki/HSL_and_HSV#Converting_to_RGB
if(Array.isArray(hue)) {
saturation = hue[1];
lightness = hue[2];
hue = hue[0];
}
if (hue === undefined) {
return [0, 0, 0];
}
saturation /= 100;
lightness /= 100;
var chroma = (1 - Math.abs((2 * lightness) - 1)) * saturation;
var huePrime = hue / 60;
var secondComponent = chroma * (1 - Math.abs((huePrime % 2) - 1));
huePrime = Math.floor(huePrime);
var red;
var green;
var blue;
if (huePrime === 0) {
red = chroma;
green = secondComponent;
blue = 0;
} else if (huePrime === 1) {
red = secondComponent;
green = chroma;
blue = 0;
} else if (huePrime === 2) {
red = 0;
green = chroma;
blue = secondComponent;
} else if (huePrime === 3) {
red = 0;
green = secondComponent;
blue = chroma;
} else if (huePrime === 4) {
red = secondComponent;
green = 0;
blue = chroma;
} else if (huePrime === 5) {
red = chroma;
green = 0;
blue = secondComponent;
}
var lightnessAdjustment = lightness - (chroma / 2);
red += lightnessAdjustment;
green += lightnessAdjustment;
blue += lightnessAdjustment;
return [Math.round(red * 255), Math.round(green * 255), Math.round(blue * 255)];
};
/*
* Convert rgb array to hex string
*/
var rgbToHex = function(r, g, b) {
'use strict';
if(Array.isArray(r)) {
g = r[1];
b = r[2];
r = r[0];
}
return '#' + parseInt(r, 10).toString(16) + parseInt(g, 10).toString(16) + parseInt(b, 10).toString(16);
};
var listofcolours = [
'#31CC7C',
'#317CCC',
'#FF7A66',
'#F1DB50',
'#7C31CC',
'#CC317C',
'#3A3B3D',
'#CACBCD'
];
/*
* Generate a random colour with the core generator
*/
var randColour = function() {
'use strict';
if (typeof String.prototype.toHsl === 'function') {
return rgbToHex(hslToRgb(Math.random().toString().toHsl()));
} else {
return listofcolours[Math.floor(Math.random() * listofcolours.length)];
}
};
/**
* Directive: Colorpicker
* Description: Colorpicker for the Tasks app.
*/
angular.module('Tasks').directive('colorpicker', function() {
'use strict';
if (typeof String.prototype.toHsl === 'function') {
var hsl = "";
var hslcolour = "";
// 0 40 80 120 160 200 240 280 320
listofcolours = ["15", "9", "4", "b", "6", "11", "74", "f", "57"];
listofcolours.forEach(function(hash, index) {
hsl = hash.toHsl();
hslcolour = hslToRgb(hsl);
listofcolours[index] = rgbToHex(hslcolour);
});
}
return {
scope: {
selected: '=',
customizedColors: '=colors'
},
restrict: 'AE',
templateUrl: OC.filePath('tasks', 'templates', 'colorpicker.html'),
link: function(scope, element, attr) {
scope.colors = scope.customizedColors || listofcolours;
scope.selected = scope.selected || scope.colors[0];
scope.random = "#000000";
scope.randomizeColour = function() {
scope.random = randColour();
scope.pick(scope.random);
};
scope.pick = function(color) {
scope.selected = color;
};
}
};
});

Просмотреть файл

@ -145,10 +145,10 @@ angular.module('Tasks').run([
]);
angular.module('Tasks').controller('AppController', [
'$scope', 'ListsBusinessLayer', '$route', 'Status', '$timeout', '$location', '$routeParams', 'Loading', 'SettingsModel', 'Persistence', function($scope, ListsBusinessLayer, $route, status, $timeout, $location, $routeParams, Loading, SettingsModel, Persistence) {
'$scope', '$rootScope', 'ListsBusinessLayer', '$route', 'Status', '$timeout', '$location', '$routeParams', 'Loading', 'SettingsModel', 'Persistence', function($scope, $rootScope, ListsBusinessLayer, $route, status, $timeout, $location, $routeParams, Loading, SettingsModel, Persistence) {
'use strict';
var AppController = (function() {
function AppController(_$scope, _$listsbusinesslayer, _$route, _$status, _$timeout, _$location, _$routeparams, _Loading, _$settingsmodel, _persistence) {
function AppController(_$scope, $rootScope, _$listsbusinesslayer, _$route, _$status, _$timeout, _$location, _$routeparams, _Loading, _$settingsmodel, _persistence) {
this._$scope = _$scope;
this._$listsbusinesslayer = _$listsbusinesslayer;
this._$route = _$route;
@ -175,6 +175,9 @@ angular.module('Tasks').controller('AppController', [
this._$scope.closeAll = function($event) {
if ($($event.target).closest('.close-all').length || $($event.currentTarget).is($($event.target).closest('.handler'))) {
if (!angular.isUndefined(_$scope.route.calendarID)) {
if (_$scope.route.listparameter == 'name') {
$rootScope.$broadcast('cancelEditCalendar', _$scope.route.calendarID);
}
_$location.path('/calendars/' + _$scope.route.calendarID);
} else if (!angular.isUndefined(_$scope.route.collectionID)) {
_$location.path('/collections/' + _$scope.route.collectionID);
@ -200,7 +203,7 @@ angular.module('Tasks').controller('AppController', [
}
return AppController;
})();
return new AppController($scope, ListsBusinessLayer, $route, status, $timeout, $location, $routeParams, Loading, SettingsModel, Persistence);
return new AppController($scope, $rootScope, ListsBusinessLayer, $route, status, $timeout, $location, $routeParams, Loading, SettingsModel, Persistence);
}
]);
@ -575,16 +578,16 @@ angular.module('Tasks').controller('DetailsController', [
]);
angular.module('Tasks').controller('ListController', [
'$scope', '$window', '$routeParams', 'ListsModel', 'TasksBusinessLayer', 'CollectionsModel', 'ListsBusinessLayer', '$location',
'$scope', '$rootScope', '$window', '$routeParams', 'ListsModel', 'TasksBusinessLayer', 'CollectionsModel', 'ListsBusinessLayer', '$location',
'SearchBusinessLayer', 'CalendarService', 'TasksModel',
function($scope, $window, $routeParams, ListsModel, TasksBusinessLayer, CollectionsModel, ListsBusinessLayer, $location,
function($scope, $rootScope, $window, $routeParams, ListsModel, TasksBusinessLayer, CollectionsModel, ListsBusinessLayer, $location,
SearchBusinessLayer, CalendarService, TasksModel) {
'use strict';
var ListController;
ListController = (function() {
function ListController(_$scope, _$window, _$routeParams, _$listsmodel, _$tasksbusinesslayer, _$collectionsmodel, _$listsbusinesslayer, $location,
function ListController(_$scope, $rootScope, _$window, _$routeParams, _$listsmodel, _$tasksbusinesslayer, _$collectionsmodel, _$listsbusinesslayer, $location,
_$searchbusinesslayer, _$calendarservice, _$tasksmodel) {
this._$scope = _$scope;
this._$window = _$window;
this._$routeParams = _$routeParams;
@ -641,21 +644,30 @@ angular.module('Tasks').controller('ListController', [
}
};
this._$scope.startRename = function(calendar) {
this._$scope.startEdit = function(calendar) {
_$scope.status.addingList = false;
calendar.prepareUpdate();
return $location.path('/calendars/' + _$scope.route.calendarID + '/edit/name');
};
this._$scope.cancelRename = function(event,calendar) {
this._$scope.checkKey = function(event,calendar) {
if (event.keyCode === 27) {
event.preventDefault();
calendar.resetToPreviousState();
$location.path('/calendars/' + _$scope.route.calendarID);
_$scope.cancelEdit(calendar);
}
};
this._$scope.rename = function(calendar) {
$rootScope.$on('cancelEditCalendar', function(s, calendarUri) {
var calendar = _$listsmodel.getByUri(calendarUri);
_$scope.cancelEdit(calendar);
});
this._$scope.cancelEdit = function(calendar) {
calendar.resetToPreviousState();
$location.path('/calendars/' + _$scope.route.calendarID);
};
this._$scope.saveEdit = function(calendar) {
var name = calendar.displayname;
if (name) {
if (!_$listsmodel.isNameAlreadyTaken(calendar.displayname, calendar.uri)) {
@ -744,7 +756,7 @@ angular.module('Tasks').controller('ListController', [
return ListController;
})();
return new ListController($scope, $window, $routeParams, ListsModel, TasksBusinessLayer, CollectionsModel, ListsBusinessLayer, $location,
return new ListController($scope, $rootScope, $window, $routeParams, ListsModel, TasksBusinessLayer, CollectionsModel, ListsBusinessLayer, $location,
SearchBusinessLayer, CalendarService, TasksModel);
}
]);
@ -1233,6 +1245,150 @@ angular.module('Tasks').directive('avatar', function() {
};
});
/* https://github.com/kayellpeee/hsl_rgb_converter
* expected hue range: [0, 360)
* expected saturation range: [0, 1]
* expected lightness range: [0, 1]
*/
var hslToRgb = function(hue, saturation, lightness) {
'use strict';
// based on algorithm from http://en.wikipedia.org/wiki/HSL_and_HSV#Converting_to_RGB
if(Array.isArray(hue)) {
saturation = hue[1];
lightness = hue[2];
hue = hue[0];
}
if (hue === undefined) {
return [0, 0, 0];
}
saturation /= 100;
lightness /= 100;
var chroma = (1 - Math.abs((2 * lightness) - 1)) * saturation;
var huePrime = hue / 60;
var secondComponent = chroma * (1 - Math.abs((huePrime % 2) - 1));
huePrime = Math.floor(huePrime);
var red;
var green;
var blue;
if (huePrime === 0) {
red = chroma;
green = secondComponent;
blue = 0;
} else if (huePrime === 1) {
red = secondComponent;
green = chroma;
blue = 0;
} else if (huePrime === 2) {
red = 0;
green = chroma;
blue = secondComponent;
} else if (huePrime === 3) {
red = 0;
green = secondComponent;
blue = chroma;
} else if (huePrime === 4) {
red = secondComponent;
green = 0;
blue = chroma;
} else if (huePrime === 5) {
red = chroma;
green = 0;
blue = secondComponent;
}
var lightnessAdjustment = lightness - (chroma / 2);
red += lightnessAdjustment;
green += lightnessAdjustment;
blue += lightnessAdjustment;
return [Math.round(red * 255), Math.round(green * 255), Math.round(blue * 255)];
};
/*
* Convert rgb array to hex string
*/
var rgbToHex = function(r, g, b) {
'use strict';
if(Array.isArray(r)) {
g = r[1];
b = r[2];
r = r[0];
}
return '#' + parseInt(r, 10).toString(16) + parseInt(g, 10).toString(16) + parseInt(b, 10).toString(16);
};
var listofcolours = [
'#31CC7C',
'#317CCC',
'#FF7A66',
'#F1DB50',
'#7C31CC',
'#CC317C',
'#3A3B3D',
'#CACBCD'
];
/*
* Generate a random colour with the core generator
*/
var randColour = function() {
'use strict';
if (typeof String.prototype.toHsl === 'function') {
return rgbToHex(hslToRgb(Math.random().toString().toHsl()));
} else {
return listofcolours[Math.floor(Math.random() * listofcolours.length)];
}
};
/**
* Directive: Colorpicker
* Description: Colorpicker for the Tasks app.
*/
angular.module('Tasks').directive('colorpicker', function() {
'use strict';
if (typeof String.prototype.toHsl === 'function') {
var hsl = "";
var hslcolour = "";
// 0 40 80 120 160 200 240 280 320
listofcolours = ["15", "9", "4", "b", "6", "11", "74", "f", "57"];
listofcolours.forEach(function(hash, index) {
hsl = hash.toHsl();
hslcolour = hslToRgb(hsl);
listofcolours[index] = rgbToHex(hslcolour);
});
}
return {
scope: {
selected: '=',
customizedColors: '=colors'
},
restrict: 'AE',
templateUrl: OC.filePath('tasks', 'templates', 'colorpicker.html'),
link: function(scope, element, attr) {
scope.colors = scope.customizedColors || listofcolours;
scope.selected = scope.selected || scope.colors[0];
scope.random = "#000000";
scope.randomizeColour = function() {
scope.random = randColour();
scope.pick(scope.random);
};
scope.pick = function(color) {
scope.selected = color;
};
}
};
});
angular.module('Tasks').directive('datepicker', function() {
'use strict';
return {

Просмотреть файл

@ -0,0 +1,9 @@
<ul class="colorpicker-list">
<li ng-repeat="color in colors"
ng-class="{ selected: (color===selected) }"
ng-click="pick(color)"
ng-style="{ 'background-color':color}; "></li>
<!--<li class="randomcolour"
ng-click="randomizeColour()"
ng-style="{ 'background-color':random}; "></li>-->
</ul>

Просмотреть файл

@ -57,9 +57,9 @@
</div>
<div class="app-navigation-entry-menu" ng-show="calendar.writable">
<ul>
<li title="<?php p($l->t('Rename')); ?>" ng-click="startRename(calendar)" >
<li title="<?php p($l->t('Edit')); ?>" ng-click="startEdit(calendar)" >
<img class="icon-rename svg" src="<?php p(image_path('core', 'actions/rename.svg'))?>"/>
<span><?php p($l->t('Rename')); ?></span>
<span><?php p($l->t('Edit')); ?></span>
</li>
<li title="<?php p($l->t('Delete')); ?>" ng-click="delete(calendar)">
<img class="icon-delete svg" src="<?php p(image_path('core', 'actions/delete.svg'))?>"/>
@ -69,9 +69,13 @@
</div>
<div class="app-navigation-entry-edit">
<form>
<input ng-model="calendar.displayname" class="edit" type="text" ng-keydown="cancelRename($event,calendar)" autofocus-on-insert>
<input type="submit" value="" class="action icon-checkmark svg" ng-click="rename(calendar)">
<input ng-model="calendar.displayname" class="edit" type="text" ng-keydown="checkKey($event,calendar)" autofocus-on-insert>
<input type="cancel" value="" class="action icon-close svg" ng-click="cancelEdit(calendar)" title="<?php p($l->t('Cancel')); ?>">
<input type="submit" value="" class="action icon-checkmark svg" ng-click="saveEdit(calendar)" title="<?php p($l->t('Save')); ?>">
</form>
<colorpicker class="colorpicker"
selected="calendar.color">
</colorpicker>
</div>
</li>
<li class="newList handler" ng-class="{edit: status.addingList}">