diff --git a/src/demo-app/autocomplete/autocomplete-demo.html b/src/demo-app/autocomplete/autocomplete-demo.html index ba732cbc781e..69057c719bf9 100644 --- a/src/demo-app/autocomplete/autocomplete-demo.html +++ b/src/demo-app/autocomplete/autocomplete-demo.html @@ -53,4 +53,4 @@ {{ state.name }} - \ No newline at end of file + diff --git a/src/lib/autocomplete/autocomplete-trigger.ts b/src/lib/autocomplete/autocomplete-trigger.ts index d446158def29..ee98bc35fee3 100644 --- a/src/lib/autocomplete/autocomplete-trigger.ts +++ b/src/lib/autocomplete/autocomplete-trigger.ts @@ -119,9 +119,6 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy { private _portal: TemplatePortal; private _panelOpen: boolean = false; - /** The subscription to positioning changes in the autocomplete panel. */ - private _panelPositionSubscription: Subscription; - /** Strategy that is used to position the panel. */ private _positionStrategy: ConnectedPositionStrategy; @@ -160,10 +157,6 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy { @Optional() @Inject(DOCUMENT) private _document: any) {} ngOnDestroy() { - if (this._panelPositionSubscription) { - this._panelPositionSubscription.unsubscribe(); - } - this._destroyPanel(); } @@ -467,28 +460,21 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy { private _getOverlayPosition(): PositionStrategy { this._positionStrategy = this._overlay.position().connectedTo( - this._element, + this._getConnectedElement(), {originX: 'start', originY: 'bottom'}, {overlayX: 'start', overlayY: 'top'}) .withFallbackPosition( {originX: 'start', originY: 'top'}, {overlayX: 'start', overlayY: 'bottom'} ); - this._subscribeToPositionChanges(this._positionStrategy); return this._positionStrategy; } - /** - * This method subscribes to position changes in the autocomplete panel, so the panel's - * y-offset can be adjusted to match the new position. - */ - private _subscribeToPositionChanges(strategy: ConnectedPositionStrategy) { - this._panelPositionSubscription = strategy.onPositionChange.subscribe(change => { - this.autocomplete.positionY = change.connectionPair.originY === 'top' ? 'above' : 'below'; - }); + private _getConnectedElement(): ElementRef { + return this._inputContainer ? this._inputContainer._connectionContainerRef : this._element; } /** Returns the width of the input element, so the panel width can match it. */ private _getHostWidth(): number { - return this._element.nativeElement.getBoundingClientRect().width; + return this._getConnectedElement().nativeElement.getBoundingClientRect().width; } /** Reset active item to -1 so arrow events will activate the correct options.*/ diff --git a/src/lib/autocomplete/autocomplete.scss b/src/lib/autocomplete/autocomplete.scss index 297b085473b3..03d7e62f7b53 100644 --- a/src/lib/autocomplete/autocomplete.scss +++ b/src/lib/autocomplete/autocomplete.scss @@ -6,18 +6,6 @@ */ $mat-autocomplete-panel-max-height: 256px !default; -/** - * When in "below" position, the panel needs a slight - * y-offset to ensure the input underline displays. - */ -$mat-autocomplete-panel-below-offset: 6px !default; - -/** - * When in "above" position, the panel needs a larger - * y-offset to ensure the label has room to display. - */ -$mat-autocomplete-panel-above-offset: -24px !default; - .mat-autocomplete-panel { @include mat-menu-base(8); visibility: hidden; @@ -26,14 +14,6 @@ $mat-autocomplete-panel-above-offset: -24px !default; max-height: $mat-autocomplete-panel-max-height; position: relative; - &.mat-autocomplete-panel-below { - top: $mat-autocomplete-panel-below-offset; - } - - &.mat-autocomplete-panel-above { - top: $mat-autocomplete-panel-above-offset; - } - &.mat-autocomplete-visible { visibility: visible; } diff --git a/src/lib/autocomplete/autocomplete.spec.ts b/src/lib/autocomplete/autocomplete.spec.ts index f9b5f38bd1b6..927cddd98b0a 100644 --- a/src/lib/autocomplete/autocomplete.spec.ts +++ b/src/lib/autocomplete/autocomplete.spec.ts @@ -1055,27 +1055,26 @@ describe('MdAutocomplete', () => { describe('Fallback positions', () => { let fixture: ComponentFixture; let input: HTMLInputElement; + let inputReference: HTMLInputElement; beforeEach(() => { fixture = TestBed.createComponent(SimpleAutocomplete); fixture.detectChanges(); input = fixture.debugElement.query(By.css('input')).nativeElement; + inputReference = fixture.debugElement.query(By.css('.mat-input-flex')).nativeElement; }); it('should use below positioning by default', () => { fixture.componentInstance.trigger.openPanel(); fixture.detectChanges(); - const inputBottom = input.getBoundingClientRect().bottom; + const inputBottom = inputReference.getBoundingClientRect().bottom; const panel = overlayContainerElement.querySelector('.mat-autocomplete-panel')!; const panelTop = panel.getBoundingClientRect().top; - // Panel is offset by 6px in styles so that the underline has room to display. - expect(Math.floor(inputBottom + 6)) + expect(Math.floor(inputBottom)) .toEqual(Math.floor(panelTop), `Expected panel top to match input bottom by default.`); - expect(fixture.componentInstance.trigger.autocomplete.positionY) - .toEqual('below', `Expected autocomplete positionY to default to below.`); }); it('should reposition the panel on scroll', () => { @@ -1091,11 +1090,11 @@ describe('MdAutocomplete', () => { scrolledSubject.next(); fixture.detectChanges(); - const inputBottom = input.getBoundingClientRect().bottom; + const inputBottom = inputReference.getBoundingClientRect().bottom; const panel = overlayContainerElement.querySelector('.mat-autocomplete-panel')!; const panelTop = panel.getBoundingClientRect().top; - expect(Math.floor(inputBottom + 6)).toEqual(Math.floor(panelTop), + expect(Math.floor(inputBottom)).toEqual(Math.floor(panelTop), 'Expected panel top to match input bottom after scrolling.'); document.body.removeChild(spacer); @@ -1103,27 +1102,24 @@ describe('MdAutocomplete', () => { it('should fall back to above position if panel cannot fit below', () => { // Push the autocomplete trigger down so it won't have room to open "below" - input.style.top = '600px'; - input.style.position = 'relative'; + inputReference.style.top = '600px'; + inputReference.style.position = 'relative'; fixture.componentInstance.trigger.openPanel(); fixture.detectChanges(); - const inputTop = input.getBoundingClientRect().top; + const inputTop = inputReference.getBoundingClientRect().top; const panel = overlayContainerElement.querySelector('.mat-autocomplete-panel')!; const panelBottom = panel.getBoundingClientRect().bottom; - // Panel is offset by 24px in styles so that the label has room to display. - expect(Math.floor(inputTop - 24)) + expect(Math.floor(inputTop)) .toEqual(Math.floor(panelBottom), `Expected panel to fall back to above position.`); - expect(fixture.componentInstance.trigger.autocomplete.positionY) - .toEqual('above', `Expected autocomplete positionY to be "above" if panel won't fit.`); }); it('should align panel properly when filtering in "above" position', async(() => { // Push the autocomplete trigger down so it won't have room to open "below" - input.style.top = '600px'; - input.style.position = 'relative'; + inputReference.style.top = '600px'; + inputReference.style.position = 'relative'; fixture.componentInstance.trigger.openPanel(); fixture.detectChanges(); @@ -1132,15 +1128,12 @@ describe('MdAutocomplete', () => { typeInElement('f', input); fixture.detectChanges(); - const inputTop = input.getBoundingClientRect().top; + const inputTop = inputReference.getBoundingClientRect().top; const panel = overlayContainerElement.querySelector('.mat-autocomplete-panel')!; const panelBottom = panel.getBoundingClientRect().bottom; - // Panel is offset by 24px in styles so that the label has room to display. - expect(Math.floor(inputTop - 24)) + expect(Math.floor(inputTop)) .toEqual(Math.floor(panelBottom), `Expected panel to stay aligned after filtering.`); - expect(fixture.componentInstance.trigger.autocomplete.positionY) - .toEqual('above', `Expected autocomplete positionY to be "above" if panel won't fit.`); }); })); diff --git a/src/lib/autocomplete/autocomplete.ts b/src/lib/autocomplete/autocomplete.ts index 0937d1ee2f99..92ffb49bd89d 100644 --- a/src/lib/autocomplete/autocomplete.ts +++ b/src/lib/autocomplete/autocomplete.ts @@ -28,8 +28,6 @@ import {ActiveDescendantKeyManager} from '../core/a11y/activedescendant-key-mana */ let _uniqueAutocompleteIdCounter = 0; -export type AutocompletePositionY = 'above' | 'below'; - @Component({ moduleId: module.id, selector: 'md-autocomplete, mat-autocomplete', @@ -47,9 +45,6 @@ export class MdAutocomplete implements AfterContentInit { /** Manages active item in option list based on key events. */ _keyManager: ActiveDescendantKeyManager; - /** Whether the autocomplete panel displays above or below its trigger. */ - positionY: AutocompletePositionY = 'below'; - /** Whether the autocomplete panel should be visible, depending on option length. */ showPanel = false; @@ -97,11 +92,9 @@ export class MdAutocomplete implements AfterContentInit { }); } - /** Sets a class on the panel based on its position (used to set y-offset). */ + /** Sets a class on the panel based on whether it is visible. */ _getClassList() { return { - 'mat-autocomplete-panel-below': this.positionY === 'below', - 'mat-autocomplete-panel-above': this.positionY === 'above', 'mat-autocomplete-visible': this.showPanel, 'mat-autocomplete-hidden': !this.showPanel }; diff --git a/src/lib/input/input-container.html b/src/lib/input/input-container.html index 05b9ebabb24f..b2e4f97393ea 100644 --- a/src/lib/input/input-container.html +++ b/src/lib/input/input-container.html @@ -1,5 +1,5 @@
-
+
diff --git a/src/lib/input/input-container.ts b/src/lib/input/input-container.ts index e186f62eaeb4..e24fb187d7d3 100644 --- a/src/lib/input/input-container.ts +++ b/src/lib/input/input-container.ts @@ -457,6 +457,7 @@ export class MdInputContainer implements AfterViewInit, AfterContentInit, AfterC /** Reference to the input's underline element. */ @ViewChild('underline') underlineRef: ElementRef; + @ViewChild('connectionContainer') _connectionContainerRef: ElementRef; @ContentChild(MdInputDirective) _mdInputChild: MdInputDirective; @ContentChild(MdPlaceholder) _placeholderChild: MdPlaceholder; @ContentChildren(MdErrorDirective) _errorChildren: QueryList;