Skip to content

Commit 1182b05

Browse files
committed
fix(GridRenderContainer): Correct prevScroll*
prevScrollLeft and prevScrollTop were being set improperly in the adjustScroll* methods. Neither were accounting for the viewport, causing the value to be too large. Now it only sets the value if the scrollLeft or scrollTop argument is not provided, and it includes the viewport in the calculation. Also minor fixes within cellNav
1 parent c903ecc commit 1182b05

File tree

3 files changed

+203
-102
lines changed

3 files changed

+203
-102
lines changed

src/features/cellnav/js/cellnav.js

+143-84
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@
1616
module.constant('uiGridCellNavConstants', {
1717
FEATURE_NAME: 'gridCellNav',
1818
CELL_NAV_EVENT: 'cellNav',
19-
direction: {LEFT: 0, RIGHT: 1, UP: 2, DOWN: 3}
19+
direction: {LEFT: 0, RIGHT: 1, UP: 2, DOWN: 3},
20+
EVENT_TYPE: {
21+
KEYDOWN: 0,
22+
CLICK: 1
23+
}
2024
});
2125

2226

@@ -409,60 +413,130 @@
409413
var visRowCache = grid.renderContainers.body.visibleRowCache;
410414
var visColCache = grid.renderContainers.body.visibleColumnCache;
411415

412-
// Get the top, left, right, and bottom "scrolled" edges of the grid
416+
/*-- Get the top, left, right, and bottom "scrolled" edges of the grid --*/
417+
418+
// The top boundary is the current Y scroll position PLUS the header height, because the header can obscure rows when the grid is scrolled downwards
413419
var topBound = grid.renderContainers.body.prevScrollTop + grid.headerHeight;
420+
421+
// Don't the let top boundary be less than 0
414422
topBound = (topBound < 0) ? 0 : topBound;
415423

424+
// The left boundary is the current X scroll position
416425
var leftBound = grid.renderContainers.body.prevScrollLeft;
417426

427+
// The bottom boundary is the current Y scroll position, plus the height of the grid, but minus the header height.
428+
// Basically this is the viewport height added on to the scroll position
418429
var bottomBound = grid.renderContainers.body.prevScrollTop + grid.gridHeight - grid.headerHeight;
419430

431+
// If there's a horizontal scrollbar, remove its height from the bottom boundary, otherwise we'll be letting it obscure rows
420432
if (grid.horizontalScrollbarHeight) {
421433
bottomBound = bottomBound - grid.horizontalScrollbarHeight;
422434
}
423435

424-
var rightBound = leftBound + grid.gridWidth;
436+
// The right position is the current X scroll position minus the grid width
437+
var rightBound = grid.renderContainers.body.prevScrollLeft + grid.gridWidth;
438+
439+
// If there's a vertical scrollbar, subtract it from the right boundary or we'll allow it to obscure cells
425440
if (grid.verticalScrollbarWidth) {
426441
rightBound = rightBound - grid.verticalScrollbarWidth;
427442
}
428443

