Skip to content

Commit

Permalink
fix(input): incorrect height with autosize (angular#4084)
Browse files Browse the repository at this point in the history
Currently when using the `mdTextareaAutosize` directive the textarea height might be incorrect on component initialization.

This happens because the textarea `scrollHeight` property is not ready in the `ngOnInit` lifecycle hook yet.

Other libraries like `angular-elastic` have timeouts for that. But using the `ngAfterViewInit` lifecycle hook is more elegant and also ensures that the `scrollHeight` property is ready.

Also switches `offsetHeight` to `clientHeight` since we don't want to include the border in our line-height calculations.

Also by default `textarea` elements have a padding of `2px` and the `padding` needs to be explicitly set to `0px` when resolving the line-height.

Fixes angular#4070.
  • Loading branch information
EladBezalel committed Apr 15, 2017
1 parent 21f8899 commit 5c640d0
Show file tree
Hide file tree
Showing 27 changed files with 183 additions and 126 deletions.
4 changes: 2 additions & 2 deletions src/lib/autocomplete/autocomplete-trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {ConnectedPositionStrategy} from '../core/overlay/position/connected-posi
import {Observable} from 'rxjs/Observable';
import {MdOptionSelectionChange, MdOption} from '../core/option/option';
import {ENTER, UP_ARROW, DOWN_ARROW} from '../core/keyboard/keycodes';
import {Dir} from '../core/rtl/dir';
import {Directionality} from '../core/bidi/index';
import {Subscription} from 'rxjs/Subscription';
import {Subject} from 'rxjs/Subject';
import 'rxjs/add/observable/merge';
Expand Down Expand Up @@ -101,7 +101,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {

constructor(private _element: ElementRef, private _overlay: Overlay,
private _viewContainerRef: ViewContainerRef,
@Optional() private _dir: Dir, private _zone: NgZone,
@Optional() private _dir: Directionality, private _zone: NgZone,
@Optional() @Host() private _inputContainer: MdInputContainer) {}

ngOnDestroy() {
Expand Down
6 changes: 3 additions & 3 deletions src/lib/autocomplete/autocomplete.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {NoopAnimationsModule} from '@angular/platform-browser/animations';
import {MdAutocompleteModule, MdAutocompleteTrigger} from './index';
import {OverlayContainer} from '../core/overlay/overlay-container';
import {MdInputModule} from '../input/index';
import {Dir, LayoutDirection} from '../core/rtl/dir';
import {Directionality, Direction} from '../core/bidi/index';
import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms';
import {Subscription} from 'rxjs/Subscription';
import {ENTER, DOWN_ARROW, SPACE, UP_ARROW} from '../core/keyboard/keycodes';
Expand All @@ -22,7 +22,7 @@ import 'rxjs/add/operator/map';

describe('MdAutocomplete', () => {
let overlayContainerElement: HTMLElement;
let dir: LayoutDirection;
let dir: Direction;

beforeEach(async(() => {
dir = 'ltr';
Expand Down Expand Up @@ -51,7 +51,7 @@ describe('MdAutocomplete', () => {

return {getContainerElement: () => overlayContainerElement};
}},
{provide: Dir, useFactory: () => {
{provide: Directionality, useFactory: () => {
return {value: dir};
}},
{provide: ViewportRuler, useClass: FakeViewportRuler}
Expand Down
49 changes: 49 additions & 0 deletions src/lib/core/bidi/dir.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import {
Directive,
HostBinding,
Output,
Input,
EventEmitter
} from '@angular/core';

import {Direction, Directionality} from './directionality';

/**
* Directive to listen for changes of direction of part of the DOM.
*
* Would provide itself in case a component looks for the Directionality service
*/
@Directive({
selector: '[dir]',
// TODO(hansl): maybe `$implicit` isn't the best option here, but for now that's the best we got.
exportAs: '$implicit',
providers: [
{provide: Directionality, useExisting: Dir}
]
})
export class Dir implements Directionality {
/** Layout direction of the element. */
@Input('dir') _dir: Direction = 'ltr';

/** Event emitted when the direction changes. */
@Output() change = new EventEmitter<void>();

/** @docs-private */
@HostBinding('attr.dir')
get dir(): Direction {
return this._dir;
}

set dir(v: Direction) {
let old = this._dir;
this._dir = v;
if (old != this._dir) {
this.change.emit();
}
}

/** Current layout direction of the element. */
get value(): Direction { return this.dir; }
set value(v: Direction) { this.dir = v; }
}

41 changes: 41 additions & 0 deletions src/lib/core/bidi/directionality.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {
EventEmitter,
Injectable,
Optional,
SkipSelf
} from '@angular/core';

export type Direction = 'ltr' | 'rtl';

/**
* The directionality (LTR / RTL) context for the application (or a subtree of it).
* Exposes the current direction and a stream of direction changes.
*/
@Injectable()
export class Directionality {
value: Direction = 'ltr';
change: EventEmitter<void> = new EventEmitter<void>();

constructor() {
if (document) {
// TODO: handle auto value -
// We still need to account for dir="auto".
// It looks like HTMLElemenet.dir is also "auto" when that's set to the attribute,
// but getComputedStyle return either "ltr" or "rtl". avoiding getComputedStyle for now
// though, we're already calling it for the theming check.
this.value =
(document.body.getAttribute('dir') ||
document.documentElement.getAttribute('dir') ||
'ltr') as Direction;
}
}
}

export const DIRECTIONALITY_PROVIDER = {
// If there is already a Directionality available, use that. Otherwise, provide a new one.
provide: Directionality,
deps: [[new Optional(), new SkipSelf(), Directionality]],
useFactory: function (parentDirectionality) {
return parentDirectionality || new Directionality();
}
};
26 changes: 26 additions & 0 deletions src/lib/core/bidi/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {ModuleWithProviders, NgModule} from '@angular/core';
import {Dir} from './dir';
import {Directionality, DIRECTIONALITY_PROVIDER} from './directionality';

export {
Directionality,
DIRECTIONALITY_PROVIDER,
Direction
} from './directionality';
export {Dir} from './dir';


@NgModule({
exports: [Dir],
declarations: [Dir],
providers: [Directionality]
})
export class BidiModule {
/** @deprecated */
static forRoot(): ModuleWithProviders {
return {
ngModule: BidiModule,
providers: [DIRECTIONALITY_PROVIDER]
};
}
}
8 changes: 4 additions & 4 deletions src/lib/core/core.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {NgModule, ModuleWithProviders} from '@angular/core';
import {MdLineModule} from './line/line';
import {RtlModule} from './rtl/dir';
import {BidiModule} from './bidi/index';
import {ObserveContentModule} from './observe-content/observe-content';
import {MdOptionModule} from './option/option';
import {PortalModule} from './portal/portal-directives';
Expand All @@ -11,7 +11,7 @@ import {MdRippleModule} from './ripple/index';


// RTL
export {Dir, LayoutDirection, RtlModule} from './rtl/dir';
export {Dir, Direction, Directionality, BidiModule} from './bidi/index';

// Mutation Observer
export {ObserveContentModule, ObserveContent} from './observe-content/observe-content';
Expand Down Expand Up @@ -117,7 +117,7 @@ export {CompatibilityModule, NoConflictStyleCompatibilityMode} from './compatibi
@NgModule({
imports: [
MdLineModule,
RtlModule,
BidiModule,
MdRippleModule,
ObserveContentModule,
PortalModule,
Expand All @@ -128,7 +128,7 @@ export {CompatibilityModule, NoConflictStyleCompatibilityMode} from './compatibi
],
exports: [
MdLineModule,
RtlModule,
BidiModule,
MdRippleModule,
ObserveContentModule,
PortalModule,
Expand Down
4 changes: 2 additions & 2 deletions src/lib/core/overlay/overlay-directives.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {ConnectedOverlayDirective, OverlayModule} from './overlay-directives';
import {OverlayContainer} from './overlay-container';
import {ConnectedPositionStrategy} from './position/connected-position-strategy';
import {ConnectedOverlayPositionChange} from './position/connected-position';
import {Dir} from '../rtl/dir';
import {Directionality} from '../bidi/index';


describe('Overlay directives', () => {
Expand All @@ -22,7 +22,7 @@ describe('Overlay directives', () => {
overlayContainerElement = document.createElement('div');
return {getContainerElement: () => overlayContainerElement};
}},
{provide: Dir, useFactory: () => {
{provide: Directionality, useFactory: () => {
return dir = { value: 'ltr' };
}}
],
Expand Down
6 changes: 3 additions & 3 deletions src/lib/core/overlay/overlay-directives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
import {PortalModule} from '../portal/portal-directives';
import {ConnectedPositionStrategy} from './position/connected-position-strategy';
import {Subscription} from 'rxjs/Subscription';
import {Dir, LayoutDirection} from '../rtl/dir';
import {Directionality, Direction} from '../bidi/index';
import {Scrollable} from './scroll/scrollable';
import {coerceBooleanProperty} from '../coercion/boolean-property';

Expand Down Expand Up @@ -154,7 +154,7 @@ export class ConnectedOverlayDirective implements OnDestroy {
private _overlay: Overlay,
templateRef: TemplateRef<any>,
viewContainerRef: ViewContainerRef,
@Optional() private _dir: Dir) {
@Optional() private _dir: Directionality) {
this._templatePortal = new TemplatePortal(templateRef, viewContainerRef);
}

Expand All @@ -164,7 +164,7 @@ export class ConnectedOverlayDirective implements OnDestroy {
}

/** The element's layout direction. */
get dir(): LayoutDirection {
get dir(): Direction {
return this._dir ? this._dir.value : 'ltr';
}

Expand Down
4 changes: 2 additions & 2 deletions src/lib/core/overlay/overlay-state.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {PositionStrategy} from './position/position-strategy';
import {LayoutDirection} from '../rtl/dir';
import {Direction} from '../bidi/index';


/**
Expand Down Expand Up @@ -29,7 +29,7 @@ export class OverlayState {
minHeight: number | string;

/** The direction of the text in the overlay panel. */
direction: LayoutDirection = 'ltr';
direction: Direction = 'ltr';

// TODO(jelbourn): configuration still to add
// - focus trap
Expand Down
62 changes: 0 additions & 62 deletions src/lib/core/rtl/dir.ts

This file was deleted.

4 changes: 2 additions & 2 deletions src/lib/grid-list/grid-list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {MdGridTile} from './grid-tile';
import {TileCoordinator} from './tile-coordinator';
import {TileStyler, FitTileStyler, RatioTileStyler, FixedTileStyler} from './tile-styler';
import {MdGridListColsError} from './grid-list-errors';
import {Dir} from '../core';
import {Directionality} from '../core';
import {
coerceToString,
coerceToNumber,
Expand Down Expand Up @@ -62,7 +62,7 @@ export class MdGridList implements OnInit, AfterContentChecked {
constructor(
private _renderer: Renderer,
private _element: ElementRef,
@Optional() private _dir: Dir) {}
@Optional() private _dir: Directionality) {}

/** Amount of columns in the grid list. */
@Input()
Expand Down
8 changes: 4 additions & 4 deletions src/lib/menu/menu-trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import {MdMenuPanel} from './menu-panel';
import {MdMenuMissingError} from './menu-errors';
import {
isFakeMousedownFromScreenReader,
Dir,
LayoutDirection,
Directionality,
Direction,
Overlay,
OverlayState,
OverlayRef,
Expand Down Expand Up @@ -80,7 +80,7 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy {

constructor(private _overlay: Overlay, private _element: ElementRef,
private _viewContainerRef: ViewContainerRef, private _renderer: Renderer,
@Optional() private _dir: Dir) {}
@Optional() private _dir: Directionality) {}

ngAfterViewInit() {
this._checkMenu();
Expand Down Expand Up @@ -132,7 +132,7 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy {
}

/** The text direction of the containing app. */
get dir(): LayoutDirection {
get dir(): Direction {
return this._dir && this._dir.value === 'rtl' ? 'rtl' : 'ltr';
}

Expand Down
6 changes: 3 additions & 3 deletions src/lib/menu/menu.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ import {
} from './index';
import {OverlayContainer} from '../core/overlay/overlay-container';
import {ViewportRuler} from '../core/overlay/position/viewport-ruler';
import {Dir, LayoutDirection} from '../core/rtl/dir';
import {Directionality, Direction} from '../core/bidi/index';
import {extendObject} from '../core/util/object-extend';

describe('MdMenu', () => {
let overlayContainerElement: HTMLElement;
let dir: LayoutDirection;
let dir: Direction;

beforeEach(async(() => {
dir = 'ltr';
Expand All @@ -44,7 +44,7 @@ describe('MdMenu', () => {
document.body.style.margin = '0';
return {getContainerElement: () => overlayContainerElement};
}},
{provide: Dir, useFactory: () => {
{provide: Directionality, useFactory: () => {
return {value: dir};
}},
{provide: ViewportRuler, useClass: FakeViewportRuler}
Expand Down
Loading

0 comments on commit 5c640d0

Please sign in to comment.