Refactor component initiation of inplace-edit

Well, I actually moved a line in the HALAPIResource service,
too...
pull/3701/head
Alex Dik 9 years ago committed by Jens Ulferts
parent bd86311e26
commit 66220d3f37
  1. 4
      frontend/app/components/common/services/hal-api-resource.service.js
  2. 176
      frontend/app/components/inplace-edit/directives/display-pane/display-pane.directive.js
  3. 422
      frontend/app/components/inplace-edit/directives/edit-pane/edit-pane.directive.js
  4. 58
      frontend/app/components/inplace-edit/directives/field-display/display-spent-time/display-spent-time.directive.js
  5. 66
      frontend/app/components/inplace-edit/directives/field-display/display-user/display-user.directive.js
  6. 53
      frontend/app/components/inplace-edit/directives/field-display/display-version/display-version.directive.js
  7. 231
      frontend/app/components/inplace-edit/directives/field-edit/edit-date-range/edit-date-range.directive.js
  8. 127
      frontend/app/components/inplace-edit/directives/field-edit/edit-date/edit-date.directive.js
  9. 147
      frontend/app/components/inplace-edit/directives/field-edit/edit-drop-down/edit-drop-down.directive.js
  10. 13
      frontend/app/components/inplace-edit/directives/field-edit/edit-duration/edit-duration.directive.js
  11. 135
      frontend/app/components/inplace-edit/directives/field-edit/edit-wiki-textarea/edit-wiki-textarea.directive.js
  12. 47
      frontend/app/components/inplace-edit/directives/main-pane/main-pane.directive.js
  13. 82
      frontend/app/components/inplace-edit/directives/work-package-field/work-package-field.directive.js