444+
// We were given a row to scroll to
429445
if (gridRow !== null) {
446+
// This is the index of the row we want to scroll to, within the list of rows that can be visible
430447
var seekRowIndex = visRowCache.indexOf(gridRow);
431-
var totalRows = visRowCache.length;
432-
// var percentage = ( seekRowIndex + ( seekRowIndex / ( totalRows - 1 ) ) ) / totalRows;
433-
// args.y = { percentage: percentage };
448+
449+
// Total vertical scroll length of the grid
434450
var scrollLength = (grid.renderContainers.body.getCanvasHeight() - grid.renderContainers.body.getViewportHeight());
435451

436-
// Add the height of the native horizontal scrollbar, if it's there. Otherwise it will mask over the final row
452+
// Add the height of the native horizontal scrollbar to the scroll length, if it's there. Otherwise it will mask over the final row
437453
if (grid.horizontalScrollbarHeight && grid.horizontalScrollbarHeight > 0) {
438454
scrollLength = scrollLength + grid.horizontalScrollbarHeight;
439455
}
440456

441-
// var pixelsToSeeRow = (scrollLength * percentage) + grid.options.rowHeight;
457+
// This is the minimum amount of pixels we need to scroll vertical in order to see this row.
442458
var pixelsToSeeRow = ((seekRowIndex + 1) * grid.options.rowHeight);
459+
460+
// Don't let the pixels required to see the row be less than zero
443461
pixelsToSeeRow = (pixelsToSeeRow < 0) ? 0 : pixelsToSeeRow;
444462

445463
var scrollPixels, percentage;
464+
465+
// If the scroll position we need to see the row is LESS than the top boundary, i.e. obscured above the top of the grid...
446466
if (pixelsToSeeRow < topBound) {
467+
// Get the different between the top boundary and the required scroll position and subtract it from the current scroll position\
468+
// to get the full position we need
447469
scrollPixels = grid.renderContainers.body.prevScrollTop - (topBound - pixelsToSeeRow);
470+
471+
// Turn the scroll position into a percentage and make it an argument for a scroll event
448472
percentage = scrollPixels / scrollLength;
449473
args.y = { percentage: percentage };
450474
}
475+
// Otherwise if the scroll position we need to see the row is MORE than the bottom boundary, i.e. obscured below the bottom of the grid...
451476
else if (pixelsToSeeRow > bottomBound) {
477+
// Get the different between the bottom boundary and the required scroll position and add it to the current scroll position
478+
// to get the full position we need
452479
scrollPixels = pixelsToSeeRow - bottomBound + grid.renderContainers.body.prevScrollTop;
480+
481+
// Turn the scroll position into a percentage and make it an argument for a scroll event
453482
percentage = scrollPixels / scrollLength;
454483
args.y = { percentage: percentage };
455484
}
456485
}
457486

