diff --git a/src/components/input/input.js b/src/components/input/input.js index 2a34cf9db4b..d3e8c646d76 100644 --- a/src/components/input/input.js +++ b/src/components/input/input.js @@ -110,6 +110,7 @@ function labelDirective() { * The purpose of **`md-maxlength`** is exactly to show the max length counter text. If you don't want the counter text and only need "plain" validation, you can use the "simple" `ng-maxlength` or maxlength attributes. * @param {string=} aria-label Aria-label is required when no label is present. A warning message will be logged in the console if not present. * @param {string=} placeholder An alternative approach to using aria-label when the label is not present. The placeholder text is copied to the aria-label attribute. + * @param md-no-autogrow {boolean=} When present, textareas will not grow automatically. * * @usage * @@ -230,7 +231,21 @@ function inputTextareaDirective($mdUtil, $window, $mdAria) { } function setupTextarea() { + if(angular.isDefined(element.attr('md-no-autogrow'))) { + return; + } + var node = element[0]; + var container = containerCtrl.element[0]; + + var min_rows = NaN; + var lineHeight = null; + // can't check if height was or not explicity set, + // so rows attribute will take precedence if present + if(node.hasAttribute('rows')) { + min_rows = parseInt(node.getAttribute('rows')); + } + var onChangeTextarea = $mdUtil.debounce(growTextarea, 1); function pipelineListener(value) { @@ -245,19 +260,49 @@ function inputTextareaDirective($mdUtil, $window, $mdAria) { onChangeTextarea(); } element.on('keydown input', onChangeTextarea); - element.on('scroll', onScroll); + + if(isNaN(min_rows)) { + element.attr('rows', '1'); + + element.on('scroll', onScroll); + } + angular.element($window).on('resize', onChangeTextarea); - element.attr('rows', '1'); scope.$on('$destroy', function() { angular.element($window).off('resize', onChangeTextarea); }); function growTextarea() { - node.style.height = "auto"; - node.scrollTop = 0; - var height = getHeight(); - if (height) node.style.height = height + 'px'; + // sets the md-input-container height to avoid jumping around + container.style.height = container.offsetHeight + 'px'; + + // temporarily disables element's flex so its height 'runs free' + element.addClass('md-no-flex'); + + if(isNaN(min_rows)) { + node.style.height = "auto"; + node.scrollTop = 0; + var height = getHeight(); + if (height) node.style.height = height + 'px'; + } else { + node.setAttribute("rows", 1); + + if(!lineHeight) { + node.style.minHeight = '0'; + + lineHeight = element.height(); + + node.style.minHeight = null; + } + + var rows = Math.max(min_rows, Math.round(node.scrollHeight / lineHeight)); + node.setAttribute("rows", rows); + } + + // reset everything back to normal + element.removeClass('md-no-flex'); + container.style.height = 'auto'; } function getHeight () { diff --git a/src/components/input/input.scss b/src/components/input/input.scss index 97c0b08f050..b1c451a70ba 100644 --- a/src/components/input/input.scss +++ b/src/components/input/input.scss @@ -141,6 +141,10 @@ md-input-container { outline: none; box-shadow: none; } + + &.md-no-flex { + flex: none !important; + } } ng-messages, data-ng-messages, x-ng-messages,