@ -33,8 +33,6 @@ angular.module('openproject.api')
.run(run)
.factory('HALAPIResource', HALAPIResource);
run.$inject = ['$http', '$q'];
function run($http, $q) {
Hyperagent.configure('ajax', function(settings) {
settings.transformResponse = function (data) { return data; };
@ -47,6 +45,8 @@ function run($http, $q) {
Hyperagent.configure('defer', $q.defer);
Hyperagent.configure('_', _);
}
run.$inject = ['$http', '$q'];
function HALAPIResource () {
return {

@ -26,100 +26,106 @@
// See doc/COPYRIGHT.rdoc for more details.
// ++
angular.module('openproject.inplace-edit').directive('inplaceEditorDisplayPane',[
'WorkPackageFieldService', 'EditableFieldsState', '$timeout', 'HookService', 'I18n',
angular
.module('openproject.inplace-edit')
.directive('inplaceEditorDisplayPane', inplaceEditorDisplayPane);
function(WorkPackageFieldService, EditableFieldsState, $timeout, HookService, I18n) {
return {
replace: true,
transclude: true,
scope: {},
require: '^workPackageField',
templateUrl: '/components/inplace-edit/directives/display-pane/display-pane.directive.html',
controller: function($scope) {
function inplaceEditorDisplayPane(WorkPackageFieldService, EditableFieldsState, $timeout, I18n) {
return {
replace: true,
transclude: true,
scope: {},
require: '^workPackageField',
templateUrl: '/components/inplace-edit/directives/display-pane/display-pane.directive.html',
controller: InplaceEditorDisplayPaneController,
controllerAs: 'displayPaneController',
this.placeholder = WorkPackageFieldService.defaultPlaceholder;
link: function(scope, element, attrs, fieldController) {
scope.fieldController = fieldController;
scope.displayPaneController.field = scope.fieldController.field;
scope.editableFieldsState = EditableFieldsState;
this.startEditing = function() {
var fieldController = $scope.fieldController;
fieldController.isEditing = true;
};
scope.$watchCollection('editableFieldsState.workPackage.form', function() {
var strategy = WorkPackageFieldService.getInplaceDisplayStrategy(
EditableFieldsState.workPackage,
fieldController.field
);
if (strategy !== scope.displayStrategy) {
scope.displayStrategy = strategy;
scope.templateUrl = '/templates/inplace-edit/display/fields/' + strategy +'.html';
}
});
this.isReadValueEmpty = function() {
return WorkPackageFieldService.isEmpty(
EditableFieldsState.workPackage,
$scope.fieldController.field
);
};
// TODO: extract this when more placeholders come
if (fieldController.field === 'description') {
scope.displayPaneController.placeholder = I18n.t('js.label_click_to_enter_description');
}
this.getReadValue = function() {
return WorkPackageFieldService.format(
EditableFieldsState.workPackage,
$scope.fieldController.field
);
};
scope.$watch('editableFieldsState.errors', function(errors) {
if (errors) {
if (errors[scope.fieldController.field]) {
scope.displayPaneController.startEditing();
}
}
}, true);
// for dynamic type that is set by plugins
// refactor to a service method the whole extraction
this.getDynamicDirectiveName = function() {
return HookService.call('workPackageOverviewAttributes', {
type: EditableFieldsState.workPackage.schema.props[$scope.fieldController.field].type,
field: $scope.fieldController.field,
workPackage: EditableFieldsState.workPackage
})[0];
};
scope.$watch('fieldController.isEditing', function(isEditing, oldIsEditing) {
if (!isEditing && !fieldController.lockFocus) {
$timeout(function() {
if (oldIsEditing) {
// check old value to not trigger focus on the first time
element.find('.inplace-editing--trigger-link').focus();
}
element.find('.inplace-edit--read-value a').off('click').on('click', function(e) {
e.stopPropagation();
});
});
}
// expose work package to the dynamic directive
this.getWorkPackage = function() {
return EditableFieldsState.workPackage;
};
},
controllerAs: 'displayPaneController',
link: function(scope, element, attrs, fieldController) {
scope.fieldController = fieldController;
scope.displayPaneController.field = scope.fieldController.field;
scope.editableFieldsState = EditableFieldsState;
fieldController.lockFocus = false;
});
}
};
}
inplaceEditorDisplayPane.$inject = ['WorkPackageFieldService', 'EditableFieldsState', '$timeout', 'I18n'];
scope.$watchCollection('editableFieldsState.workPackage.form', function() {
var strategy = WorkPackageFieldService.getInplaceDisplayStrategy(
EditableFieldsState.workPackage,
fieldController.field
);
if (strategy !== scope.displayStrategy) {
scope.displayStrategy = strategy;
scope.templateUrl = '/templates/inplace-edit/display/fields/' + strategy +'.html';
}
});
function InplaceEditorDisplayPaneController($scope, WorkPackageFieldService, EditableFieldsState,
HookService) {
// TODO: extract this when more placeholders come
if (fieldController.field === 'description') {
scope.displayPaneController.placeholder = I18n.t('js.label_click_to_enter_description');
}
this.placeholder = WorkPackageFieldService.defaultPlaceholder;
scope.$watch('editableFieldsState.errors', function(errors) {
if (errors) {
if (errors[scope.fieldController.field]) {
scope.displayPaneController.startEditing();
}
}
}, true);
this.startEditing = function() {
var fieldController = $scope.fieldController;
fieldController.isEditing = true;
};
scope.$watch('fieldController.isEditing', function(isEditing, oldIsEditing) {
if (!isEditing && !fieldController.lockFocus) {
$timeout(function() {
if (oldIsEditing) {
// check old value to not trigger focus on the first time
element.find('.inplace-editing--trigger-link').focus();
}
element.find('.inplace-edit--read-value a').off('click').on('click', function(e) {
e.stopPropagation();
});
});
}
this.isReadValueEmpty = function() {
return WorkPackageFieldService.isEmpty(
EditableFieldsState.workPackage,
$scope.fieldController.field
);
};
fieldController.lockFocus = false;
});
}
};
}
]);
this.getReadValue = function() {
return WorkPackageFieldService.format(
EditableFieldsState.workPackage,
$scope.fieldController.field
);
};
// for dynamic type that is set by plugins
// refactor to a service method the whole extraction
this.getDynamicDirectiveName = function() {
return HookService.call('workPackageOverviewAttributes', {
type: EditableFieldsState.workPackage.schema.props[$scope.fieldController.field].type,
field: $scope.fieldController.field,
workPackage: EditableFieldsState.workPackage
})[0];
};
// expose work package to the dynamic directive
this.getWorkPackage = function() {
return EditableFieldsState.workPackage;
};
}
InplaceEditorDisplayPaneController.$inject = ['$scope', 'WorkPackageFieldService', 'EditableFieldsState', 'HookService'];

@ -26,241 +26,249 @@
// See doc/COPYRIGHT.rdoc for more details.
// ++
angular.module('openproject.inplace-edit').directive('inplaceEditorEditPane', [
'WorkPackageFieldService', 'EditableFieldsState', 'FocusHelper', '$timeout', '$location', '$q',
'ApiHelper', '$rootScope', 'NotificationsService', 'I18n',
angular
.module('openproject.inplace-edit')
.directive('inplaceEditorEditPane', inplaceEditorEditPane);
function(WorkPackageFieldService, EditableFieldsState, FocusHelper, $timeout, $location, $q,
ApiHelper, $rootScope, NotificationsService, I18n) {
function inplaceEditorEditPane(WorkPackageFieldService, EditableFieldsState, FocusHelper,
$timeout) {
var showErrors = function() {
var errors = EditableFieldsState.errors;
if (_.isEmpty(_.keys(errors))) {
return;
}
var errorMessages = _.flatten(_.map(errors), true);
NotificationsService.addError(I18n.t('js.label_validation_error'), errorMessages);
};
return {
transclude: true,
replace: true,
scope: true,
require: '^workPackageField',
templateUrl: '/components/inplace-edit/directives/edit-pane/edit-pane.directive.html',
controllerAs: 'editPaneController',
controller: InplaceEditorEditPaneController,
link: function(scope, element, attrs, fieldController) {
scope.fieldController = fieldController;
scope.editableFieldsState = EditableFieldsState;
scope.editPaneController.isRequired = function() {
return WorkPackageFieldService.isRequired(
EditableFieldsState.workPackage,
this.field
);
};
return {
transclude: true,
replace: true,
scope: true,
require: '^workPackageField',
templateUrl: '/components/inplace-edit/directives/edit-pane/edit-pane.directive.html',
controllerAs: 'editPaneController',
controller: function($scope, $element, WorkPackageService) {
var vm = this;
scope.$watchCollection('editableFieldsState.workPackage.form', function(form) {
var strategy = WorkPackageFieldService.getInplaceEditStrategy(
EditableFieldsState.workPackage,
fieldController.field
);
if (fieldController.field === 'date' && strategy === 'date') {
form.pendingChanges = EditableFieldsState.getPendingFormChanges();
form.pendingChanges['startDate'] =
form.pendingChanges['dueDate'] =
fieldController.writeValue ? fieldController.writeValue['dueDate'] : null;
}
// go full retard
var uploadPendingAttachments = function(wp) {
$rootScope.$broadcast('uploadPendingAttachments', wp);
};
if (strategy !== scope.strategy) {
scope.strategy = strategy;
scope.templateUrl = '/templates/inplace-edit/edit/fields/' +
scope.strategy + '.html';
fieldController.updateWriteValue();
}
});
// Propagate submission to all active fields
// not contained in the workPackage.form (e.g., comment)
this.submit = function(notify) {
EditableFieldsState.save(notify, function() {
// Clears the location hash, as we're now
// scrolling to somewhere else
$location.hash(null);
$timeout(function() {
$element[0].scrollIntoView(false);
scope.focusInput = function() {
$timeout(function() {
var inputElement = element.find('.focus-input');
FocusHelper.focus(inputElement);
inputElement.triggerHandler('keyup');
scope.editPaneController.markActive();
inputElement.off('focus.inplace').on('focus.inplace', function() {
// ♥♥♥ angular ♥♥♥
scope.$apply(function() {
scope.editPaneController.markActive();
});
});
};
this.submitField = function(notify) {
var submit = $q.defer();
var fieldController = $scope.fieldController;
var pendingFormChanges = EditableFieldsState.getPendingFormChanges();
var detectedViolations = [];
var handleFailure = function(e) {
setFailure(e);
submit.reject(e);
};
});
};
pendingFormChanges[fieldController.field] = fieldController.writeValue;
if (vm.editForm.$invalid) {
var acknowledgedValidationErrors = Object.keys(vm.editForm.$error);
acknowledgedValidationErrors.forEach(function(error) {
if (vm.editForm.$error[error]) {
detectedViolations.push(I18n.t('js.inplace.errors.' + error, {
field: fieldController.getLabel()
}));
}
if (!EditableFieldsState.forcedEditState) {
element.bind('keydown keypress', function(e) {
if (e.keyCode === 27) {
scope.$apply(function() {
scope.editPaneController.discardEditing();
});
submit.reject();
}
if (detectedViolations.length) {
EditableFieldsState.errors = EditableFieldsState.errors || {};
EditableFieldsState.errors[fieldController.field] = detectedViolations.join(' ');
showErrors();
submit.reject();
} else {
fieldController.state.isBusy = true;
WorkPackageService.loadWorkPackageForm(EditableFieldsState.workPackage).then(
function(form) {
EditableFieldsState.workPackage.form = form;
if (_.isEmpty(form.embedded.validationErrors.props)) {
var result = WorkPackageService.updateWorkPackage(
EditableFieldsState.workPackage,
notify
);
result.then(angular.bind(this, function(updatedWorkPackage) {
submit.resolve();
$scope.$emit('workPackageUpdatedInEditor', updatedWorkPackage);
$scope.$on('workPackageRefreshed', function() {
fieldController.state.isBusy = false;
fieldController.isEditing = false;
fieldController.updateWriteValue();
});
uploadPendingAttachments(updatedWorkPackage);
})).catch(handleFailure);
} else {
afterError();
submit.reject();
EditableFieldsState.errors = {};
_.forEach(form.embedded.validationErrors.props, function(error, field) {
if(field === 'startDate' || field === 'dueDate') {
EditableFieldsState.errors['date'] = error.message;
} else {
EditableFieldsState.errors[field] = error.message;
}
});
}
}).catch(handleFailure);
}
});
}
return submit.promise;
};
this.discardEditing = function() {
$scope.fieldController.isEditing = false;
delete EditableFieldsState.submissionPromises['work_package'];
delete EditableFieldsState.getPendingFormChanges()[$scope.fieldController.field];
$scope.fieldController.updateWriteValue();
if (
EditableFieldsState.errors &&
EditableFieldsState.errors.hasOwnProperty($scope.fieldController.field)
) {
delete EditableFieldsState.errors[$scope.fieldController.field];
}
};
scope.$watch('fieldController.writeValue', function(writeValue) {
if (scope.fieldController.isEditing) {
var pendingChanges = EditableFieldsState.getPendingFormChanges();
pendingChanges[scope.fieldController.field] = writeValue;
}
}, true);
scope.$on('workPackageRefreshed', function() {
scope.editPaneController.discardEditing();
});
this.isActive = function() {
return EditableFieldsState.isActiveField($scope.fieldController.field);
};
scope.$watch('fieldController.isEditing', function(isEditing) {
var efs = EditableFieldsState, field = fieldController.field;
this.markActive = function() {
EditableFieldsState.submissionPromises['work_package'] = {
field: $scope.fieldController.field,
thePromise: this.submitField,
prepend: true,
};
EditableFieldsState.currentField = $scope.fieldController.field;
};
if (isEditing && !efs.editAll.state && !efs.forcedEditState) {
scope.focusInput();
function afterError() {
$scope.fieldController.state.isBusy = false;
$scope.focusInput();
}
function setFailure(e) {
afterError();
EditableFieldsState.errors = {
'_common': ApiHelper.getErrorMessages(e)
};
showErrors();
} else if (efs.editAll.state && efs.editAll.isFocusField(field)) {
$timeout(function () {
var focusElement = element.find('.focus-input');
focusElement.length && focusElement.focus()[0].select();
});
}
});
}
};
}
inplaceEditorEditPane.$inject = ['WorkPackageFieldService', 'EditableFieldsState', 'FocusHelper',
'$timeout'];
$scope.$watch('editableFieldsState.editAll.state', function(state) {
$scope.fieldController.isEditing = state;
$scope.fieldController.lockFocus = true;
!state && $scope.fieldController.updateWriteValue();
});
},
link: function(scope, element, attrs, fieldController) {
scope.fieldController = fieldController;
scope.editableFieldsState = EditableFieldsState;
function InplaceEditorEditPaneController($scope, $element, $location, $timeout, $q, $rootScope,
WorkPackageService, EditableFieldsState, ApiHelper, NotificationsService) {
scope.editPaneController.isRequired = function() {
return WorkPackageFieldService.isRequired(
EditableFieldsState.workPackage,
this.field
);
};
var showErrors = function() {
var errors = EditableFieldsState.errors;
if (_.isEmpty(_.keys(errors))) {
return;
}
var errorMessages = _.flatten(_.map(errors), true);
NotificationsService.addError(I18n.t('js.label_validation_error'), errorMessages);
};
scope.$watchCollection('editableFieldsState.workPackage.form', function(form) {
var strategy = WorkPackageFieldService.getInplaceEditStrategy(
EditableFieldsState.workPackage,
fieldController.field
);
var vm = this;
if (fieldController.field === 'date' && strategy === 'date') {
form.pendingChanges = EditableFieldsState.getPendingFormChanges();
form.pendingChanges['startDate'] =
form.pendingChanges['dueDate'] =
fieldController.writeValue ? fieldController.writeValue['dueDate'] : null;
}
// go full retard
var uploadPendingAttachments = function(wp) {
$rootScope.$broadcast('uploadPendingAttachments', wp);
};
if (strategy !== scope.strategy) {
scope.strategy = strategy;
scope.templateUrl = '/templates/inplace-edit/edit/fields/' +
scope.strategy + '.html';
fieldController.updateWriteValue();
}
});
// Propagate submission to all active fields
// not contained in the workPackage.form (e.g., comment)
this.submit = function(notify) {
EditableFieldsState.save(notify, function() {
// Clears the location hash, as we're now
// scrolling to somewhere else
$location.hash(null);
$timeout(function() {
$element[0].scrollIntoView(false);
});
});
};
this.submitField = function(notify) {
var submit = $q.defer();
var fieldController = $scope.fieldController;
var pendingFormChanges = EditableFieldsState.getPendingFormChanges();
var detectedViolations = [];
var handleFailure = function(e) {
setFailure(e);
submit.reject(e);
};
scope.focusInput = function() {
$timeout(function() {
var inputElement = element.find('.focus-input');
FocusHelper.focus(inputElement);
inputElement.triggerHandler('keyup');
scope.editPaneController.markActive();
inputElement.off('focus.inplace').on('focus.inplace', function() {
// ♥♥♥ angular ♥♥♥
scope.$apply(function() {
scope.editPaneController.markActive();
pendingFormChanges[fieldController.field] = fieldController.writeValue;
if (vm.editForm.$invalid) {
var acknowledgedValidationErrors = Object.keys(vm.editForm.$error);
acknowledgedValidationErrors.forEach(function(error) {
if (vm.editForm.$error[error]) {
detectedViolations.push(I18n.t('js.inplace.errors.' + error, {
field: fieldController.getLabel()
}));
}
});
submit.reject();
}
if (detectedViolations.length) {
EditableFieldsState.errors = EditableFieldsState.errors || {};
EditableFieldsState.errors[fieldController.field] = detectedViolations.join(' ');
showErrors();
submit.reject();
} else {
fieldController.state.isBusy = true;
WorkPackageService.loadWorkPackageForm(EditableFieldsState.workPackage).then(
function(form) {
EditableFieldsState.workPackage.form = form;
if (_.isEmpty(form.embedded.validationErrors.props)) {
var result = WorkPackageService.updateWorkPackage(
EditableFieldsState.workPackage,
notify
);
result.then(angular.bind(this, function(updatedWorkPackage) {
submit.resolve();
$scope.$emit('workPackageUpdatedInEditor', updatedWorkPackage);
$scope.$on('workPackageRefreshed', function() {
fieldController.state.isBusy = false;
fieldController.isEditing = false;
fieldController.updateWriteValue();
});
uploadPendingAttachments(updatedWorkPackage);
})).catch(handleFailure);
} else {
afterError();
submit.reject();
EditableFieldsState.errors = {};
_.forEach(form.embedded.validationErrors.props, function(error, field) {
if(field === 'startDate' || field === 'dueDate') {
EditableFieldsState.errors['date'] = error.message;
} else {
EditableFieldsState.errors[field] = error.message;
}
});
});
};
}
}).catch(handleFailure);
}
if (!EditableFieldsState.forcedEditState) {
element.bind('keydown keypress', function(e) {
if (e.keyCode === 27) {
scope.$apply(function() {
scope.editPaneController.discardEditing();
});
}
});
}
return submit.promise;
};
scope.$watch('fieldController.writeValue', function(writeValue) {
if (scope.fieldController.isEditing) {
var pendingChanges = EditableFieldsState.getPendingFormChanges();
pendingChanges[scope.fieldController.field] = writeValue;
}
}, true);
scope.$on('workPackageRefreshed', function() {
scope.editPaneController.discardEditing();
});
this.discardEditing = function() {
$scope.fieldController.isEditing = false;
delete EditableFieldsState.submissionPromises['work_package'];
delete EditableFieldsState.getPendingFormChanges()[$scope.fieldController.field];
$scope.fieldController.updateWriteValue();
if (
EditableFieldsState.errors &&
EditableFieldsState.errors.hasOwnProperty($scope.fieldController.field)
) {
delete EditableFieldsState.errors[$scope.fieldController.field];
}
};
scope.$watch('fieldController.isEditing', function(isEditing) {
var efs = EditableFieldsState, field = fieldController.field;
this.isActive = function() {
return EditableFieldsState.isActiveField($scope.fieldController.field);
};
if (isEditing && !efs.editAll.state && !efs.forcedEditState) {
scope.focusInput();
this.markActive = function() {
EditableFieldsState.submissionPromises['work_package'] = {
field: $scope.fieldController.field,
thePromise: this.submitField,
prepend: true,
};
EditableFieldsState.currentField = $scope.fieldController.field;
};
} else if (efs.editAll.state && efs.editAll.isFocusField(field)) {
$timeout(function () {
var focusElement = element.find('.focus-input');
focusElement.length && focusElement.focus()[0].select();
});
}
});
}
function afterError() {
$scope.fieldController.state.isBusy = false;
$scope.focusInput();
}
function setFailure(e) {
afterError();
EditableFieldsState.errors = {
'_common': ApiHelper.getErrorMessages(e)
};
showErrors();
}
]);
$scope.$watch('editableFieldsState.editAll.state', function(state) {
$scope.fieldController.isEditing = state;
$scope.fieldController.lockFocus = true;
!state && $scope.fieldController.updateWriteValue();
});
}
InplaceEditorEditPaneController.$inject = ['$scope', '$element', '$location', '$timeout', '$q',
'$rootScope', 'WorkPackageService', 'EditableFieldsState', 'ApiHelper', 'NotificationsService'];

@ -26,29 +26,37 @@
// See doc/COPYRIGHT.rdoc for more details.
// ++
angular.module('openproject.inplace-edit').directive('inplaceDisplaySpentTime', [
'EditableFieldsState', function(EditableFieldsState) {
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {},
require: ['^inplaceEditorDisplayPane', '^workPackageField'],
templateUrl: '/components/inplace-edit/directives/field-display/display-spent-time/display-spent-time.directive.html',
controller: function() {
this.isLinkViewable = function() {
return EditableFieldsState.workPackage.links.timeEntries;
};
angular
.module('openproject.inplace-edit')
.directive('inplaceDisplaySpentTime', inplaceDisplaySpentTime);
this.getPath = function() {
return EditableFieldsState.workPackage.links.timeEntries.href;
};
},
controllerAs: 'customEditorController',
link: function(scope, element, attrs, controllers) {
scope.displayPaneController = controllers[0];
scope.fieldController = controllers[1];
}
};
}
]);
function inplaceDisplaySpentTime() {
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {},
require: ['^inplaceEditorDisplayPane', '^workPackageField'],
templateUrl: '/components/inplace-edit/directives/field-display/display-spent-time/' +
'display-spent-time.directive.html',
controller: InplaceDisplaySpentTimeController,
controllerAs: 'customEditorController',
link: function(scope, element, attrs, controllers) {
scope.displayPaneController = controllers[0];
scope.fieldController = controllers[1];
}
};
}
function InplaceDisplaySpentTimeController(EditableFieldsState) {
this.isLinkViewable = function() {
return EditableFieldsState.workPackage.links.timeEntries;
};
this.getPath = function() {
return EditableFieldsState.workPackage.links.timeEntries.href;
};
}
InplaceDisplaySpentTimeController.$inject = ['EditableFieldsState'];

@ -26,32 +26,40 @@
// See doc/COPYRIGHT.rdoc for more details.
// ++
angular.module('openproject.inplace-edit').directive('inplaceDisplayUser', [
'PathHelper', function(PathHelper) {
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {},
require: '^inplaceEditorDisplayPane',
templateUrl: '/components/inplace-edit/directives/field-display/display-user/display-user.directive.html',
controller: ['$scope', function($scope) {
this.userPath = PathHelper.staticUserPath;
this.getUser = function() {
return $scope.inplaceEditorDisplayPane.getReadValue();
};
this.getUserName = function() {
var user = this.getUser();
if (user && user.props && (user.props.firstName || user.props.lastName)) {
return user.props.firstName + ' ' + user.props.lastName;
}
return null;
};
}],
controllerAs: 'customEditorController',
link: function(scope, element, attrs, inplaceEditorDisplayPane) {
scope.inplaceEditorDisplayPane = inplaceEditorDisplayPane;
}
};
}
]);
angular
.module('openproject.inplace-edit')
.directive('inplaceDisplayUser', inplaceDisplayUser);
function inplaceDisplayUser() {
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {},
require: '^inplaceEditorDisplayPane',
templateUrl: '/components/inplace-edit/directives/field-display/display-user/' +
'display-user.directive.html',
controller: InplaceDisplayUserController,
controllerAs: 'customEditorController',
link: function(scope, element, attrs, inplaceEditorDisplayPane) {
scope.inplaceEditorDisplayPane = inplaceEditorDisplayPane;
}
};
}
function InplaceDisplayUserController($scope, PathHelper) {
this.userPath = PathHelper.staticUserPath;
this.getUser = function() {
return $scope.inplaceEditorDisplayPane.getReadValue();
};
this.getUserName = function() {
var user = this.getUser();
if (user && user.props && (user.props.firstName || user.props.lastName)) {
return user.props.firstName + ' ' + user.props.lastName;
}
return null;
};
}
InplaceDisplayUserController.$inject = ['$scope', 'PathHelper'];

@ -26,27 +26,34 @@
// See doc/COPYRIGHT.rdoc for more details.
// ++
angular.module('openproject.inplace-edit').directive('inplaceDisplayVersion', [
'EditableFieldsState', 'PathHelper',
function(EditableFieldsState, PathHelper) {
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {},
require: '^inplaceEditorDisplayPane',
templateUrl: '/components/inplace-edit/directives/field-display/display-version/display-version.directive.html',
controller: ['$scope', function($scope) {
this.pathHelper = PathHelper;
this.isVersionLinkViewable = function() {
var version = $scope.displayPaneController.getReadValue();
return version.links.definingProject && version.links.definingProject.href;
}
}],
controllerAs: 'customEditorController',
link: function(scope, element, attrs, displayPaneController) {
scope.displayPaneController = displayPaneController;
}
};
angular
.module('openproject.inplace-edit')
.directive('inplaceDisplayVersion', inplaceDisplayVersion);
function inplaceDisplayVersion() {
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {},
require: '^inplaceEditorDisplayPane',
templateUrl: '/components/inplace-edit/directives/field-display/display-version/' +
'display-version.directive.html',
controller: InplaceDisplayVersionController,
controllerAs: 'customEditorController',
link: function(scope, element, attrs, displayPaneController) {
scope.displayPaneController = displayPaneController;
}
};
}
function InplaceDisplayVersionController($scope, PathHelper) {
this.pathHelper = PathHelper;
this.isVersionLinkViewable = function() {
var version = $scope.displayPaneController.getReadValue();
return version.links.definingProject && version.links.definingProject.href;
}
]);
}
InplaceDisplayVersionController.$inject = ['$scope', 'PathHelper'];

@ -26,126 +26,129 @@
// See doc/COPYRIGHT.rdoc for more details.
// ++
angular.module('openproject.inplace-edit').directive('inplaceEditorDateRange', [
'TimezoneService', 'ConfigurationService', 'I18n', '$timeout', 'WorkPackageFieldService',
'EditableFieldsState', 'Datepicker',
function(TimezoneService, ConfigurationService, I18n, $timeout, WorkPackageFieldService,
EditableFieldsState, Datepicker) {
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {},
require: '^workPackageField',
templateUrl: '/components/inplace-edit/directives/field-edit/edit-date-range/edit-date-range.directive.html',
controller: function() {
},
controllerAs: 'customEditorController',
link: function(scope, element, attrs, fieldController) {
var customDateFormat = 'YYYY-MM-DD';
function getTitle(labelName) {
return I18n.t('js.inplace.button_edit', {
attribute: WorkPackageFieldService.getLabel(
EditableFieldsState.workPackage,
labelName
)
});
}
angular
.module('openproject.inplace-edit')
.directive('inplaceEditorDateRange', inplaceEditorDateRange);
scope.startDate = fieldController.writeValue.startDate;
scope.endDate = fieldController.writeValue.dueDate;
var form = element.parents('.inplace-edit--form'),
inputStart = element.find('.inplace-edit--date-range-start-date'),
inputEnd = element.find('.inplace-edit--date-range-end-date'),
divStart = element.find('.inplace-edit--date-range-start-date-picker'),
divEnd = element.find('.inplace-edit--date-range-end-date-picker'),
startDatepicker, endDatepicker;
function inplaceEditorDateRange(TimezoneService, I18n, $timeout, WorkPackageFieldService,
EditableFieldsState, Datepicker) {
scope.startDateIsChanged = scope.endDateIsChanged = false;
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {},
require: '^workPackageField',
templateUrl: '/components/inplace-edit/directives/field-edit/edit-date-range/' +
'edit-date-range.directive.html',
if (scope.endDate) {
scope.endDate = TimezoneService.formattedISODate(scope.endDate);
}
if (scope.startDate) {
scope.startDate = TimezoneService.formattedISODate(scope.startDate);
}
controller: function() {},
controllerAs: 'customEditorController',
scope.execute = function() {
form.scope().editPaneController.submit(false);
};
startDatepicker = new Datepicker(divStart, inputStart, scope.startDate);
endDatepicker = new Datepicker(divEnd, inputEnd, scope.endDate);
startDatepicker.onChange = function(date) {
scope.startDate = fieldController.writeValue.startDate = date;
if (startDatepicker.prevDate.isAfter(endDatepicker.prevDate)) {
scope.startDateIsChanged = true;
scope.endDate = fieldController.writeValue.dueDate = scope.startDate;
endDatepicker.setDate(scope.endDate);
}
};
scope.onStartEdit = function() {
scope.startDateIsChanged = scope.endDateIsChanged = false;
startDatepicker.onEdit();
};
endDatepicker.onChange = function(date) {
scope.endDate = fieldController.writeValue.dueDate = date;
if (endDatepicker.prevDate.isBefore(startDatepicker.prevDate)) {
scope.endDateIsChanged = true;
scope.startDate = fieldController.writeValue.startDate = scope.endDate;
startDatepicker.setDate(scope.startDate);
}
};
scope.onEndEdit = function() {
scope.startDateIsChanged = scope.endDateIsChanged = false;
endDatepicker.onEdit();
};
startDatepicker.onDone = endDatepicker.onDone = function() {
form.scope().editPaneController.discardEditing();
};
$timeout(function() {
EditableFieldsState.editAll.state || startDatepicker.focus();
});
link: function(scope, element, attrs, fieldController) {
var customDateFormat = 'YYYY-MM-DD';
startDatepicker.textbox.on('click focusin', function() {
if (divStart.is(':hidden') || divEnd.is(':visible')) {
endDatepicker.hide();
startDatepicker.show();
}
scope.startDateIsChanged = scope.endDateIsChanged = false;
}).attr({
'placeholder': customDateFormat,
'aria-label': customDateFormat,
'title': getTitle('startDate')
function getTitle(labelName) {
return I18n.t('js.inplace.button_edit', {
attribute: WorkPackageFieldService.getLabel(
EditableFieldsState.workPackage,
labelName
)
});
}
endDatepicker.textbox.on('click focusin', function() {
if (divEnd.is(':hidden') || divStart.is(':visible')) {
endDatepicker.show();
startDatepicker.hide();
}
scope.startDateIsChanged = scope.endDateIsChanged = false;
}).attr({
'placeholder': customDateFormat,
'aria-label': customDateFormat,
'title': getTitle('dueDate')
});
scope.startDate = fieldController.writeValue.startDate;
scope.endDate = fieldController.writeValue.dueDate;
var form = element.parents('.inplace-edit--form'),
inputStart = element.find('.inplace-edit--date-range-start-date'),
inputEnd = element.find('.inplace-edit--date-range-end-date'),
divStart = element.find('.inplace-edit--date-range-start-date-picker'),
divEnd = element.find('.inplace-edit--date-range-end-date-picker'),
startDatepicker, endDatepicker;
angular.element('.work-packages--details-content').on('click', function(e) {
var target = angular.element(e.target);
if (!target.is('.inplace-edit--date-range input') &&
target.parents('.hasDatepicker').length <= 0 &&
target.parents('.ui-datepicker-header').length <= 0) {
startDatepicker.hide();
endDatepicker.hide();
}
});
scope.startDateIsChanged = scope.endDateIsChanged = false;
if (scope.endDate) {
scope.endDate = TimezoneService.formattedISODate(scope.endDate);
}
if (scope.startDate) {
scope.startDate = TimezoneService.formattedISODate(scope.startDate);
}
};
}
]);
scope.execute = function() {
form.scope().editPaneController.submit(false);
};
startDatepicker = new Datepicker(divStart, inputStart, scope.startDate);
endDatepicker = new Datepicker(divEnd, inputEnd, scope.endDate);
startDatepicker.onChange = function(date) {
scope.startDate = fieldController.writeValue.startDate = date;
if (startDatepicker.prevDate.isAfter(endDatepicker.prevDate)) {
scope.startDateIsChanged = true;
scope.endDate = fieldController.writeValue.dueDate = scope.startDate;
endDatepicker.setDate(scope.endDate);
}
};
scope.onStartEdit = function() {
scope.startDateIsChanged = scope.endDateIsChanged = false;
startDatepicker.onEdit();
};
endDatepicker.onChange = function(date) {
scope.endDate = fieldController.writeValue.dueDate = date;
if (endDatepicker.prevDate.isBefore(startDatepicker.prevDate)) {
scope.endDateIsChanged = true;
scope.startDate = fieldController.writeValue.startDate = scope.endDate;
startDatepicker.setDate(scope.startDate);
}
};
scope.onEndEdit = function() {
scope.startDateIsChanged = scope.endDateIsChanged = false;
endDatepicker.onEdit();
};
startDatepicker.onDone = endDatepicker.onDone = function() {
form.scope().editPaneController.discardEditing();
};
$timeout(function() {
EditableFieldsState.editAll.state || startDatepicker.focus();
});
startDatepicker.textbox.on('click focusin', function() {
if (divStart.is(':hidden') || divEnd.is(':visible')) {
endDatepicker.hide();
startDatepicker.show();
}
scope.startDateIsChanged = scope.endDateIsChanged = false;
}).attr({
'placeholder': customDateFormat,
'aria-label': customDateFormat,
'title': getTitle('startDate')
});
endDatepicker.textbox.on('click focusin', function() {
if (divEnd.is(':hidden') || divStart.is(':visible')) {
endDatepicker.show();
startDatepicker.hide();
}
scope.startDateIsChanged = scope.endDateIsChanged = false;
}).attr({
'placeholder': customDateFormat,
'aria-label': customDateFormat,
'title': getTitle('dueDate')
});
angular.element('.work-packages--details-content').on('click', function(e) {
var target = angular.element(e.target);
if (!target.is('.inplace-edit--date-range input') &&
target.parents('.hasDatepicker').length <= 0 &&
target.parents('.ui-datepicker-header').length <= 0) {
startDatepicker.hide();
endDatepicker.hide();
}
});
}
};
}
inplaceEditorDateRange.$inject = ['TimezoneService', 'I18n', '$timeout', 'WorkPackageFieldService',
'EditableFieldsState', 'Datepicker'];

@ -26,76 +26,77 @@
// See doc/COPYRIGHT.rdoc for more details.
// ++
angular.module('openproject.inplace-edit').directive('inplaceEditorDate', [
'WorkPackageFieldService', 'EditableFieldsState', 'TimezoneService', 'ConfigurationService',
'I18n', '$timeout', 'Datepicker',
angular
.module('openproject.inplace-edit')
.directive('inplaceEditorDate', inplaceEditorDate);
function(WorkPackageFieldService, EditableFieldsState, TimezoneService, ConfigurationService,
I18n, $timeout, Datepicker) {
function inplaceEditorDate(EditableFieldsState, TimezoneService, $timeout, Datepicker) {
var parseISODate = TimezoneService.parseISODate,
customDateFormat = 'YYYY-MM-DD',
customFormattedDate = function(date) {
return parseISODate(date).format(customDateFormat);
};
var parseISODate = TimezoneService.parseISODate,
customDateFormat = 'YYYY-MM-DD',
customFormattedDate = function(date) {
return parseISODate(date).format(customDateFormat);
};
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {},
require: '^workPackageField',
templateUrl: '/components/inplace-edit/directives/field-edit/edit-date/' +
'edit-date.directive.html',
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {},
require: '^workPackageField',
templateUrl: '/components/inplace-edit/directives/field-edit/edit-date/edit-date.directive.html',
controller: function() {
},
controllerAs: 'customEditorController',
link: function(scope, element, attrs, fieldController) {
scope.fieldController = fieldController;
var form = element.parents('.inplace-edit--form'),
input = element.find('.inplace-edit--date'),
datepickerContainer = element.find('.inplace-edit--date-picker'),
datepicker;
controller: function() {},
controllerAs: 'customEditorController',
scope.execute = function() {
form.scope().editPaneController.submit(false);
};
link: function(scope, element, attrs, fieldController) {
scope.fieldController = fieldController;
var form = element.parents('.inplace-edit--form'),
input = element.find('.inplace-edit--date'),
datepickerContainer = element.find('.inplace-edit--date-picker'),
datepicker;
if(scope.fieldController.writeValue) {
scope.fieldController.writeValue = customFormattedDate(scope.fieldController.writeValue);
}
scope.execute = function() {
form.scope().editPaneController.submit(false);
};
datepicker = new Datepicker(datepickerContainer, input, scope.fieldController.writeValue);
datepicker.onChange = function(date) {
scope.fieldController.writeValue = date;
};
scope.onEdit = function() {
datepicker.onEdit();
};
datepicker.onDone = function() {
form.scope().editPaneController.discardEditing();
};
if(scope.fieldController.writeValue) {
scope.fieldController.writeValue = customFormattedDate(scope.fieldController.writeValue);
}
datepicker.textbox.attr({
'placeholder': '-',
'aria-label': customDateFormat
});
datepicker = new Datepicker(datepickerContainer, input, scope.fieldController.writeValue);
datepicker.onChange = function(date) {
scope.fieldController.writeValue = date;
};
scope.onEdit = function() {
datepicker.onEdit();
};
datepicker.onDone = function() {
form.scope().editPaneController.discardEditing();
};
scope.showDatepicker = function() {
datepicker.show();
};
datepicker.textbox.attr({
'placeholder': '-',
'aria-label': customDateFormat
});
$timeout(function() {
EditableFieldsState.editAll.state || datepicker.focus();
});
scope.showDatepicker = function() {
datepicker.show();
};
angular.element('.work-packages--details-content').on('click', function(e) {
var target = angular.element(e.target);
if(!target.is('.inplace-edit--date input') &&
target.parents('.inplace-edit--date .hasDatepicker').length <= 0 &&
target.parents('.ui-datepicker-header').length <= 0) {
datepicker.hide();
}
});
}
};
}]);
$timeout(function() {
EditableFieldsState.editAll.state || datepicker.focus();
});
angular.element('.work-packages--details-content').on('click', function(e) {
var target = angular.element(e.target);
if(!target.is('.inplace-edit--date input') &&
target.parents('.inplace-edit--date .hasDatepicker').length <= 0 &&
target.parents('.ui-datepicker-header').length <= 0) {
datepicker.hide();
}
});
}
};
}
inplaceEditorDate.$inject = ['EditableFieldsState', 'TimezoneService', '$timeout', 'Datepicker'];

@ -26,74 +26,83 @@
// See doc/COPYRIGHT.rdoc for more details.
// ++
angular.module('openproject.inplace-edit').directive('inplaceEditorDropDown', [
'WorkPackageFieldService', 'WorkPackageFieldConfigurationService', 'EditableFieldsState', 'I18n',
'$timeout', '$q', 'FocusHelper',
function(WorkPackageFieldService, WorkPackageFieldConfigurationService, EditableFieldsState,
I18n, $timeout, $q, FocusHelper) {
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {},
require: '^workPackageField',
templateUrl: '/components/inplace-edit/directives/field-edit/edit-drop-down/edit-drop-down.directive.html',
controller: function() {
this.allowedValues = [];
this.nullValueLabel = I18n.t('js.inplace.null_value_label');
this.updateAllowedValues = function(field) {
var customEditorController = this;
return $q(function(resolve) {
WorkPackageFieldService.getAllowedValues(
EditableFieldsState.workPackage,
field
).then(function(values) {
var sorting = WorkPackageFieldConfigurationService
.getDropdownSortingStrategy(field);
if (sorting !== null) {
values = _.sortBy(values, sorting);
}
if (!WorkPackageFieldService.isRequired(EditableFieldsState.workPackage,
field)) {
var arrayWithEmptyOption = [{
href: null,
name: I18n.t('js.inplace.clear_value_label')
}];
values = arrayWithEmptyOption.concat(values);
}
customEditorController.allowedValues = values;
resolve();
});
});
};
},
controllerAs: 'customEditorController',
link: function(scope, element, attrs, fieldController) {
var selected = WorkPackageFieldService.format(
EditableFieldsState.workPackage,
fieldController.field
);
scope.fieldController = fieldController;
scope.customEditorController.selected = selected && selected.props && selected.props.name;
scope.fieldController.state.isBusy = true;
scope.customEditorController.updateAllowedValues(fieldController.field).then(function() {
fieldController.state.isBusy = false;
if (!EditableFieldsState.forcedEditState) {
EditableFieldsState.editAll.state || FocusHelper.focusUiSelect(element);
angular
.module('openproject.inplace-edit')
.directive('inplaceEditorDropDown', inplaceEditorDropDown);
function inplaceEditorDropDown(WorkPackageFieldService, EditableFieldsState, FocusHelper) {
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {},
require: '^workPackageField',
templateUrl: '/components/inplace-edit/directives/field-edit/edit-drop-down/' +
'edit-drop-down.directive.html',
controller: InplaceEditorDropDownController,
controllerAs: 'customEditorController',
link: function(scope, element, attrs, fieldController) {
var selected = WorkPackageFieldService.format(
EditableFieldsState.workPackage,
fieldController.field
);
scope.fieldController = fieldController;
scope.customEditorController.selected = selected && selected.props && selected.props.name;
scope.fieldController.state.isBusy = true;
scope.customEditorController.updateAllowedValues(fieldController.field).then(function() {
fieldController.state.isBusy = false;
if (!EditableFieldsState.forcedEditState) {
EditableFieldsState.editAll.state || FocusHelper.focusUiSelect(element);
}
});
}
};
}
inplaceEditorDropDown.$inject = ['WorkPackageFieldService', 'EditableFieldsState', 'FocusHelper'];
function InplaceEditorDropDownController($q, I18n, WorkPackageFieldService,
WorkPackageFieldConfigurationService, EditableFieldsState) {
this.allowedValues = [];
this.nullValueLabel = I18n.t('js.inplace.null_value_label');
this.updateAllowedValues = function(field) {
var customEditorController = this;
return $q(function(resolve) {
WorkPackageFieldService.getAllowedValues(
EditableFieldsState.workPackage,
field
).then(function(values) {
var sorting = WorkPackageFieldConfigurationService
.getDropdownSortingStrategy(field);
if (sorting !== null) {
values = _.sortBy(values, sorting);
}
if (!WorkPackageFieldService.isRequired(EditableFieldsState.workPackage,
field)) {
var arrayWithEmptyOption = [{
href: null,
name: I18n.t('js.inplace.clear_value_label')
}];
values = arrayWithEmptyOption.concat(values);
}
customEditorController.allowedValues = values;
resolve();
});
}
};
}]);
});
};
}
InplaceEditorDropDownController.$inject = ['$q', 'I18n', 'WorkPackageFieldService',
'WorkPackageFieldConfigurationService', 'EditableFieldsState'];

@ -26,16 +26,23 @@
// See doc/COPYRIGHT.rdoc for more details.
// ++
angular.module('openproject.inplace-edit').directive('inplaceEditorDuration', function() {
angular
.module('openproject.inplace-edit')
.directive('inplaceEditorDuration', inplaceEditorDuration);
function inplaceEditorDuration() {
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {},
require: '^workPackageField',
templateUrl: '/components/inplace-edit/directives/field-edit/edit-duration/edit-duration.directive.html',
templateUrl: '/components/inplace-edit/directives/field-edit/edit-duration/' +
'edit-duration.directive.html',
controllerAs: 'customEditorController',
controller: function() {},
link: function(scope, element, attrs, fieldController) {
scope.fieldController = fieldController;
if (fieldController.writeValue === null) {
@ -60,4 +67,4 @@ angular.module('openproject.inplace-edit').directive('inplaceEditorDuration', fu
});
}
};
});
}

@ -26,73 +26,82 @@
// See doc/COPYRIGHT.rdoc for more details.
// ++
angular.module('openproject.inplace-edit').directive('inplaceEditorWikiTextarea', [
'TextileService', 'EditableFieldsState', '$sce', 'AutoCompleteHelper', '$timeout',
angular
.module('openproject.inplace-edit')
.directive('inplaceEditorWikiTextarea', inplaceEditorWikiTextarea);
function(TextileService, EditableFieldsState, $sce, AutoCompleteHelper, $timeout) {
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {},
templateUrl: '/components/inplace-edit/directives/field-edit/edit-wiki-textarea/edit-wiki-textarea.directive.html',
controller: function($scope) {
this.isPreview = false;
this.previewHtml = '';
this.autocompletePath = '/work_packages/auto_complete.json';
function inplaceEditorWikiTextarea(AutoCompleteHelper, $timeout) {
return {
restrict: 'E',
transclude: true,
replace: true,
scope: {},
templateUrl: '/components/inplace-edit/directives/field-edit/edit-wiki-textarea/' +
'edit-wiki-textarea.directive.html',
this.togglePreview = function() {
this.isPreview = !this.isPreview;
this.previewHtml = '';
// $scope.error = null;
if (!this.isPreview) {
return;
}
$scope.fieldController.state.isBusy = true;
TextileService
.renderWithWorkPackageContext(
EditableFieldsState.workPackage.form,
$scope.fieldController.writeValue.raw)
.then(angular.bind(this, function(r) {
this.previewHtml = $sce.trustAsHtml(r.data);
$scope.fieldController.state.isBusy = false;
}), angular.bind(this, function() {
this.isPreview = false;
$scope.fieldController.state.isBusy = false;
}));
};
},
controllerAs: 'customEditorController',
link: function(scope, element) {
scope.fieldController = scope.$parent.fieldController;
$timeout(function() {
AutoCompleteHelper.enableTextareaAutoCompletion(element.find('textarea'));
// set as dirty for the script to show a confirm on leaving the page
element.find('textarea').data('changed', true);
});
controller: InplaceEditorWikiTextareaController,
controllerAs: 'customEditorController',
link: function(scope, element) {
scope.fieldController = scope.$parent.fieldController;
$timeout(function() {
AutoCompleteHelper.enableTextareaAutoCompletion(element.find('textarea'));
// set as dirty for the script to show a confirm on leaving the page
element.find('textarea').data('changed', true);
});
// Listen to elastic textara expansion to always make the bottom
// of that textarea visible.
// Otherwise, when expanding the textarea with newlines,
// its bottom border may no longer be visible
scope.$on('elastic:resize', function(event, textarea, oldHeight, newHeight) {
var containerHeight = element.scrollParent().height();
// We can only help the user if the whole textarea fits in the screen
if (newHeight >= (containerHeight - (containerHeight / 5))) {
return;
}
// Listen to elastic textara expansion to always make the bottom
// of that textarea visible.
// Otherwise, when expanding the textarea with newlines,
// its bottom border may no longer be visible
scope.$on('elastic:resize', function(event, textarea, oldHeight, newHeight) {
var containerHeight = element.scrollParent().height();
$timeout(function() {
var controls = element.closest('.inplace-edit--form ')
.find('.inplace-edit--controls');
// We can only help the user if the whole textarea fits in the screen
if (newHeight >= (containerHeight - (containerHeight / 5))) {
return;
if (!controls.isVisibleWithin(controls.scrollParent())) {
controls[0].scrollIntoView(false);
}
}, 200);
});
}
};
}
inplaceEditorWikiTextarea.$inject = ['AutoCompleteHelper', '$timeout'];
function InplaceEditorWikiTextareaController($scope,$sce, TextileService, EditableFieldsState) {
this.isPreview = false;
this.previewHtml = '';
this.autocompletePath = '/work_packages/auto_complete.json';
$timeout(function() {
var controls = element.closest('.inplace-edit--form ')
.find('.inplace-edit--controls');
this.togglePreview = function() {
this.isPreview = !this.isPreview;
this.previewHtml = '';
// $scope.error = null;
if (!this.isPreview) {
return;
}
if (!controls.isVisibleWithin(controls.scrollParent())) {
controls[0].scrollIntoView(false);
}
}, 200);
});
}
};
}
]);
$scope.fieldController.state.isBusy = true;
TextileService.renderWithWorkPackageContext(EditableFieldsState.workPackage.form,
$scope.fieldController.writeValue.raw)
.then(angular.bind(this, function(r) {
this.previewHtml = $sce.trustAsHtml(r.data);
$scope.fieldController.state.isBusy = false;
}), angular.bind(this, function() {
this.isPreview = false;
$scope.fieldController.state.isBusy = false;
}));
};
}
InplaceEditorWikiTextareaController.$inject = ['$scope', '$sce', 'TextileService',
'EditableFieldsState'];

@ -26,30 +26,37 @@
// See doc/COPYRIGHT.rdoc for more details.
// ++
angular.module('openproject.inplace-edit').directive('inplaceEditorMainPane', function() {
angular
.module('openproject.inplace-edit')
.directive('inplaceEditorMainPane', inplaceEditorMainPane);
function inplaceEditorMainPane() {
return {
transclude: true,
replace: true,
scope: false,
templateUrl: '/components/inplace-edit/directives/main-pane/main-pane.directive.html',
controller: function($scope, $timeout) {
// controller is invoked before linker
$timeout(function() {
var fieldController = $scope.fieldController;
this.saveTitle = I18n.t(
'js.inplace.button_save',
{ attribute: fieldController.field }
);
this.saveAndSendTitle = I18n.t(
'js.inplace.button_save_and_send',
{ attribute: fieldController.field }
);
this.cancelTitle = I18n.t(
'js.inplace.button_cancel',
{ attribute: fieldController.field }
);
});
},
controller: InplaceEditorMainPaneController,
controllerAs: 'mainPaneController'
};
});
}
function InplaceEditorMainPaneController($scope, $timeout) {
// controller is invoked before linker
$timeout(function() {
var fieldController = $scope.fieldController;
this.saveTitle = I18n.t(
'js.inplace.button_save',
{ attribute: fieldController.field }
);
this.saveAndSendTitle = I18n.t(
'js.inplace.button_save_and_send',
{ attribute: fieldController.field }
);
this.cancelTitle = I18n.t(
'js.inplace.button_cancel',
{ attribute: fieldController.field }
);
});
}
InplaceEditorMainPaneController.$inject = ['$scope', '$timeout'];

@ -26,48 +26,54 @@
// See doc/COPYRIGHT.rdoc for more details.
//++
angular.module('openproject.inplace-edit').directive('workPackageField',
function() {
return {
restrict: 'E',
replace: true,
controllerAs: 'fieldController',
bindToController: true,
templateUrl: '/components/inplace-edit/directives/work-package-field/work-package-field.directive.html',
scope: {
field: '='
},
controller: function ($scope, EditableFieldsState, WorkPackageFieldService) {
this.state = EditableFieldsState;
angular
.module('openproject.inplace-edit')
.directive('workPackageField', workPackageField);
this.isEditable = function() {
return WorkPackageFieldService.isEditable(EditableFieldsState.workPackage, this.field);
};
function workPackageField() {
return {
restrict: 'E',
replace: true,
controllerAs: 'fieldController',
bindToController: true,
templateUrl: '/components/inplace-edit/directives/work-package-field/work-package-field.directive.html',
scope: {
field: '='
},
controller: WorkPackageFieldController
};
}
this.isEmpty = function() {
return WorkPackageFieldService.isEmpty(EditableFieldsState.workPackage, this.field);
};
function WorkPackageFieldController($scope, EditableFieldsState, WorkPackageFieldService) {
this.state = EditableFieldsState;
this.getLabel = function() {
return WorkPackageFieldService.getLabel(EditableFieldsState.workPackage, this.field);
};
this.isEditable = function() {
return WorkPackageFieldService.isEditable(EditableFieldsState.workPackage, this.field);
};
this.updateWriteValue = function() {
this.writeValue = EditableFieldsState.editAll.getFieldValue(this.field) || _.cloneDeep(
WorkPackageFieldService.getValue(EditableFieldsState.workPackage, this.field));
};
this.isEmpty = function() {
return WorkPackageFieldService.isEmpty(EditableFieldsState.workPackage, this.field);
};
if (this.isEditable()) {
this.state.isBusy = false;
this.isEditing = this.state.forcedEditState;
this.updateWriteValue();
this.editTitle = I18n.t('js.inplace.button_edit', { attribute: this.getLabel() });
}
this.getLabel = function() {
return WorkPackageFieldService.getLabel(EditableFieldsState.workPackage, this.field);
};
$scope.$watch('fieldController.writeValue', angular.bind(this, function (newValue) {
EditableFieldsState.editAll.addFieldValue(this.field, newValue);
}));
}
};
this.updateWriteValue = function() {
this.writeValue = EditableFieldsState.editAll.getFieldValue(this.field) || _.cloneDeep(
WorkPackageFieldService.getValue(EditableFieldsState.workPackage, this.field));
};
if (this.isEditable()) {
this.state.isBusy = false;
this.isEditing = this.state.forcedEditState;
this.updateWriteValue();
this.editTitle = I18n.t('js.inplace.button_edit', { attribute: this.getLabel() });
}
);
$scope.$watch('fieldController.writeValue', angular.bind(this, function (newValue) {
EditableFieldsState.editAll.addFieldValue(this.field, newValue);
}));
}
WorkPackageFieldController.$inject = ['$scope', 'EditableFieldsState', 'WorkPackageFieldService'];

Loading…
Cancel
Save