From 169a7504900bb360159ae75d49b5537f68b2c9b4 Mon Sep 17 00:00:00 2001 From: Jonathan Niles Date: Tue, 26 Jul 2016 12:00:33 +0100 Subject: [PATCH] feat(bhGridDateEditor): implement ui-grid datepicker This commit implements a ui-grid datepicker directive for transaction date on the posting journal (although it can be used anywhere). The directive still needs lots of work to be fully functional, including validation and super user edit permission. --- client/src/js/directives/bhGridDateEditor.js | 177 ++++++++----------- client/src/js/services/grid/GridEditors.js | 26 ++- 2 files changed, 90 insertions(+), 113 deletions(-) diff --git a/client/src/js/directives/bhGridDateEditor.js b/client/src/js/directives/bhGridDateEditor.js index bed88abd29..2c99f4e10d 100644 --- a/client/src/js/directives/bhGridDateEditor.js +++ b/client/src/js/directives/bhGridDateEditor.js @@ -1,100 +1,46 @@ -/** forked from https://github.com/Joiler/ui-grid-edit-datepicker */ - -var app = angular.module('ui.grid.edit'); - -app.directive('uiGridEditDatepicker', ['$timeout', '$document', 'uiGridConstants', 'uiGridEditConstants', function($timeout, $document, uiGridConstants, uiGridEditConstants) { +/** originally forked from https://github.com/Joiler/ui-grid-edit-datepicker */ + +angular.module('ui.grid.edit') + .directive('uiGridEditDatepicker', uiGridEditDatePicker); + +uiGridEditDatePicker.$inject = [ + '$timeout', 'uiGridConstants', 'uiGridEditConstants' +]; + +/** + * @class uiGridEditDatePicker + * + * @description + * This directive implements a datepicker editor for angular's ui-grid. + */ +function uiGridEditDatePicker($timeout, uiGridConstants, uiGridEditConstants) { return { - template: function(element, attrs) { - var html = '
'; - return html; - }, + template : + '', require: ['?^uiGrid', '?^uiGridRenderContainer'], scope: true, - compile: function() { + compile: function () { return { - pre: function($scope, $elm, $attrs) { - - }, - post: function($scope, $elm, $attrs, controllers) { - var setCorrectPosition = function() { - var gridElement = $('.ui-grid-viewport'); - var gridPosition = { - width: gridElement.outerWidth(), - height: gridElement.outerHeight(), - offset: gridElement.offset() - }; - - var cellElement = $($elm); - var cellPosition = { - width: cellElement.outerWidth(), - height: cellElement.outerHeight(), - offset: cellElement.offset() - }; - - var datepickerElement = $('body > .dropdown-menu'); - var datepickerPosition = { - width: datepickerElement.outerWidth(), - height: datepickerElement.outerHeight() - }; - - var setCorrectTopPositionInGrid = function() { - var topPosition; - var freePixelsOnBottom = gridPosition.height - (cellPosition.offset.top - gridPosition.offset.top) - cellPosition.height; - var freePixelsOnTop = gridPosition.height - freePixelsOnBottom - cellPosition.height; - var requiredPixels = (datepickerPosition.height - cellPosition.height) / 2; - if (freePixelsOnBottom >= requiredPixels && freePixelsOnTop >= requiredPixels) { - topPosition = cellPosition.offset.top - requiredPixels + 10; - } else if (freePixelsOnBottom >= requiredPixels && freePixelsOnTop < requiredPixels) { - topPosition = cellPosition.offset.top - freePixelsOnTop + 10; - } else { - topPosition = gridPosition.height - datepickerPosition.height + gridPosition.offset.top - 20; - } - return topPosition; - }; - - var setCorrectTopPositionInWindow = function() { - var topPosition; - var windowHeight = window.innerHeight - 10; - - var freePixelsOnBottom = windowHeight - cellPosition.offset.top; - var freePixelsOnTop = windowHeight - freePixelsOnBottom - cellPosition.height; - var requiredPixels = (datepickerPosition.height - cellPosition.height) / 2; - if (freePixelsOnBottom >= requiredPixels && freePixelsOnTop >= requiredPixels) { - topPosition = cellPosition.offset.top - requiredPixels; - } else if (freePixelsOnBottom >= requiredPixels && freePixelsOnTop < requiredPixels) { - topPosition = cellPosition.offset.top - freePixelsOnTop; - } else { - topPosition = windowHeight - datepickerPosition.height - 10; - } - return topPosition; - }; - + post: function ($scope, $elm, $attrs, controllers) { - var newOffsetValues = {}; - - var isFreeOnRight = (gridPosition.width - (cellPosition.offset.left - gridPosition.offset.left) - cellPosition.width) > datepickerPosition.width; - if (isFreeOnRight) { - newOffsetValues.left = cellPosition.offset.left + cellPosition.width; - } else { - newOffsetValues.left = cellPosition.offset.left - datepickerPosition.width; - } - - if (datepickerPosition.height < gridPosition.height) { - newOffsetValues.top = setCorrectTopPositionInGrid(); - } else { - newOffsetValues.top = setCorrectTopPositionInWindow(); - } - - datepickerElement.offset(newOffsetValues); - datepickerElement.css('visibility', 'visible'); - }; - - $timeout(function() { - setCorrectPosition(); - }, 0); + // the original datepicker values + var originalValue = new Date($scope.row.entity[$scope.col.field]); + // bind datePickerValue to the correct value $scope.datePickerValue = new Date($scope.row.entity[$scope.col.field]); $scope.isOpen = true; + $scope.datepickerOptions = { initDate : new Date() }; + + var uiGridCtrl = controllers[0]; var renderContainerCtrl = controllers[1]; @@ -105,8 +51,7 @@ app.directive('uiGridEditDatepicker', ['$timeout', '$document', 'uiGridConstants if (!inDatepicker && evt.target.nodeName !== 'INPUT') { $scope.stopEdit(evt); } - } - else { + } else { $scope.stopEdit(evt); } }; @@ -116,10 +61,10 @@ app.directive('uiGridEditDatepicker', ['$timeout', '$document', 'uiGridConstants $scope.stopEdit(evt); }; - $scope.changeDate = function (evt) { - $scope.row.entity[$scope.col.field] = $scope.datePickerValue; - $scope.stopEdit(evt); - }; + // @todo - make sure this actually gets cleaned up when $scope is destroyed! + uiGridCtrl.grid.api.edit.on.cancelCellEdit($scope, function () { + $scope.stopEdit(); + }); $scope.$on(uiGridEditConstants.events.BEGIN_CELL_EDIT, function () { if (uiGridCtrl.grid.api.cellNav) { @@ -129,25 +74,32 @@ app.directive('uiGridEditDatepicker', ['$timeout', '$document', 'uiGridConstants } else { angular.element(document.querySelectorAll('.ui-grid-cell-contents')).on('click', onCellClick); } - angular.element(window).on('click', onWindowClick); - }); - $scope.$on('$destroy', function () { - angular.element(window).off('click', onWindowClick); - $('body > .dropdown-menu').remove(); + angular.element(window).on('click', onWindowClick); }); - $scope.stopEdit = function(evt) { + $scope.stopEdit = function (evt) { + $scope.row.entity[$scope.col.field] = $scope.datePickerValue; $scope.$emit(uiGridEditConstants.events.END_CELL_EDIT); }; - $elm.on('keydown', function(evt) { - switch (evt.keyCode) { - case uiGridConstants.keymap.ESC: - evt.stopPropagation(); - $scope.$emit(uiGridEditConstants.events.CANCEL_CELL_EDIT); - break; + // Make sure that the edit is canceled on the ESC key. The event is not + // propogated by uib-datepicker-popup. + // See: https://github.com/angular-ui/bootstrap/commit/000d6c309e7c2065576d535feaf6868ac06b75d0 + $scope.$watch('isOpen', function (isOpen) { + if (!isOpen) { + $timeout($scope.stopEdit, 0, false); } + }); + + // when we cancel the edit, we want to preserve the original value. + function cancelEdit() { + $scope.row.entity[$scope.col.field] = originalValue; + $scope.$emit(uiGridEditConstants.events.CANCEL_CELL_EDIT); + } + + // make sure we quit when we need to. + function handleKeydown(evt) { if (uiGridCtrl && uiGridCtrl.grid.api.cellNav) { evt.uiGridTargetRenderContainerId = renderContainerCtrl.containerId; if (uiGridCtrl.cellNav.handleKeyDown(evt) !== null) { @@ -163,10 +115,19 @@ app.directive('uiGridEditDatepicker', ['$timeout', '$document', 'uiGridConstants break; } } + return true; + } + + $elm.on('keydown', handleKeydown); + + $scope.$on('$destroy', function () { + angular.element(window).off('click', onWindowClick); + $('body > .dropdown-menu').remove(); + $elm.off('keydown', handleKeydown); }); } }; } }; -}]); +} diff --git a/client/src/js/services/grid/GridEditors.js b/client/src/js/services/grid/GridEditors.js index 37b9619d78..6c7a633ee1 100644 --- a/client/src/js/services/grid/GridEditors.js +++ b/client/src/js/services/grid/GridEditors.js @@ -16,12 +16,20 @@ function GridEditorService(util) { util.after(gridOptions, 'onRegisterApi', function onRegisterApi(api) { this.api = api; - this.api.edit.on.beginCellEdit(null, function beginCellEdit() { - if (gridOptions.authenticateEdits && !this.authenticated) { - this.requestUserAuthentication(); - } + this.api.edit.on.beginCellEdit(null, function beginCellEdit(row, column) { + console.log('[external] beginCellEdit'); + this.requestUserAuthentication(); }.bind(this)); + // notify that edits have been canceled + this.api.edit.on.cancelCellEdit(null, function cancelCellEdit(row, column) { + console.log('[external] cancelCellEdit'); + }); + + this.api.edit.on.afterCellEdit(null, function afterCellEdit(row, column) { + console.log('[external] afterCellEdit'); + }); + }.bind(this)); } @@ -34,7 +42,15 @@ function GridEditorService(util) { * user's edit session. It is currently unimplemented. */ GridEditors.prototype.requestUserAuthentication = function requestUserAuthentication() { - this.authenticated = true; + + // pretend we got authentication + //this.authenticated = true; + + // if we are not authenticated, cancel the edit + if (!this.authenticated) { + this.api.edit.raise.cancelCellEdit(); + } + // noop() };