From 71c34ae056169dce272d78d9f3fe1ce869c322e3 Mon Sep 17 00:00:00 2001 From: "david.rus" Date: Mon, 29 Jun 2015 21:25:34 +0200 Subject: [PATCH 01/10] feat(typeahead): popup position scroll & resize events in 'append-to-body' version of render set popup position into right place --- src/typeahead/typeahead.js | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/typeahead/typeahead.js b/src/typeahead/typeahead.js index 10b7778cf7..7ac84dc194 100644 --- a/src/typeahead/typeahead.js +++ b/src/typeahead/typeahead.js @@ -153,8 +153,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap //position pop-up with matches - we need to re-calculate its position each time we are opening a window //with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page //due to other elements being rendered - scope.position = appendToBody ? $position.offset(element) : $position.position(element); - scope.position.top = scope.position.top + element.prop('offsetHeight'); + recalculatePosition(); element.attr('aria-expanded', true); } else { @@ -170,6 +169,23 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap }); }; + if (appendToBody) { + angular.element( $window ).bind( 'resize', fireRecalculating ); + angular.element( document.body ).bind( 'scroll', fireRecalculating ); + } + + function fireRecalculating() { + if ( scope.matches.length > 0 ) { + recalculatePosition(); + scope.$apply(); + } + } + + function recalculatePosition() { + scope.position = appendToBody ? $position.offset(element) : $position.position(element); + scope.position.top = scope.position.top + element.prop('offsetHeight'); + } + resetMatches(); //we need to propagate user's query so we can higlight matches From 7271ca093df1da4be70cc1d5aee0696a7f6a01d2 Mon Sep 17 00:00:00 2001 From: "david.rus" Date: Mon, 29 Jun 2015 21:30:09 +0200 Subject: [PATCH 02/10] chore(typeahead): add some comments --- src/typeahead/typeahead.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/typeahead/typeahead.js b/src/typeahead/typeahead.js index 7ac84dc194..a95d57d7ee 100644 --- a/src/typeahead/typeahead.js +++ b/src/typeahead/typeahead.js @@ -169,18 +169,23 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap }); }; - if (appendToBody) { + // bind events only if appendToBody params exist - performance feature + if ( appendToBody ) { angular.element( $window ).bind( 'resize', fireRecalculating ); angular.element( document.body ).bind( 'scroll', fireRecalculating ); } + function fireRecalculating() { + // if popup is visible if ( scope.matches.length > 0 ) { recalculatePosition(); scope.$apply(); } } + // recalculate actual position and set new values to scope + // after digest loop is popup in right position function recalculatePosition() { scope.position = appendToBody ? $position.offset(element) : $position.position(element); scope.position.top = scope.position.top + element.prop('offsetHeight'); From 810733affbfecad68e4fbf33e5e75f5fe883ee8f Mon Sep 17 00:00:00 2001 From: "david.rus" Date: Tue, 30 Jun 2015 08:30:29 +0200 Subject: [PATCH 03/10] feat(typeahead): add $window DI --- src/typeahead/typeahead.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/typeahead/typeahead.js b/src/typeahead/typeahead.js index a95d57d7ee..b5141d0b23 100644 --- a/src/typeahead/typeahead.js +++ b/src/typeahead/typeahead.js @@ -29,8 +29,8 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap }; }]) - .directive('typeahead', ['$compile', '$parse', '$q', '$timeout', '$document', '$rootScope', '$position', 'typeaheadParser', - function ($compile, $parse, $q, $timeout, $document, $rootScope, $position, typeaheadParser) { + .directive('typeahead', ['$compile', '$parse', '$q', '$timeout', '$document', '$window', '$rootScope', '$position', 'typeaheadParser', + function ($compile, $parse, $q, $timeout, $document, $window, $rootScope, $position, typeaheadParser) { var HOT_KEYS = [9, 13, 27, 38, 40]; From 215561021081f2fbcc76dd45ac724e535e7bfd71 Mon Sep 17 00:00:00 2001 From: "david.rus" Date: Tue, 30 Jun 2015 08:31:28 +0200 Subject: [PATCH 04/10] test(typeahead): append-to-body dropdown position after scroll --- src/typeahead/test/typeahead.spec.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/typeahead/test/typeahead.spec.js b/src/typeahead/test/typeahead.spec.js index a86dcedd7e..64f62e7ef3 100644 --- a/src/typeahead/test/typeahead.spec.js +++ b/src/typeahead/test/typeahead.spec.js @@ -752,6 +752,29 @@ describe('typeahead tests', function () { changeInputValueTo(element, 'ba'); expect(findDropDown($document.find('body')).length).toEqual(0); }); + + it('should have right position after scroll', function() { + var element = prepareInputEl('
'); + var dropdown = findDropDown($document.find('body')); + var body = angular.element(document.body); + + // Set body height to allow scrolling + body.css({height:"10000px"}); + + // Scroll top + window.scroll(0, 1000); + + // Set input value to show dropdown + changeInputValueTo(element, 'ba'); + + // Init position of dropdown must be 1000px + expect(dropdown.css('top') ).toEqual('1000px'); + + // After scroll, must have new position + window.scroll(0, 500); + body.triggerHandler('scroll'); + expect(dropdown.css('top') ).toEqual('500px'); + }) }); describe('focus first', function () { From 690d78b2609f1ed800e78d8fc2d545784863fc06 Mon Sep 17 00:00:00 2001 From: "david.rus" Date: Tue, 30 Jun 2015 08:43:49 +0200 Subject: [PATCH 05/10] chore(typeahead): jshint errors --- src/typeahead/test/typeahead.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/typeahead/test/typeahead.spec.js b/src/typeahead/test/typeahead.spec.js index 64f62e7ef3..154f2730ef 100644 --- a/src/typeahead/test/typeahead.spec.js +++ b/src/typeahead/test/typeahead.spec.js @@ -759,7 +759,7 @@ describe('typeahead tests', function () { var body = angular.element(document.body); // Set body height to allow scrolling - body.css({height:"10000px"}); + body.css({height:'10000px'}); // Scroll top window.scroll(0, 1000); @@ -774,7 +774,7 @@ describe('typeahead tests', function () { window.scroll(0, 500); body.triggerHandler('scroll'); expect(dropdown.css('top') ).toEqual('500px'); - }) + }); }); describe('focus first', function () { From b7ab0205a5d8a89defa91c98c221d47ca9ba9567 Mon Sep 17 00:00:00 2001 From: "david.rus" Date: Tue, 30 Jun 2015 08:47:40 +0200 Subject: [PATCH 06/10] chore(typeahead): pull request improvements --- src/typeahead/typeahead.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/typeahead/typeahead.js b/src/typeahead/typeahead.js index b5141d0b23..6207927f74 100644 --- a/src/typeahead/typeahead.js +++ b/src/typeahead/typeahead.js @@ -172,15 +172,15 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap // bind events only if appendToBody params exist - performance feature if ( appendToBody ) { angular.element( $window ).bind( 'resize', fireRecalculating ); - angular.element( document.body ).bind( 'scroll', fireRecalculating ); + angular.element( $document.find('body') ).bind( 'scroll', fireRecalculating ); } function fireRecalculating() { // if popup is visible - if ( scope.matches.length > 0 ) { + if ( scope.matches.length ) { recalculatePosition(); - scope.$apply(); + scope.$digest(); } } From a35d89a5a421728ee1bf183ded26085512bb6bf2 Mon Sep 17 00:00:00 2001 From: "david.rus" Date: Tue, 30 Jun 2015 09:05:49 +0200 Subject: [PATCH 07/10] chore(typeahead): document.find return angular element --- src/typeahead/typeahead.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/typeahead/typeahead.js b/src/typeahead/typeahead.js index 6207927f74..dc0ebb6487 100644 --- a/src/typeahead/typeahead.js +++ b/src/typeahead/typeahead.js @@ -172,7 +172,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap // bind events only if appendToBody params exist - performance feature if ( appendToBody ) { angular.element( $window ).bind( 'resize', fireRecalculating ); - angular.element( $document.find('body') ).bind( 'scroll', fireRecalculating ); + $document.find( 'body' ).bind( 'scroll', fireRecalculating ); } From 156b2230daacb033c7c8a3ef77a4fb4a7c77909b Mon Sep 17 00:00:00 2001 From: "david.rus" Date: Fri, 3 Jul 2015 08:41:49 +0200 Subject: [PATCH 08/10] feat(typeahead): add debounce fire events & hiding popup popup is hidden if scroll & resize is in progress --- src/typeahead/typeahead.js | 35 ++++++++++++++++++++++--- template/typeahead/typeahead-popup.html | 2 +- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/typeahead/typeahead.js b/src/typeahead/typeahead.js index dc0ebb6487..9e45141ba9 100644 --- a/src/typeahead/typeahead.js +++ b/src/typeahead/typeahead.js @@ -30,9 +30,10 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap }]) .directive('typeahead', ['$compile', '$parse', '$q', '$timeout', '$document', '$window', '$rootScope', '$position', 'typeaheadParser', - function ($compile, $parse, $q, $timeout, $document, $window, $rootScope, $position, typeaheadParser) { + function ($compile, $parse, $q, $timeout, $document, $window, $rootScope, $position, typeaheadParser) { var HOT_KEYS = [9, 13, 27, 38, 40]; + var eventDebounceTime = 200; return { require:'ngModel', @@ -96,6 +97,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap matches: 'matches', active: 'activeIdx', select: 'select(activeIdx)', + 'move-in-progress': 'moveInProgress', query: 'query', position: 'position' }); @@ -175,13 +177,37 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap $document.find( 'body' ).bind( 'scroll', fireRecalculating ); } + // Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later + var timeoutEventPromise; + + // Default progress type + scope.moveInProgress = false; function fireRecalculating() { - // if popup is visible - if ( scope.matches.length ) { - recalculatePosition(); + + if( !scope.moveInProgress ){ + scope.moveInProgress = true; scope.$digest(); } + + // Cancel previous timeout + if ( timeoutEventPromise ) { + $timeout.cancel(timeoutEventPromise); + } + + // Debounced executing recalculate after events fired + timeoutEventPromise = $timeout(function () { + + // if popup is visible + if ( scope.matches.length ) { + recalculatePosition(); + } + + scope.moveInProgress = false; + scope.$digest(); + + }, eventDebounceTime); + } // recalculate actual position and set new values to scope @@ -379,6 +405,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap query:'=', active:'=', position:'&', + moveInProgress:'=', select:'&' }, replace:true, diff --git a/template/typeahead/typeahead-popup.html b/template/typeahead/typeahead-popup.html index 9760c154b5..43693ac35e 100644 --- a/template/typeahead/typeahead-popup.html +++ b/template/typeahead/typeahead-popup.html @@ -1,4 +1,4 @@ -