Initial recurrence editor for appointments

This commit is contained in:
Francis Lachapelle
2015-05-20 11:11:01 -04:00
parent 7e96f37809
commit dc91be3c0d
13 changed files with 330 additions and 245 deletions
@@ -48,14 +48,13 @@
* @desc Factory registration of Component in Angular module.
*/
angular.module('SOGo.SchedulerUI')
/* Factory registration in Angular module */
.factory('Component', Component.$factory);
/**
* @function $filter
* @memberof Component.prototype
* @desc Search for components matching some criterias
* @param {string} type - Either 'events' or 'tasks'
* @param {string} type - either 'events' or 'tasks'
* @param {object} [options] - additional options to the query
* @returns a collection of Components instances
*/
@@ -82,11 +81,11 @@
};
/**
* @memberof Card
* @desc Fetch a card from a specific addressbook.
* @param {string} addressbook_id - the addressbook ID
* @param {string} card_id - the card ID
* @see {@link AddressBook.$getCard}
* @function $find
* @desc Fetch a component from a specific calendar.
* @param {string} calendarId - the calendar ID
* @param {string} componentId - the component ID
* @see {@link Calendar.$getComponent}
*/
Component.$find = function(calendarId, componentId) {
var futureComponentData = this.$$resource.fetch([calendarId, componentId].join('/'), 'view');
@@ -96,7 +95,6 @@
/**
* @function filterCategories
* @memberof Component.prototype
* @desc Search for categories matching some criterias
* @param {string} search - the search string to match
* @returns a collection of strings
@@ -110,7 +108,6 @@
/**
* @function $eventsBlocksForView
* @memberof Component.prototype
* @desc Events blocks for a specific week
* @param {string} view - Either 'day' or 'week'
* @param {Date} type - Date of any day of the desired period
@@ -146,7 +143,6 @@
/**
* @function $eventsBlocks
* @memberof Component.prototype
* @desc Events blocks for a specific view and period
* @param {string} view - Either 'day' or 'week'
* @param {Date} startDate - period's start date
@@ -197,9 +193,9 @@
};
/**
* @function $unwrap
* @memberof Comonent.prototype
* @function $unwrapCollection
* @desc Unwrap a promise and instanciate new Component objects using received data.
* @param {string} type - either 'events' or 'tasks'
* @param {promise} futureComponentData - a promise of the components' metadata
* @returns a promise of the HTTP operation
*/
@@ -241,10 +237,59 @@
*/
Component.prototype.init = function(data) {
this.categories = [];
this.repeat = {};
angular.extend(this, data);
// Parse recurrence rule definition and initialize default values
if (this.repeat.days) {
var byDayMask = _.find(this.repeat.days, function(o) {
return angular.isDefined(o.occurrence);
});
if (byDayMask)
if (this.repeat.frequency == 'yearly')
this.repeat.year = { byday: true };
this.repeat.month = {
type: 'byday',
occurrence: byDayMask.occurrence.toString(),
day: byDayMask.day
};
}
else {
this.repeat.days = [];
}
if (angular.isUndefined(this.repeat.interval))
this.repeat.interval = 1;
if (angular.isUndefined(this.repeat.month))
this.repeat.month = { occurrence: '1', day: 'SU', type: 'bymonthday' };
if (angular.isUndefined(this.repeat.monthdays))
this.repeat.monthdays = [];
if (angular.isUndefined(this.repeat.months))
this.repeat.months = [];
if (angular.isUndefined(this.repeat.year))
this.repeat.year = {};
if (this.repeat.count)
this.repeat.end = 'count';
else if (this.repeat.until) {
this.repeat.end = 'until';
this.repeat.until = this.repeat.until.substring(0,10).asDate();
}
else
this.repeat.end = 'never';
this.$hasCustomRepeat = this.hasCustomRepeat();
// Allow the event to be moved to a different calendar
this.destinationCalendar = this.pid;
};
Component.prototype.hasCustomRepeat = function() {
var b = angular.isDefined(this.repeat) &&
(this.repeat.interval > 1 ||
this.repeat.days && this.repeat.days.length > 0 ||
this.repeat.monthdays && this.repeat.monthdays.length > 0 ||
this.repeat.months && this.repeat.months.length > 0);
return b;
};
/**
* @function getClassName
* @memberof Component.prototype
@@ -260,8 +305,8 @@
/**
* @function $reset
* @memberof Card.prototype
* @desc Reset the original state the card's data.
* @memberof Component.prototype
* @desc Reset the original state the component's data.
*/
Component.prototype.$reset = function() {
var _this = this;
@@ -300,29 +345,21 @@
* @param {promise} futureComponentData - a promise of some of the Component's data
*/
Component.prototype.$unwrap = function(futureComponentData) {
var _this = this,
deferred = Component.$q.defer();
var _this = this;
// Expose the promise
this.$futureComponentData = futureComponentData;
// Resolve the promise
this.$futureComponentData.then(function(data) {
// Calling $timeout will force Angular to refresh the view
Component.$timeout(function() {
_this.init(data);
// Make a copy of the data for an eventual reset
_this.$shadowData = _this.$omit();
deferred.resolve(_this);
});
_this.init(data);
// Make a copy of the data for an eventual reset
_this.$shadowData = _this.$omit();
}, function(data) {
angular.extend(_this, data);
_this.isError = true;
Component.$log.error(_this.error);
deferred.reject();
});
return deferred.promise;
};
/**
@@ -335,13 +372,39 @@
var component = {}, date;
angular.forEach(this, function(value, key) {
if (key != 'constructor' && key[0] != '$') {
component[key] = value;
component[key] = angular.copy(value);
}
});
// Format times
component.startTime = component.startDate ? formatTime(component.startDate) : '';
component.endTime = component.endDate ? formatTime(component.endDate) : '';
// Update recurrence definition depending on selections
if (this.$hasCustomRepeat) {
if (this.repeat.frequency == 'monthly' && this.repeat.month.type && this.repeat.month.type == 'byday'
|| this.repeat.frequency == 'yearly' && this.repeat.year.byday) {
// BYDAY mask for a monthly or yearly recurrence
delete component.repeat.monthdays;
component.repeat.days = [{ day: this.repeat.month.day, occurrence: this.repeat.month.occurrence.toString() }];
}
else if (this.repeat.month.type) {
// montly recurrence by month days or yearly by month
delete component.repeat.days;
}
}
else {
component.repeat = { frequency: this.repeat.frequency };
}
if (this.repeat.end == 'until' && this.repeat.until)
component.repeat.until = this.repeat.until.stringWithSeparator('-');
else if (this.repeat.end == 'count' && this.repeat.count)
component.repeat.count = this.repeat.count;
else {
delete component.repeat.until;
delete component.repeat.count;
}
function formatTime(dateString) {
// YYYY-MM-DDTHH:MM-05:00
var date = new Date(dateString.substring(0,10) + ' ' + dateString.substring(11,16)),
@@ -353,7 +416,6 @@
return hours + ':' + minutes;
}
return component;
};
@@ -13,7 +13,8 @@
vm.calendars = stateCalendars;
vm.event = stateComponent;
vm.categories = {};
vm.editRecurrence = editRecurrence;
vm.showRecurrenceEditor = vm.event.$hasCustomRepeat;
vm.toggleRecurrenceEditor = toggleRecurrenceEditor;
vm.cancel = cancel;
vm.save = save;
@@ -35,14 +36,9 @@
}, 100); // don't ask why
});
function editRecurrence($event) {
$mdDialog.show({
templateUrl: 'editRecurrence', // UI/Templates/SchedulerUI/UIxRecurrenceEditor.wox
controller: RecurrenceController
});
function RecurrenceController() {
}
function toggleRecurrenceEditor() {
vm.showRecurrenceEditor = !vm.showRecurrenceEditor;
vm.event.$hasCustomRepeat = vm.showRecurrenceEditor;
}
function save(form) {
@@ -32,3 +32,9 @@ md-content {
padding: $mg;
}
}
.sg-subcontent {
border-left: $baseline-grid solid sg-color($sogoGreen, 100);
margin-left: ($baseline-grid / 2);
padding-left: $baseline-grid;
}
@@ -0,0 +1,2 @@
/*! input/_extends.scss - */
@import '../../../angular-material/src/components/input/input.scss';
@@ -1,97 +1,14 @@
/// input.scss -*- Mode: text; indent-tabs-mode: nil; basic-offset: 2 -*-
$input-container-padding: 2px !default;
/// input.scss -*- Mode: scss; indent-tabs-mode: nil; basic-offset: 2 -*-
@import 'extends';
$input-label-default-offset: 24px !default;
$input-label-default-scale: 1.0 !default;
$input-label-float-offset: 4px !default;
$input-label-float-scale: 0.75 !default;
$input-border-width-default: 1px !default;
$input-border-width-focused: 2px !default;
$input-line-height: 26px !default;
$input-padding-top: 2px !default;
md-input-container {
display: flex;
position: relative;
flex-direction: column;
padding: $input-container-padding;
textarea,
input[type="text"],
input[type="password"],
input[type="datetime"],
input[type="datetime-local"],
input[type="date"],
input[type="month"],
input[type="time"],
input[type="week"],
input[type="number"],
input[type="email"],
input[type="url"],
input[type="search"],
input[type="tel"],
input[type="color"] {
/* remove default appearance from all input/textarea */
-moz-appearance: none;
-webkit-appearance: none;
}
textarea {
resize: none;
overflow: hidden;
}
label {
order: 1;
pointer-events: none;
-webkit-font-smoothing: antialiased;
z-index: 1;
transform: translate3d(0, $input-label-default-offset, 0) scale($input-label-default-scale);
transform-origin: left top;
transition: all $swift-ease-out-timing-function 0.2s;
}
/*
* The .md-input class is added to the input/textarea
*/
.md-input {
flex: 1;
order: 2;
display: block;
background: none;
padding-top: $input-padding-top;
padding-bottom: $input-border-width-focused - $input-border-width-default;
border-width: 0 0 $input-border-width-default 0;
line-height: $input-line-height;
-ms-flex-preferred-size: $input-line-height; //IE fix
&:focus {
outline: none;
}
}
&.md-input-focused,
&.md-input-has-value {
label {
transform: translate3d(0,$input-label-float-offset,0) scale($input-label-float-scale);
}
}
&.md-input-focused {
.md-input {
padding-bottom: 0px; // Increase border width by 1px, decrease padding by 1
border-width: 0 0 $input-border-width-focused 0;
}
}
.md-input[disabled] {
background-position: 0 bottom;
// This background-size is coordinated with a linear-gradient set in input-theme.scss
// to create a dotted line under the input.
background-size: 3px 1px;
background-repeat: repeat-x;
md-input-container.md-input-number {
flex-grow: 0;
width: 4em;
input {
text-align: center;
}
}
md-input-container .bgroup {
display: block;
}
@@ -68,10 +68,6 @@ $input-padding-top: 2px !default;
.pseudo-input-field {
display: block;
margin-bottom: $line;
padding: $line 0 0 0;
font-size: sg-size(subhead);
line-height: 1;
}
.pseudo-input-field--underline {
@@ -5,6 +5,13 @@ md-select {
margin-right: $bl;
}
// Try to align select labels with other input components
[layout="row"] {
.md-select-label {
padding-top: 4px;
}
}
// angular material overqualifies, so we are
md-select.md-default-theme.sg-toolbar-sort {
margin: 0 $bl 4px 0;