diff --git a/src/datepicker/datepicker.css b/src/datepicker/datepicker.css index a9af3ab849..64c7cc5c62 100644 --- a/src/datepicker/datepicker.css +++ b/src/datepicker/datepicker.css @@ -8,6 +8,11 @@ .uib-datepicker-popup.dropdown-menu { display: block; + float: none; + visibility: hidden; + margin: 0; + top: -9999px; + left: -9999px; } .uib-button-bar { diff --git a/src/datepicker/datepicker.js b/src/datepicker/datepicker.js index eb514561a3..f810996bb8 100644 --- a/src/datepicker/datepicker.js +++ b/src/datepicker/datepicker.js @@ -332,6 +332,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst } else { self.activeDate = date; $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) - 1]; + $scope.$emit('uib:datepicker.mode'); } }; @@ -351,6 +352,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst } $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) + direction]; + $scope.$emit('uib:datepicker.mode'); }; // Key event mapper @@ -717,15 +719,16 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst 'month': 'yyyy-MM' }, onOpenFocus: true, - showButtonBar: true + showButtonBar: true, + placement: 'auto bottom-left' }) -.controller('UibDatepickerPopupController', ['$scope', '$element', '$attrs', '$compile', '$log', '$parse', '$document', '$rootScope', '$uibPosition', 'dateFilter', 'uibDateParser', 'uibDatepickerPopupConfig', '$timeout', 'uibDatepickerConfig', 'uibDatepickerPopupAttributeWarning', -function($scope, $element, $attrs, $compile, $log, $parse, $document, $rootScope, $position, dateFilter, dateParser, datepickerPopupConfig, $timeout, datepickerConfig, datepickerPopupAttributeWarning) { +.controller('UibDatepickerPopupController', ['$scope', '$element', '$attrs', '$compile', '$log', '$parse', '$window', '$document', '$rootScope', '$uibPosition', 'dateFilter', 'uibDateParser', 'uibDatepickerPopupConfig', '$timeout', 'uibDatepickerConfig', 'uibDatepickerPopupAttributeWarning', +function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $rootScope, $position, dateFilter, dateParser, datepickerPopupConfig, $timeout, datepickerConfig, datepickerPopupAttributeWarning) { var cache = {}, isHtml5DateInput = false; var dateFormat, closeOnDateSelection, appendToBody, onOpenFocus, - datepickerPopupTemplateUrl, datepickerTemplateUrl, popupEl, datepickerEl, + datepickerPopupTemplateUrl, datepickerTemplateUrl, popupEl, datepickerEl, scrollParentEl, ngModel, ngModelOptions, $popup, altInputFormats, watchListeners = []; $scope.watchData = {}; @@ -965,6 +968,10 @@ function($scope, $element, $attrs, $compile, $log, $parse, $document, $rootScope $popup.remove(); $element.off('keydown', inputKeydownBind); $document.off('click', documentClickBind); + if (scrollParentEl) { + scrollParentEl.off('scroll', positionPopup); + } + angular.element($window).off('resize', positionPopup); //Clear all watch listeners on destroy while (watchListeners.length) { @@ -1045,20 +1052,35 @@ function($scope, $element, $attrs, $compile, $log, $parse, $document, $rootScope $scope.$watch('isOpen', function(value) { if (value) { if (!$scope.disabled) { - $scope.position = appendToBody ? $position.offset($element) : $position.position($element); - $scope.position.top = $scope.position.top + $element.prop('offsetHeight'); - $timeout(function() { + positionPopup(); + if (onOpenFocus) { $scope.$broadcast('uib:datepicker.focus'); } $document.on('click', documentClickBind); + + var placement = $attrs.popupPlacement ? $attrs.popupPlacement : datepickerPopupConfig.placement; + if (appendToBody || $position.parsePlacement(placement)[2]) { + scrollParentEl = scrollParentEl || angular.element($position.scrollParent($element)); + if (scrollParentEl) { + scrollParentEl.on('scroll', positionPopup); + } + } else { + scrollParentEl = null; + } + + angular.element($window).on('resize', positionPopup); }, 0, false); } else { $scope.isOpen = false; } } else { $document.off('click', documentClickBind); + if (scrollParentEl) { + scrollParentEl.off('scroll', positionPopup); + } + angular.element($window).off('resize', positionPopup); } }); @@ -1162,6 +1184,19 @@ function($scope, $element, $attrs, $compile, $log, $parse, $document, $rootScope }); } } + + function positionPopup() { + if ($scope.isOpen) { + var dpElement = $popup[0].querySelector('.uib-datepicker-popup'); + var placement = $attrs.popupPlacement ? $attrs.popupPlacement : datepickerPopupConfig.placement; + var position = $position.positionElements($element, dpElement, placement, appendToBody); + angular.element(dpElement).css({top: position.top + 'px', left: position.left + 'px', visibility: 'visible'}); + } + } + + $scope.$on('uib:datepicker.mode', function() { + $timeout(positionPopup, 0, false); + }); }]) .directive('uibDatepickerPopup', function() { diff --git a/src/datepicker/docs/readme.md b/src/datepicker/docs/readme.md index f97a83099e..3c15f891f1 100644 --- a/src/datepicker/docs/readme.md +++ b/src/datepicker/docs/readme.md @@ -78,7 +78,7 @@ The datepicker has 3 modes: * `format-month-title` C _(Default: `yyyy`)_ - - Format of title when selecting month. + Format of title when selecting month. * `init-date` $ @@ -210,7 +210,7 @@ The popup is a wrapper that you can use in an input to toggle a datepicker. To c * `datepicker-template-url` C _(Default: `uib/template/datepicker/datepicker.html`)_ - - Add the ability to override the template used on the component (inner uib-datepicker). + Add the ability to override the template used on the component (inner uib-datepicker). * `is-open` $ @@ -235,6 +235,24 @@ The popup is a wrapper that you can use in an input to toggle a datepicker. To c _(Default: `text`, Config: `html5Types`)_ - You can override the input type to be _(date|datetime-local|month)_. That will change the date format of the popup. +* `popup-placement` + C + _(Default: `auto bottom-left`, Config: 'placement')_ - + Passing in 'auto' separated by a space before the placement will enable auto positioning, e.g: "auto bottom-left". The popup will attempt to position where it fits in the closest scrollable ancestor. Accepts: + + * `top` - popup on top, horizontally centered on input element. + * `top-left` - popup on top, left edge aligned with input element left edge. + * `top-right` - popup on top, right edge aligned with input element right edge. + * `bottom` - popup on bottom, horizontally centered on input element. + * `bottom-left` - popup on bottom, left edge aligned with input element left edge. + * `bottom-right` - popup on bottom, right edge aligned with input element right edge. + * `left` - popup on left, vertically centered on input element. + * `left-top` - popup on left, top edge aligned with input element top edge. + * `left-bottom` - popup on left, bottom edge aligned with input element bottom edge. + * `right` - popup on right, vertically centered on input element. + * `right-top` - popup on right, top edge aligned with input element top edge. + * `right-bottom` - popup on right, bottom edge aligned with input element bottom edge. + * `uib-datepicker-popup` C _(Default: `yyyy-MM-dd`, Config: `datepickerConfig`)_ -