|
5 | 5 | Input,
|
6 | 6 | Directive,
|
7 | 7 | AfterContentInit,
|
| 8 | + Attribute, |
8 | 9 | OnInit,
|
9 | 10 | ContentChild,
|
10 | 11 | HostListener,
|
@@ -97,32 +98,116 @@ export class MdHint {
|
97 | 98 | selector: 'textarea[mdAutosize]'
|
98 | 99 | })
|
99 | 100 | export class MdAutosize implements OnInit {
|
| 101 | + // public because of AOT |
100 | 102 | @HostListener('input')
|
101 |
| - private _onChange() { |
| 103 | + public _onChange() { |
102 | 104 | if (this._isActive) {
|
103 | 105 | this._adjust();
|
104 | 106 | }
|
105 | 107 | }
|
106 | 108 |
|
| 109 | + // public because of AOT |
107 | 110 | @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; |
109 | 120 |
|
110 | 121 | constructor(private _elRef: ElementRef) {}
|
111 | 122 |
|
112 | 123 | ngOnInit() {
|
113 | 124 | if (this._isActive) {
|
114 | 125 | 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 | + |
115 | 134 | this._adjust();
|
116 | 135 | }
|
117 | 136 | }
|
118 | 137 |
|
119 | 138 | private _adjust() {
|
| 139 | + // reset height(and rows if it was set by the user) |
| 140 | + // to the default values to properly calculate new height |
120 | 141 | 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; |
122 | 206 | }
|
123 | 207 | }
|
124 | 208 |
|
125 | 209 |
|
| 210 | + |
126 | 211 | /**
|
127 | 212 | * Component that represents a text input. It encapsulates the <input> HTMLElement and
|
128 | 213 | * improve on its behaviour, along with styling it according to the Material Design.
|
|
0 commit comments