Skip to content

Commit ce71c6c

Browse files
author
Ales Rechtorik
committed
make autosize work along rows
1 parent 16ae388 commit ce71c6c

File tree

2 files changed

+101
-5
lines changed

2 files changed

+101
-5
lines changed

src/demo-app/input/input-demo.html

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,25 @@ <h4>Textarea</h4>
7878
<md-card class="demo-card demo-basic">
7979
<md-toolbar color="primary">Expanding textarea</md-toolbar>
8080
<md-card-content>
81-
<p>
81+
<div>
8282
<md-textarea
8383
placeholder="Textarea with autosize"
8484
class="demo-full-width"
8585
autosize
8686
value="Hello world. How are you?">
8787
</md-textarea>
88-
</p>
88+
</div>
89+
90+
<h4>With `rows` explicitly set</h4>
91+
<div>
92+
<md-textarea
93+
placeholder="Textarea with autosize"
94+
class="demo-full-width"
95+
autosize
96+
rows="8"
97+
value="Hello world. How are you?">
98+
</md-textarea>
99+
</div>
89100
</md-card-content>
90101
</md-card>
91102

src/lib/input/input.ts

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
Input,
66
Directive,
77
AfterContentInit,
8+
Attribute,
89
OnInit,
910
ContentChild,
1011
HostListener,
@@ -97,32 +98,116 @@ export class MdHint {
9798
selector: 'textarea[mdAutosize]'
9899
})
99100
export class MdAutosize implements OnInit {
101+
// public because of AOT
100102
@HostListener('input')
101-
private _onChange() {
103+
public _onChange() {
102104
if (this._isActive) {
103105
this._adjust();
104106
}
105107
}
106108

109+
// public because of AOT
107110
@Input('mdAutosize')
108-
private _isActive: boolean;
111+
public _isActive: boolean;
112+
113+
// public because of AOT
114+
// TODO: change to a setter, to recalculate on change
115+
@Input('attr.rows')
116+
public _rows: string;
117+
118+
private _height: number;
119+
private _lineHeight: number;
109120

110121
constructor(private _elRef: ElementRef) {}
111122

112123
ngOnInit() {
113124
if (this._isActive) {
114125
this._elRef.nativeElement.style.overflow = 'hidden';
126+
127+
// check is rows is explicitly set, otherwise we don't need to
128+
// care about it(and getting the actual line-height) at all
129+
if (this._rows) {
130+
this._lineHeight = this._getLineHeight();
131+
this._elRef.nativeElement.style.minHeight = `${+this._rows * this._lineHeight}px`;
132+
}
133+
115134
this._adjust();
116135
}
117136
}
118137

119138
private _adjust() {
139+
// reset height(and rows if it was set by the user)
140+
// to the default values to properly calculate new height
120141
this._elRef.nativeElement.style.height = 'auto';
121-
this._elRef.nativeElement.style.height = `${this._elRef.nativeElement.scrollHeight}px`;
142+
143+
// only need to do this if user explicitly set rows
144+
if (this._rows) {
145+
this._elRef.nativeElement.rows = this._rows;
146+
}
147+
148+
this._height = +this._elRef.nativeElement.scrollHeight;
149+
150+
// only need to do this if user explicitly set rows
151+
if (this._rows) {
152+
this._elRef.nativeElement.rows = this._getCalculatedRows();
153+
}
154+
155+
this._elRef.nativeElement.style.height = `${this._height}px`;
156+
}
157+
158+
private _getCalculatedRows() {
159+
return Math.round(this._height / this._lineHeight);
160+
}
161+
162+
private _getLineHeight(): number {
163+
const el: HTMLElement = this._elRef.nativeElement;
164+
165+
// get the actual computed styles for the element
166+
const computedStyles = window.getComputedStyle(el);
167+
168+
// if line height is explicitly set(to a pixel value), use that
169+
if (computedStyles.lineHeight && computedStyles.lineHeight.toLowerCase().indexOf('px') >= 0) {
170+
// return stripped of the "px" and as a number
171+
return +computedStyles.lineHeight.replace('px', '');
172+
}
173+
174+
// create temporary element
175+
const tempEl = document.createElement(el.nodeName);
176+
177+
// reset styling, visually hiden the element
178+
// and set its font styles to match the ones of our textarea
179+
tempEl.setAttribute('rows', '1');
180+
tempEl.setAttribute(
181+
'style',
182+
`
183+
margin: 0px;
184+
padding: 0px;
185+
visibility: hidden;
186+
opacity: 0;
187+
font-family: ${computedStyles.fontFamily};
188+
font-size: ${computedStyles.fontSize};
189+
`
190+
);
191+
192+
// fill in one row
193+
tempEl.innerHTML = '-';
194+
195+
// append to parent element to correctly inherit same values
196+
// as the actual textarea
197+
el.parentNode.appendChild(tempEl);
198+
199+
// get the actual line height
200+
const lineHeight = tempEl.clientHeight;
201+
202+
// cleanup
203+
tempEl.parentNode.removeChild(tempEl);
204+
205+
return lineHeight;
122206
}
123207
}
124208

125209

210+
126211
/**
127212
* Component that represents a text input. It encapsulates the <input> HTMLElement and
128213
* improve on its behaviour, along with styling it according to the Material Design.

0 commit comments

Comments
 (0)