|
16 | 16 | module.constant('uiGridCellNavConstants', {
|
17 | 17 | FEATURE_NAME: 'gridCellNav',
|
18 | 18 | 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 | + } |
20 | 24 | });
|
21 | 25 |
|
22 | 26 |
|
|
409 | 413 | var visRowCache = grid.renderContainers.body.visibleRowCache;
|
410 | 414 | var visColCache = grid.renderContainers.body.visibleColumnCache;
|
411 | 415 |
|
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 |
413 | 419 | var topBound = grid.renderContainers.body.prevScrollTop + grid.headerHeight;
|
| 420 | + |
| 421 | + // Don't the let top boundary be less than 0 |
414 | 422 | topBound = (topBound < 0) ? 0 : topBound;
|
415 | 423 |
|
| 424 | + // The left boundary is the current X scroll position |
416 | 425 | var leftBound = grid.renderContainers.body.prevScrollLeft;
|
417 | 426 |
|
| 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 |
418 | 429 | var bottomBound = grid.renderContainers.body.prevScrollTop + grid.gridHeight - grid.headerHeight;
|
419 | 430 |
|
| 431 | + // If there's a horizontal scrollbar, remove its height from the bottom boundary, otherwise we'll be letting it obscure rows |
420 | 432 | if (grid.horizontalScrollbarHeight) {
|
421 | 433 | bottomBound = bottomBound - grid.horizontalScrollbarHeight;
|
422 | 434 | }
|
423 | 435 |
|
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 |
425 | 440 | if (grid.verticalScrollbarWidth) {
|
426 | 441 | rightBound = rightBound - grid.verticalScrollbarWidth;
|
427 | 442 | }
|
428 | 443 |
|
| 444 | + // We were given a row to scroll to |
429 | 445 | 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 |
430 | 447 | 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 |
434 | 450 | var scrollLength = (grid.renderContainers.body.getCanvasHeight() - grid.renderContainers.body.getViewportHeight());
|
435 | 451 |
|
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 |
437 | 453 | if (grid.horizontalScrollbarHeight && grid.horizontalScrollbarHeight > 0) {
|
438 | 454 | scrollLength = scrollLength + grid.horizontalScrollbarHeight;
|
439 | 455 | }
|
440 | 456 |
|
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. |
442 | 458 | var pixelsToSeeRow = ((seekRowIndex + 1) * grid.options.rowHeight);
|
| 459 | + |
| 460 | + // Don't let the pixels required to see the row be less than zero |
443 | 461 | pixelsToSeeRow = (pixelsToSeeRow < 0) ? 0 : pixelsToSeeRow;
|
444 | 462 |
|
445 | 463 | 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... |
446 | 466 | 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 |
447 | 469 | 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 |
448 | 472 | percentage = scrollPixels / scrollLength;
|
449 | 473 | args.y = { percentage: percentage };
|
450 | 474 | }
|
| 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... |
451 | 476 | 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 |
452 | 479 | 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 |
453 | 482 | percentage = scrollPixels / scrollLength;
|
454 | 483 | args.y = { percentage: percentage };
|
455 | 484 | }
|
456 | 485 | }
|
457 | 486 |
|
| 487 | + // We were given a column to scroll to |
458 | 488 | 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; |
460 | 507 |
|
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 }; |
463 | 536 | }
|
464 | 537 | }
|
465 | 538 |
|
| 539 | + // If we need to scroll on either the x or y axes, fire a scroll event |
466 | 540 | if (args.y || args.x) {
|
467 | 541 | $scope.$broadcast(uiGridConstants.events.GRID_SCROLL, args);
|
468 | 542 | }
|
|
541 | 615 | </file>
|
542 | 616 | </example>
|
543 | 617 | */
|
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) { |
546 | 620 | return {
|
547 | 621 | replace: true,
|
548 | 622 | priority: -150,
|
|
557 | 631 |
|
558 | 632 | uiGridCtrl.cellNav = {};
|
559 | 633 |
|
| 634 | + uiGridCtrl.cellNav.focusCell = function (row, col) { |
| 635 | + uiGridCtrl.cellNav.broadcastCellNav({ row: row, col: col }); |
| 636 | + }; |
| 637 | + |
560 | 638 | // gridUtil.logDebug('uiGridEdit preLink');
|
561 | 639 | uiGridCtrl.cellNav.broadcastCellNav = function (newRowCol) {
|
562 | 640 | $scope.$broadcast(uiGridCellNavConstants.CELL_NAV_EVENT, newRowCol);
|
|
571 | 649 | }
|
572 | 650 | };
|
573 | 651 |
|
| 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 | + }; |
574 | 683 | },
|
575 | 684 | post: function ($scope, $elm, $attrs, uiGridCtrl) {
|
576 | 685 | }
|
|
579 | 688 | };
|
580 | 689 | }]);
|
581 | 690 |
|
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) { |
584 | 693 | return {
|
585 | 694 | replace: true,
|
586 | 695 | priority: -99999, //this needs to run very last
|
|
600 | 709 | //needs to run last after all renderContainers are built
|
601 | 710 | uiGridCellNavService.decorateRenderContainers(grid);
|
602 | 711 |
|
| 712 | + // Bind to keydown events in the render container |
603 | 713 | $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); |
621 | 716 | });
|
622 | 717 |
|
623 | 718 | // When there's a scroll event we need to make sure to re-focus the right row, because the cell contents may have changed
|
624 | 719 | $scope.$on(uiGridConstants.events.GRID_SCROLL, function (evt, args) {
|
| 720 | + if (uiGridCtrl.grid.api.cellNav.getFocusedCell() == null) { |
| 721 | + return; |
| 722 | + } |
| 723 | + |
625 | 724 | // We have to wrap in TWO timeouts so that we run AFTER the scroll event is resolved.
|
626 | 725 | $timeout(function () {
|
627 | 726 | $timeout(function () {
|
| 727 | + // Get the last row+col combo |
628 | 728 | var lastRowCol = uiGridCtrl.grid.api.cellNav.getFocusedCell();
|
| 729 | + |
| 730 | + // Re-broadcast a cellNav event so we re-focus the right cell |
629 | 731 | uiGridCtrl.cellNav.broadcastCellNav(lastRowCol);
|
630 | 732 | });
|
631 | 733 | });
|
|
643 | 745 | * @restrict A
|
644 | 746 | * @description Stacks on top of ui.grid.uiGridCell to provide cell navigation
|
645 | 747 | */
|
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) { |
648 | 750 | return {
|
649 | 751 | priority: -150, // run after default uiGridCell directive and ui.grid.edit uiGridCell
|
650 | 752 | restrict: 'A',
|
|
656 | 758 | }
|
657 | 759 |
|
658 | 760 | 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 |
678 | 763 | $elm.find('div').on('click', function (evt) {
|
679 | 764 | uiGridCtrl.cellNav.broadcastCellNav(new RowCol($scope.row, $scope.col));
|
680 | 765 |
|
681 | 766 | evt.stopPropagation();
|
682 | 767 | });
|
683 | 768 |
|
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 |
685 | 770 | $scope.$on(uiGridCellNavConstants.CELL_NAV_EVENT, function (evt, rowCol) {
|
686 | 771 | if (rowCol.row === $scope.row &&
|
687 | 772 | rowCol.col === $scope.col) {
|
688 | 773 | setFocused();
|
| 774 | + |
| 775 | + if (rowCol.hasOwnProperty('eventType') && rowCol.eventType === uiGridCellNavConstants.EVENT_TYPE.KEYDOWN) { |
| 776 | + $elm.find('div')[0].focus(); |
| 777 | + } |
689 | 778 | }
|
690 | 779 | else {
|
691 | 780 | clearFocus();
|
692 | 781 | }
|
693 |
| - |
694 |
| - // $scope.grid.queueRefresh(); |
695 | 782 | });
|
696 | 783 |
|
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 |
| - |
717 | 784 | function setTabEnabled() {
|
718 | 785 | $elm.find('div').attr("tabindex", -1);
|
719 | 786 | }
|
720 | 787 |
|
721 | 788 | function setFocused() {
|
722 | 789 | var div = $elm.find('div');
|
723 |
| - // gridUtil.logDebug('setFocused: ' + div[0].parentElement.className); |
724 |
| - // div[0].focus(); |
725 |
| - // div.attr("tabindex", 0); |
726 | 790 | div.addClass('ui-grid-cell-focus');
|
727 |
| - // $scope.grid.queueRefresh(); |
728 | 791 | }
|
729 | 792 |
|
730 | 793 | function clearFocus() {
|
731 | 794 | var div = $elm.find('div');
|
732 |
| - // gridUtil.logDebug('setFocused: ' + div[0].parentElement.className); |
733 |
| - // div[0].focus(); |
734 |
| - // div.attr("tabindex", 0); |
735 | 795 | div.removeClass('ui-grid-cell-focus');
|
736 |
| - // $scope.grid.queueRefresh(); |
737 | 796 | }
|
738 | 797 |
|
739 | 798 | $scope.$on('$destroy', function () {
|
|
0 commit comments