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 @@
-
diff --git a/frontend/app/components/wp-edit/wp-edit-field/wp-edit-field.directive.ts b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-field.directive.ts index 3ef3c10828..b302956564 100644 --- a/frontend/app/components/wp-edit/wp-edit-field/wp-edit-field.directive.ts +++ b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-field.directive.ts @@ -62,7 +62,7 @@ export class WorkPackageEditFieldController { } public get isEditable():boolean { - return this.isSupportedField && this.workPackage.isEditable; + return this.workPackage.isEditable; } public deactivate():boolean { @@ -75,20 +75,6 @@ export class WorkPackageEditFieldController { this.workPackage, this.fieldName, schema[this.fieldName]); }); } - - // This method is temporarily needed to control which fields - // we support for inline editing. Once all fields are supported, - // the method is to be removed. - private get isSupportedField():boolean { - return ['subject', - 'priority', - 'type', - 'status', - 'assignee', - 'responsible', - 'version', - 'category'].indexOf(this.fieldName) !== -1 - } } function wpEditFieldLink(scope, element, attrs, controllers: diff --git a/frontend/app/components/wp-edit/wp-edit-field/wp-edit-float-field.directive.html b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-float-field.directive.html new file mode 100644 index 0000000000..975a826036 --- /dev/null +++ b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-float-field.directive.html @@ -0,0 +1,6 @@ + diff --git a/frontend/app/components/wp-edit/wp-edit-field/wp-edit-float-field.module.ts b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-float-field.module.ts new file mode 100644 index 0000000000..ad5f9c2f7e --- /dev/null +++ b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-float-field.module.ts @@ -0,0 +1,32 @@ +// 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 FloatField extends Field { + public template:string = '/components/wp-edit/wp-edit-field/wp-edit-float-field.directive.html' +} diff --git a/frontend/app/components/wp-edit/wp-edit-field/wp-edit-integer-field.directive.html b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-integer-field.directive.html new file mode 100644 index 0000000000..eef361d6e4 --- /dev/null +++ b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-integer-field.directive.html @@ -0,0 +1,5 @@ + diff --git a/frontend/app/components/wp-edit/wp-edit-field/wp-edit-integer-field.module.ts b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-integer-field.module.ts new file mode 100644 index 0000000000..10eff95a34 --- /dev/null +++ b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-integer-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 IntegerField extends Field { + public template:string = '/components/wp-edit/wp-edit-field/wp-edit-integer-field.directive.html' +} diff --git a/frontend/app/components/wp-edit/wp-edit-field/wp-edit-select-field.directive.html b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-select-field.directive.html index 53726b538b..19996331c5 100644 --- a/frontend/app/components/wp-edit/wp-edit-field/wp-edit-select-field.directive.html +++ b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-select-field.directive.html @@ -1,7 +1,8 @@ diff --git a/frontend/app/components/wp-edit/wp-edit-field/wp-edit-select-field.module.ts b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-select-field.module.ts index 69d6511c2c..7adfee2741 100644 --- a/frontend/app/components/wp-edit/wp-edit-field/wp-edit-select-field.module.ts +++ b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-select-field.module.ts @@ -27,7 +27,6 @@ // ++ import {Field} from "./wp-edit-field.module"; -import {WorkPackageEditFieldService} from "./wp-edit-field.service"; export class SelectField extends Field { public options:any[]; diff --git a/frontend/app/components/wp-edit/wp-edit-field/wp-edit-text-field.directive.html b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-text-field.directive.html index 524cc942a0..8988f49ce6 100644 --- a/frontend/app/components/wp-edit/wp-edit-field/wp-edit-text-field.directive.html +++ b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-text-field.directive.html @@ -1,4 +1,5 @@ + ng-blur="vm.deactivate()" + focus> diff --git a/frontend/app/components/wp-edit/wp-edit-field/wp-edit-text-field.module.ts b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-text-field.module.ts index d581192f00..c899b0fa79 100644 --- a/frontend/app/components/wp-edit/wp-edit-field/wp-edit-text-field.module.ts +++ b/frontend/app/components/wp-edit/wp-edit-field/wp-edit-text-field.module.ts @@ -27,7 +27,6 @@ // ++ import {Field} from "./wp-edit-field.module"; -import {WorkPackageEditFieldService} from "./wp-edit-field.service"; export class TextField extends Field { public template:string = '/components/wp-edit/wp-edit-field/wp-edit-text-field.directive.html' diff --git a/frontend/app/components/wp-table/wp-td/wp-td.directive.js b/frontend/app/components/wp-table/wp-td/wp-td.directive.js index 59b727cd67..dc8d6eb2df 100644 --- a/frontend/app/components/wp-table/wp-td/wp-td.directive.js +++ b/frontend/app/components/wp-table/wp-td/wp-td.directive.js @@ -49,6 +49,7 @@ function wpTd(){ function WorkPackageTdController($scope, I18n, PathHelper, WorkPackagesHelper) { var vm = this; + vm.displayText = I18n.t('js.work_packages.placeholders.default'); function updateAttribute() { if (!vm.schema[vm.attribute]) {