diff --git a/app/assets/stylesheets/content/_datepicker.sass b/app/assets/stylesheets/content/_datepicker.sass
index c3a6ed2cae..8f72a3ad40 100644
--- a/app/assets/stylesheets/content/_datepicker.sass
+++ b/app/assets/stylesheets/content/_datepicker.sass
@@ -139,6 +139,11 @@ $dp-shadow-box-opacity: 1
padding-bottom: $dp-week-padding-bottom
@include set-table-cells-width($dp-table-width, $dp-number-of-columns)
+ // Cancel out effect of min-width css in generic-table in cases where the
+ // date picker is included inside a table.
+ table.generic-table &
+ min-width: 0px
+
td
padding: 0
color: $dp-days-text-color
@@ -156,6 +161,11 @@ $dp-shadow-box-opacity: 1
border: $dp-border
border-radius: $dp-cell-corner-radius
+ // Cancel out effect of min-width css in generic-table in cases where the
+ // date picker is included inside a table.
+ table.generic-table &
+ min-width: 0px
+
.ui-datepicker-week-col
color: $dp-week-text-color
text-align: center
@@ -174,3 +184,6 @@ $dp-shadow-box-opacity: 1
opacity: 1
filter: Alpha(Opacity = 100)
font-weight: bold
+
+.ui-datepicker--container
+ position: absolute
diff --git a/frontend/app/components/wp-edit/op-date-picker.directive.html b/frontend/app/components/wp-edit/op-date-picker.directive.html
new file mode 100644
index 0000000000..2480b3a275
--- /dev/null
+++ b/frontend/app/components/wp-edit/op-date-picker.directive.html
@@ -0,0 +1,7 @@
+
+
+
+
+
+
diff --git a/frontend/app/components/wp-edit/op-date-picker.directive.ts b/frontend/app/components/wp-edit/op-date-picker.directive.ts
new file mode 100644
index 0000000000..3a873ce5d5
--- /dev/null
+++ b/frontend/app/components/wp-edit/op-date-picker.directive.ts
@@ -0,0 +1,167 @@
+// -- copyright
+// OpenProject is a project management system.
+// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License version 3.
+//
+// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+// Copyright (C) 2006-2013 Jean-Philippe Lang
+// Copyright (C) 2010-2013 the ChiliProject Team
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+// See doc/COPYRIGHT.rdoc for more details.
+// ++
+
+interface opDatePickerScope extends ng.IScope {
+ onDeactivate:Function,
+ onChange:Function
+}
+
+function opDatePickerLink(scope: opDatePickerScope, element: ng.IAugmentedJQuery, attrs, ngModel) {
+ // we don't want the date picker in the accessibility mode
+ if (this.ConfigurationService.accessibilityModeEnabled()) {
+ return;
+ }
+
+ let input = element.find('.hidden-date-picker-input');
+ let datePickerContainer = element.find('.ui-datepicker--container');
+ let datePickerInstance;
+ let DatePicker = this.Datepicker;
+ let onDeactivate = scope.onDeactivate;
+ let onChange = scope.onChange;
+ let onClickCallback;
+
+ let unbindNgModelInitializationWatch = scope.$watch(() => ngModel.$viewValue !== NaN, () => {
+ showDatePicker();
+ unbindNgModelInitializationWatch();
+ });
+
+ function hide() {
+ datePickerInstance.hide();
+ unregisterClickCallback();
+ };
+
+ function registerClickCallback() {
+ // HACK: we need to bind to 'mousedown' because the wp-edit-field.directive
+ // binds to 'click' and stops the event propagation
+ onClickCallback = angular.element('body').bind('mousedown', function(e) {
+ var target = angular.element(e.target);
+ if(!target.is(input) &&
+ target.parents(datePickerContainer.attr('class')).length <= 0 &&
+ target.parents('.ui-datepicker-header').length <= 0) {
+
+ hide();
+ onDeactivate();
+ }
+ });
+ }
+
+ function unregisterClickCallback() {
+ angular.element('body').unbind('click', onClickCallback);
+ }
+
+ function showDatePicker() {
+ datePickerInstance = new DatePicker(datePickerContainer, input, ngModel.$viewValue);
+ ensureDatePickerVisible();
+
+ datePickerInstance.onChange = function(date) {
+ ngModel.$setViewValue(date);
+ onChange();
+ };
+
+ datePickerInstance.onDone = function() {
+ onChange();
+ };
+
+ registerClickCallback();
+ };
+
+ function ensureDatePickerVisible() {
+ let visibilityContainer = datePickerContainer.offsetParent();
+ let templateContainer = element.children('div');
+ // typescript compiler does not like it if we simply use
+ // containerBoundaries = visibilityContainer.offset()
+ let containerBoundaries = { top: visibilityContainer.offset().top,
+ left: visibilityContainer.offset().left,
+ right: visibilityContainer.offset().left + visibilityContainer.width(),
+ bottom: visibilityContainer.offset().top + visibilityContainer.height() };
+
+ let positions = [
+ {
+ check: ((templateContainer.offset().top + templateContainer.height() + datePickerContainer.height() <= containerBoundaries.bottom) &&
+ (templateContainer.offset().left + datePickerContainer.width() <= containerBoundaries.right)),
+ css: {} //no change
+ },
+ {
+ check: ((templateContainer.offset().top + templateContainer.height() + datePickerContainer.height() <= containerBoundaries.bottom) &&
+ (templateContainer.offset().left + datePickerContainer.width() >= containerBoundaries.right)),
+ css: { marginLeft: templateContainer[0].offsetWidth - datePickerContainer.width() }
+ },
+ {
+ check: ((templateContainer.offset().top - datePickerContainer.height() >= containerBoundaries.top) &&
+ (templateContainer.offset().left + datePickerContainer.width() <= containerBoundaries.right)),
+ css: { marginTop: -templateContainer[0].offsetHeight - datePickerContainer.height() + 'px' }
+ },
+ {
+ check: ((templateContainer.offset().top - datePickerContainer.height() >= containerBoundaries.top) &&
+ (templateContainer.offset().left + datePickerContainer.width() >= containerBoundaries.right)),
+ css: { marginTop: -templateContainer[0].offsetHeight - datePickerContainer.height() + 'px',
+ marginLeft: templateContainer[0].offsetWidth - datePickerContainer.width() }
+ },
+ {
+ check: templateContainer.offset().left + templateContainer.width() + datePickerContainer.width() <= containerBoundaries.right,
+ css: { marginTop: -templateContainer[0].offsetHeight/2 - datePickerContainer.height()/2 + 'px',
+ marginLeft: templateContainer[0].offsetWidth }
+ },
+ {
+ check: true,
+ css: { marginTop: -templateContainer[0].offsetHeight/2 - datePickerContainer.height()/2 + 'px',
+ marginLeft: -datePickerContainer[0].offsetWidth }
+ }
+ ]
+
+ // use _.some to limit the checks to the first truthy position
+ _.some(positions, (position) => {
+ if (position.check) {
+ datePickerContainer.css(position.css);
+ return true;
+ }
+ });
+ };
+}
+
+function opDatePicker(ConfigurationService, Datepicker) {
+ let dependencies = { ConfigurationService: ConfigurationService,
+ Datepicker: Datepicker };
+
+ return {
+ restrict: 'E',
+ transclude: true,
+ templateUrl: '/components/wp-edit/op-date-picker.directive.html',
+ // by curtesy of http://stackoverflow.com/a/33614939/3206935
+ link: angular.bind(dependencies, opDatePickerLink),
+ require: 'ngModel',
+ scope: {
+ onChange: "&",
+ onDeactivate: "&"
+ }
+ };
+}
+
+angular
+ .module('openproject')
+ .directive('opDatePicker', opDatePicker);
diff --git a/frontend/app/components/wp-edit/wp-edit-field/wp-edit-boolean-field.directive.html b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-boolean-field.directive.html
new file mode 100644
index 0000000000..2515f67e13
--- /dev/null
+++ b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-boolean-field.directive.html
@@ -0,0 +1,6 @@
+
diff --git a/frontend/app/components/wp-edit/wp-edit-field/wp-edit-boolean-field.module.ts b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-boolean-field.module.ts
new file mode 100644
index 0000000000..368b35170c
--- /dev/null
+++ b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-boolean-field.module.ts
@@ -0,0 +1,33 @@
+// -- copyright
+// OpenProject is a project management system.
+// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License version 3.
+//
+// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+// Copyright (C) 2006-2013 Jean-Philippe Lang
+// Copyright (C) 2010-2013 the ChiliProject Team
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+// See doc/COPYRIGHT.rdoc for more details.
+// ++
+
+import {Field} from "./wp-edit-field.module";
+
+export class BooleanField extends Field {
+ public template:string = '/components/wp-edit/wp-edit-field/wp-edit-boolean-field.directive.html'
+}
diff --git a/frontend/app/components/wp-edit/wp-edit-field/wp-edit-date-field.directive.html b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-date-field.directive.html
new file mode 100644
index 0000000000..9a82e98e0d
--- /dev/null
+++ b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-date-field.directive.html
@@ -0,0 +1,10 @@
+
+
+
+
+
diff --git a/frontend/app/components/wp-edit/wp-edit-field/wp-edit-date-field.module.ts b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-date-field.module.ts
new file mode 100644
index 0000000000..b4980357a9
--- /dev/null
+++ b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-date-field.module.ts
@@ -0,0 +1,33 @@
+// -- copyright
+// OpenProject is a project management system.
+// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License version 3.
+//
+// OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+// Copyright (C) 2006-2013 Jean-Philippe Lang
+// Copyright (C) 2010-2013 the ChiliProject Team
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+// See doc/COPYRIGHT.rdoc for more details.
+// ++
+
+import {Field} from "./wp-edit-field.module";
+
+export class DateField extends Field {
+ public template:string = '/components/wp-edit/wp-edit-field/wp-edit-date-field.directive.html'
+}
diff --git a/frontend/app/components/wp-edit/wp-edit-field/wp-edit-field.config.ts b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-field.config.ts
index 46deec9d27..e6cf285c34 100644
--- a/frontend/app/components/wp-edit/wp-edit-field/wp-edit-field.config.ts
+++ b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-field.config.ts
@@ -26,26 +26,18 @@
// See doc/COPYRIGHT.rdoc for more details.
// ++
-import {Field} from "./wp-edit-field.module";
import {WorkPackageEditFieldService} from "./wp-edit-field.service";
+import {Field} from "./wp-edit-field.module";
import {TextField} from "./wp-edit-text-field.module";
import {SelectField} from "./wp-edit-select-field.module";
-
-//TODO: Implement
-class DateField extends Field {}
+import {FloatField} from "./wp-edit-float-field.module";
+import {IntegerField} from "./wp-edit-integer-field.module";
+import {BooleanField} from "./wp-edit-boolean-field.module";
+import {DateField} from "./wp-edit-date-field.module";
//TODO: Implement
class DateRangeField extends Field {}
-//TODO: Implement
-class IntegerField extends Field {}
-
-//TODO: Implement
-class FloatField extends Field {}
-
-//TODO: Implement
-class BooleanField extends Field {}
-
//TODO: Implement
class DurationField extends Field {}
@@ -65,5 +57,10 @@ angular
'Type',
'User',
'Version',
- 'Category']);
+ 'Category',
+ 'StringObject'])
+ .addFieldType(FloatField, 'float', ['Float'])
+ .addFieldType(IntegerField, 'integer', ['Integer'])
+ .addFieldType(BooleanField, 'boolean', ['Boolean'])
+ .addFieldType(DateField, 'date', ['Date']);
});
diff --git a/frontend/app/components/wp-edit/wp-edit-field/wp-edit-field.directive.html b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-field.directive.html
index 9423766990..3240a426c1 100644
--- a/frontend/app/components/wp-edit/wp-edit-field/wp-edit-field.directive.html
+++ b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-field.directive.html
@@ -1,6 +1,5 @@