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