Skip to content

Commit

Permalink
feat(bhGridDateEditor): implement ui-grid datepicker
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Jonathan Niles committed Aug 4, 2016
1 parent 9a922b6 commit 169a750
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 113 deletions.
177 changes: 69 additions & 108 deletions client/src/js/directives/bhGridDateEditor.js
Original file line number Diff line number Diff line change
@@ -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 = '<div class="datepicker-wrapper"><input class="form-control" type="text" uib-datepicker-popup datepicker-append-to-body="true" show-button-bar="false" is-open="isOpen" ng-model="datePickerValue" ng-change="changeDate($event)"/></div>';
return html;
},
template :
'<input ' +
'class="form-control" ' +
'type="text" ' +
'uib-datepicker-popup ' +
'datepicker-options="datepickerOptions" ' +
'datepicker-append-to-body="true" ' +
'show-button-bar="false" ' +
'is-open="isOpen" ' +
'ng-model="datePickerValue" ' +
'ng-change="changeDate($event)"/>',
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];

Expand All @@ -105,8 +51,7 @@ app.directive('uiGridEditDatepicker', ['$timeout', '$document', 'uiGridConstants
if (!inDatepicker && evt.target.nodeName !== 'INPUT') {
$scope.stopEdit(evt);
}
}
else {
} else {
$scope.stopEdit(evt);
}
};
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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);
});
}
};
}
};
}]);
}
26 changes: 21 additions & 5 deletions client/src/js/services/grid/GridEditors.js
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}

Expand All @@ -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()
};

Expand Down

0 comments on commit 169a750

Please sign in to comment.