487+
// We were given a column to scroll to
458488
if (gridCol !== null) {
459-
var pixelsToSeeColumn = this.getLeftWidth(grid, gridCol) + gridCol.drawnWidth;
489+
// This is the index of the row we want to scroll to, within the list of rows that can be visible
490+
var seekColumnIndex = visColCache.indexOf(gridCol);
491+
492+
// Total vertical scroll length of the grid
493+
var horizScrollLength = (grid.renderContainers.body.getCanvasWidth() - grid.renderContainers.body.getViewportWidth());
494+
495+
// Add the height of the native horizontal scrollbar to the scroll length, if it's there. Otherwise it will mask over the final row
496+
// if (grid.verticalScrollbarWidth && grid.verticalScrollbarWidth > 0) {
497+
// horizScrollLength = horizScrollLength + grid.verticalScrollbarWidth;
498+
// }
499+
500+
// This is the minimum amount of pixels we need to scroll vertical in order to see this column
501+
var columnLeftEdge = 0;
502+
for (var i = 0; i < seekColumnIndex; i++) {
503+
var col = visColCache[i];
504+
columnLeftEdge += col.drawnWidth;
505+
}
506+
columnLeftEdge = (columnLeftEdge < 0) ? 0 : columnLeftEdge;
460507

461-
if (pixelsToSeeColumn < leftBound || pixelsToSeeColumn > rightBound) {
462-
args.x = { percentage: this.getLeftWidth(grid, gridCol) / this.getLeftWidth(grid, visColCache[visColCache.length - 1] ) };
508+
var columnRightEdge = columnLeftEdge + gridCol.drawnWidth;
509+
510+
// Don't let the pixels required to see the column be less than zero
511+
columnRightEdge = (columnRightEdge < 0) ? 0 : columnRightEdge;
512+
513+
var horizScrollPixels, horizPercentage;
514+
515+
// If the scroll position we need to see the row is LESS than the top boundary, i.e. obscured above the top of the grid...
516+
if (columnLeftEdge < leftBound) {
517+
// Get the different between the top boundary and the required scroll position and subtract it from the current scroll position\
518+
// to get the full position we need
519+
horizScrollPixels = grid.renderContainers.body.prevScrollLeft - (leftBound - columnLeftEdge);
520+
521+
// Turn the scroll position into a percentage and make it an argument for a scroll event
522+
horizPercentage = horizScrollPixels / horizScrollLength;
523+
horizPercentage = (horizPercentage > 1) ? 1 : horizPercentage;
524+
args.x = { percentage: horizPercentage };
525+
}
526+
// Otherwise if the scroll position we need to see the row is MORE than the bottom boundary, i.e. obscured below the bottom of the grid...
527+
else if (columnRightEdge > rightBound) {
528+
// Get the different between the bottom boundary and the required scroll position and add it to the current scroll position
529+
// to get the full position we need
530+
horizScrollPixels = columnRightEdge - rightBound + grid.renderContainers.body.prevScrollLeft;
531+
532+
// Turn the scroll position into a percentage and make it an argument for a scroll event
533+
horizPercentage = horizScrollPixels / horizScrollLength;
534+
horizPercentage = (horizPercentage > 1) ? 1 : horizPercentage;
535+
args.x = { percentage: horizPercentage };
463536
}
464537
}
465538

539+
// If we need to scroll on either the x or y axes, fire a scroll event
466540
if (args.y || args.x) {
467541
$scope.$broadcast(uiGridConstants.events.GRID_SCROLL, args);
468542
}
@@ -541,8 +615,8 @@
541615
</file>
542616
</example>
543617
*/
544-
module.directive('uiGridCellnav', ['$log', 'gridUtil', 'uiGridCellNavService', 'uiGridCellNavConstants',
545-
function ($log, gridUtil, uiGridCellNavService, uiGridCellNavConstants) {
618+
module.directive('uiGridCellnav', ['gridUtil', 'uiGridCellNavService', 'uiGridCellNavConstants',
619+
function (gridUtil, uiGridCellNavService, uiGridCellNavConstants) {
546620
return {
547621
replace: true,
548622
priority: -150,
@@ -557,6 +631,10 @@
557631

558632
uiGridCtrl.cellNav = {};
559633

634+
uiGridCtrl.cellNav.focusCell = function (row, col) {
635+
uiGridCtrl.cellNav.broadcastCellNav({ row: row, col: col });
636+
};
637+
560638
// gridUtil.logDebug('uiGridEdit preLink');
561639
uiGridCtrl.cellNav.broadcastCellNav = function (newRowCol) {
562640
$scope.$broadcast(uiGridCellNavConstants.CELL_NAV_EVENT, newRowCol);
@@ -571,6 +649,37 @@
571649
}
572650
};
573651

652+
uiGridCtrl.cellNav.handleKeyDown = function (evt) {
653+
var direction = uiGridCellNavService.getDirection(evt);
654+
if (direction === null) {
655+
return true;
656+
}
657+
658+
var containerId = 'body';
659+
if (evt.uiGridTargetRenderContainerId) {
660+
containerId = evt.uiGridTargetRenderContainerId;
661+
}
662+
663+
// Get the last-focused row+col combo
664+
var lastRowCol = uiGridCtrl.grid.api.cellNav.getFocusedCell();
665+
if (lastRowCol) {
666+
// Figure out which new row+combo we're navigating to
667+
var rowCol = uiGridCtrl.grid.renderContainers[containerId].cellNav.getNextRowCol(direction, lastRowCol.row, lastRowCol.col);
668+
669+
rowCol.eventType = uiGridCellNavConstants.EVENT_TYPE.KEYDOWN;
670+
671+
// Broadcast the navigation
672+
uiGridCtrl.cellNav.broadcastCellNav(rowCol);
673+
674+
// Scroll to the new cell, if it's not completely visible within the render container's viewport
675+
uiGridCellNavService.scrollToIfNecessary(grid, $scope, rowCol.row, rowCol.col);
676+
677+
evt.stopPropagation();
678+
evt.preventDefault();
679+
680+
return false;
681+
}
682+
};
574683
},
575684
post: function ($scope, $elm, $attrs, uiGridCtrl) {
576685
}
@@ -579,8 +688,8 @@
579688
};
580689
}]);
581690

582-
module.directive('uiGridRenderContainer', ['$log', '$timeout', 'gridUtil', 'uiGridConstants', 'uiGridCellNavService', 'uiGridCellNavConstants',
583-
function ($log, $timeout, gridUtil, uiGridConstants, uiGridCellNavService, uiGridCellNavConstants) {
691+
module.directive('uiGridRenderContainer', ['$timeout', 'gridUtil', 'uiGridConstants', 'uiGridCellNavService', 'uiGridCellNavConstants',
692+
function ($timeout, gridUtil, uiGridConstants, uiGridCellNavService, uiGridCellNavConstants) {
584693
return {
585694
replace: true,
586695
priority: -99999, //this needs to run very last
@@ -600,32 +709,25 @@
600709
//needs to run last after all renderContainers are built
601710
uiGridCellNavService.decorateRenderContainers(grid);
602711

712+
// Bind to keydown events in the render container
603713
$elm.on('keydown', function (evt) {
604-
var direction = uiGridCellNavService.getDirection(evt);
605-
if (direction === null) {
606-
return true;
607-
}
608-
609-
var lastRowCol = uiGridCtrl.grid.api.cellNav.getFocusedCell();
610-
var rowCol = uiGridCtrl.grid.renderContainers[containerId].cellNav.getNextRowCol(direction, lastRowCol.row, lastRowCol.col);
611-
// $log.debug('next id', rowCol.row.entity.id);
612-
613-
uiGridCtrl.cellNav.broadcastCellNav(rowCol);
614-
uiGridCellNavService.scrollToIfNecessary(grid, $scope, rowCol.row, rowCol.col);
615-
// setTabEnabled();
616-
617-
evt.stopPropagation();
618-
evt.preventDefault();
619-
620-
return false;
714+
evt.uiGridTargetRenderContainerId = containerId;
715+
return uiGridCtrl.cellNav.handleKeyDown(evt);
621716
});
622717

623718
// When there's a scroll event we need to make sure to re-focus the right row, because the cell contents may have changed
624719
$scope.$on(uiGridConstants.events.GRID_SCROLL, function (evt, args) {
720+
if (uiGridCtrl.grid.api.cellNav.getFocusedCell() == null) {
721+
return;
722+
}
723+
625724
// We have to wrap in TWO timeouts so that we run AFTER the scroll event is resolved.
626725
$timeout(function () {
627726
$timeout(function () {
727+
// Get the last row+col combo
628728
var lastRowCol = uiGridCtrl.grid.api.cellNav.getFocusedCell();
729+
730+
// Re-broadcast a cellNav event so we re-focus the right cell
629731
uiGridCtrl.cellNav.broadcastCellNav(lastRowCol);
630732
});
631733
});
@@ -643,8 +745,8 @@
643745
* @restrict A
644746
* @description Stacks on top of ui.grid.uiGridCell to provide cell navigation
645747
*/
646-
module.directive('uiGridCell', ['$log', '$timeout', 'uiGridCellNavService', 'gridUtil', 'uiGridCellNavConstants', 'uiGridConstants',
647-
function ($log, $timeout, uiGridCellNavService, gridUtil, uiGridCellNavConstants, uiGridConstants) {
748+
module.directive('uiGridCell', ['$timeout', 'uiGridCellNavService', 'gridUtil', 'uiGridCellNavConstants', 'uiGridConstants',
749+
function ($timeout, uiGridCellNavService, gridUtil, uiGridCellNavConstants, uiGridConstants) {
648750
return {
649751
priority: -150, // run after default uiGridCell directive and ui.grid.edit uiGridCell
650752
restrict: 'A',
@@ -656,84 +758,41 @@
656758
}
657759

658760
setTabEnabled();
659-
660-
// $elm.on('keydown', function (evt) {
661-
// var direction = uiGridCellNavService.getDirection(evt);
662-
// if (direction === null) {
663-
// return true;
664-
// }
665-
666-
// var rowCol = $scope.colContainer.cellNav.getNextRowCol(direction, $scope.row, $scope.col);
667-
// $log.debug('next id', rowCol.row.entity.id);
668-
669-
// uiGridCtrl.cellNav.broadcastCellNav(rowCol);
670-
// setTabEnabled();
671-
672-
// evt.stopPropagation();
673-
// evt.preventDefault();
674-
675-
// return false;
676-
// });
677-
761+
762+
// When a cell is clicked, broadcast a cellNav event saying that this row+col combo is now focused
678763
$elm.find('div').on('click', function (evt) {
679764
uiGridCtrl.cellNav.broadcastCellNav(new RowCol($scope.row, $scope.col));
680765

681766
evt.stopPropagation();
682767
});
683768

684-
//this event is fired for all cells. If the cell matches, then focus is set
769+
// This event is fired for all cells. If the cell matches, then focus is set
685770
$scope.$on(uiGridCellNavConstants.CELL_NAV_EVENT, function (evt, rowCol) {
686771
if (rowCol.row === $scope.row &&
687772
rowCol.col === $scope.col) {
688773
setFocused();
774+
775+
if (rowCol.hasOwnProperty('eventType') && rowCol.eventType === uiGridCellNavConstants.EVENT_TYPE.KEYDOWN) {
776+
$elm.find('div')[0].focus();
777+
}
689778
}
690779
else {
691780
clearFocus();
692781
}
693-
694-
// $scope.grid.queueRefresh();
695782
});
696783

697-
// $scope.$on(uiGridConstants.events.GRID_SCROLL, function (evt, args) {
698-
// clearFocus();
699-
700-
// $log.debug('scrollIndex 1', uiGridCtrl.grid.renderContainers.body.prevRowScrollIndex);
701-
702-
// $timeout(function () {
703-
// $log.debug('scrollIndex 2', uiGridCtrl.grid.renderContainers.body.prevRowScrollIndex);
704-
705-
// var lastRowCol = uiGridCtrl.grid.api.cellNav.getFocusedCell();
706-
707-
// if (lastRowCol === null) {
708-
// return;
709-
// }
710-
711-
// if (lastRowCol.hasOwnProperty('row') && lastRowCol.row === $scope.row && lastRowCol.hasOwnProperty('col') && lastRowCol.col === $scope.col) {
712-
// setFocused();
713-
// }
714-
// });
715-
// });
716-
717784
function setTabEnabled() {
718785
$elm.find('div').attr("tabindex", -1);
719786
}
720787

721788
function setFocused() {
722789
var div = $elm.find('div');
723-
// gridUtil.logDebug('setFocused: ' + div[0].parentElement.className);
724-
// div[0].focus();
725-
// div.attr("tabindex", 0);
726790
div.addClass('ui-grid-cell-focus');
727-
// $scope.grid.queueRefresh();
728791
}
729792

730793
function clearFocus() {
731794
var div = $elm.find('div');
732-
// gridUtil.logDebug('setFocused: ' + div[0].parentElement.className);
733-
// div[0].focus();
734-
// div.attr("tabindex", 0);
735795
div.removeClass('ui-grid-cell-focus');
736-
// $scope.grid.queueRefresh();
737796
}
738797

739798
$scope.$on('$destroy', function () {

0 commit comments

Comments
 (0)