Skip to content

Commit 45c7c84

Browse files
author
Ales Rechtorik
committed
make autosize work along rows
1 parent 7076914 commit 45c7c84

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,
@@ -101,32 +102,116 @@ export class MdHint {
101102
selector: 'textarea[mdAutosize]'
102103
})
103104
export class MdAutosize implements OnInit {
105+
// public because of AOT
104106
@HostListener('input')
105-
private _onChange() {
107+
public _onChange() {
106108
if (this._isActive) {
107109
this._adjust();
108110
}
109111
}
110112

113+
// public because of AOT
111114
@Input('mdAutosize')
112-
private _isActive: boolean;
115+
public _isActive: boolean;
116+
117+
// public because of AOT
118+
// TODO: change to a setter, to recalculate on change
119+
@Input('attr.rows')
120+
public _rows: string;
121+
122+
private _height: number;
123+
private _lineHeight: number;
113124

114125
constructor(private _elRef: ElementRef) {}
115126

116127
ngOnInit() {
117128
if (this._isActive) {
118129
this._elRef.nativeElement.style.overflow = 'hidden';
130+
131+
// check is rows is explicitly set, otherwise we don't need to
132+
// care about it(and getting the actual line-height) at all
133+
if (this._rows) {
134+
this._lineHeight = this._getLineHeight();
135+
this._elRef.nativeElement.style.minHeight = `${+this._rows * this._lineHeight}px`;
136+
}
137+
119138
this._adjust();
120139
}
121140
}
122141

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

129213

214+
130215
/**
131216
* Component that represents a text input. It encapsulates the <input> HTMLElement and
132217
* improve on its behaviour, along with styling it according to the Material Design.

0 commit comments

Comments
 (0)