diff --git a/src/demo-app/autocomplete/autocomplete-demo.html b/src/demo-app/autocomplete/autocomplete-demo.html
index 0745c4ac4ea0..3ab4a1eaabd5 100644
--- a/src/demo-app/autocomplete/autocomplete-demo.html
+++ b/src/demo-app/autocomplete/autocomplete-demo.html
@@ -1,3 +1,9 @@
-
+
+
+
+
+
+ {{ state.name }}
+
diff --git a/src/demo-app/autocomplete/autocomplete-demo.ts b/src/demo-app/autocomplete/autocomplete-demo.ts
index b89197833174..c06a099fd343 100644
--- a/src/demo-app/autocomplete/autocomplete-demo.ts
+++ b/src/demo-app/autocomplete/autocomplete-demo.ts
@@ -6,4 +6,33 @@ import {Component} from '@angular/core';
templateUrl: 'autocomplete-demo.html',
styleUrls: ['autocomplete-demo.css'],
})
-export class AutocompleteDemo {}
+export class AutocompleteDemo {
+ states = [
+ {code: 'AL', name: 'Alabama'},
+ {code: 'AZ', name: 'Arizona'},
+ {code: 'CA', name: 'California'},
+ {code: 'CO', name: 'Colorado'},
+ {code: 'CT', name: 'Connecticut'},
+ {code: 'FL', name: 'Florida'},
+ {code: 'GA', name: 'Georgia'},
+ {code: 'ID', name: 'Idaho'},
+ {code: 'KS', name: 'Kansas'},
+ {code: 'LA', name: 'Louisiana'},
+ {code: 'MA', name: 'Massachusetts'},
+ {code: 'MN', name: 'Minnesota'},
+ {code: 'MI', name: 'Mississippi'},
+ {code: 'NY', name: 'New York'},
+ {code: 'NC', name: 'North Carolina'},
+ {code: 'OK', name: 'Oklahoma'},
+ {code: 'OH', name: 'Ohio'},
+ {code: 'OR', name: 'Oregon'},
+ {code: 'PA', name: 'Pennsylvania'},
+ {code: 'SC', name: 'South Carolina'},
+ {code: 'TN', name: 'Tennessee'},
+ {code: 'TX', name: 'Texas'},
+ {code: 'VA', name: 'Virginia'},
+ {code: 'WA', name: 'Washington'},
+ {code: 'WI', name: 'Wisconsin'},
+ {code: 'WY', name: 'Wyoming'},
+ ];
+}
diff --git a/src/lib/autocomplete/_autocomplete-theme.scss b/src/lib/autocomplete/_autocomplete-theme.scss
index b77fff253e0c..5d0493df039c 100644
--- a/src/lib/autocomplete/_autocomplete-theme.scss
+++ b/src/lib/autocomplete/_autocomplete-theme.scss
@@ -1,5 +1,16 @@
@import '../core/theming/theming';
@mixin md-autocomplete-theme($theme) {
+ $foreground: map-get($theme, foreground);
+ $background: map-get($theme, background);
+ md-option {
+ background: md-color($background, card);
+ color: md-color($foreground, text);
+
+ &.md-selected {
+ background: md-color($background, card);
+ color: md-color($foreground, text);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/lib/autocomplete/autocomplete-trigger.ts b/src/lib/autocomplete/autocomplete-trigger.ts
new file mode 100644
index 000000000000..c037b403a096
--- /dev/null
+++ b/src/lib/autocomplete/autocomplete-trigger.ts
@@ -0,0 +1,116 @@
+import {Directive, ElementRef, Input, ViewContainerRef, OnDestroy} from '@angular/core';
+import {Overlay, OverlayRef, OverlayState, TemplatePortal} from '../core';
+import {MdAutocomplete} from './autocomplete';
+import {PositionStrategy} from '../core/overlay/position/position-strategy';
+import {Observable} from 'rxjs/Observable';
+import {Subscription} from 'rxjs/Subscription';
+import 'rxjs/add/observable/merge';
+
+/** The panel needs a slight y-offset to ensure the input underline displays. */
+export const MD_AUTOCOMPLETE_PANEL_OFFSET = 6;
+
+@Directive({
+ selector: 'input[mdAutocomplete], input[matAutocomplete]',
+ host: {
+ '(focus)': 'openPanel()'
+ }
+})
+export class MdAutocompleteTrigger implements OnDestroy {
+ private _overlayRef: OverlayRef;
+ private _portal: TemplatePortal;
+ private _panelOpen: boolean = false;
+ private _closeWatcher: Subscription;
+
+ /* The autocomplete panel to be attached to this trigger. */
+ @Input('mdAutocomplete') autocomplete: MdAutocomplete;
+
+ constructor(private _element: ElementRef, private _overlay: Overlay,
+ private _vcr: ViewContainerRef) {}
+
+ ngOnDestroy() { this.destroyPanel(); }
+
+ /* Whether or not the autocomplete panel is open. */
+ get panelOpen(): boolean {
+ return this._panelOpen;
+ }
+
+ /** Opens the autocomplete suggestion panel. */
+ openPanel(): void {
+ if (!this._overlayRef) {
+ this._createOverlay();
+ }
+
+ if (!this._overlayRef.hasAttached()) {
+ this._overlayRef.attach(this._portal);
+ this._watchForClose();
+ }
+
+ this._panelOpen = true;
+ }
+
+ /** Closes the autocomplete suggestion panel. */
+ closePanel(): void {
+ if (this._overlayRef && this._overlayRef.hasAttached()) {
+ this._overlayRef.detach();
+ }
+
+ this._closeWatcher.unsubscribe();
+ this._panelOpen = false;
+ }
+
+ /** Destroys the autocomplete suggestion panel. */
+ destroyPanel(): void {
+ if (this._overlayRef) {
+ this.closePanel();
+ this._overlayRef.dispose();
+ this._overlayRef = null;
+ }
+ }
+
+ /**
+ * This method will close the panel if it receives a selection event from any of the options
+ * or a click on the backdrop.
+ */
+ private _watchForClose() {
+ // TODO(kara): add tab event watcher when adding keyboard events
+ this._closeWatcher = Observable.merge(...this._getOptionObs(), this._overlayRef.backdropClick())
+ .subscribe(() => this.closePanel());
+ }
+
+ /**
+ * This method maps all the autocomplete's child options into a flattened list
+ * of their selection events. This map will be used to merge the option events and
+ * the backdrop click into one observable.
+ */
+ private _getOptionObs(): Observable[] {
+ return this.autocomplete.options.map((option) => option.onSelect);
+ }
+
+ private _createOverlay(): void {
+ this._portal = new TemplatePortal(this.autocomplete.template, this._vcr);
+ this._overlayRef = this._overlay.create(this._getOverlayConfig());
+ }
+
+ private _getOverlayConfig(): OverlayState {
+ const overlayState = new OverlayState();
+ overlayState.positionStrategy = this._getOverlayPosition();
+ overlayState.width = this._getHostWidth();
+ overlayState.hasBackdrop = true;
+ overlayState.backdropClass = 'md-overlay-transparent-backdrop';
+ return overlayState;
+ }
+
+ private _getOverlayPosition(): PositionStrategy {
+ return this._overlay.position().connectedTo(
+ this._element,
+ {originX: 'start', originY: 'bottom'}, {overlayX: 'start', overlayY: 'top'})
+ .withOffsetY(MD_AUTOCOMPLETE_PANEL_OFFSET);
+ }
+
+ /** Returns the width of the input element, so the panel width can match it. */
+ private _getHostWidth(): number {
+ return this._element.nativeElement.getBoundingClientRect().width;
+ }
+
+}
+
diff --git a/src/lib/autocomplete/autocomplete.html b/src/lib/autocomplete/autocomplete.html
index f0d1cbc032a0..97727158af0c 100644
--- a/src/lib/autocomplete/autocomplete.html
+++ b/src/lib/autocomplete/autocomplete.html
@@ -1 +1,5 @@
-I'm an autocomplete!
\ No newline at end of file
+
+
+
+
+
\ No newline at end of file
diff --git a/src/lib/autocomplete/autocomplete.scss b/src/lib/autocomplete/autocomplete.scss
index e69de29bb2d1..d6c9b0162d35 100644
--- a/src/lib/autocomplete/autocomplete.scss
+++ b/src/lib/autocomplete/autocomplete.scss
@@ -0,0 +1,5 @@
+@import '../core/style/menu-common';
+
+.md-autocomplete-panel {
+ @include md-menu-base();
+}
\ No newline at end of file
diff --git a/src/lib/autocomplete/autocomplete.spec.ts b/src/lib/autocomplete/autocomplete.spec.ts
index 158a6cf263d6..8af275692b3a 100644
--- a/src/lib/autocomplete/autocomplete.spec.ts
+++ b/src/lib/autocomplete/autocomplete.spec.ts
@@ -1,29 +1,188 @@
-import {TestBed, async} from '@angular/core/testing';
-import {Component} from '@angular/core';
-import {MdAutocompleteModule} from './index';
+import {TestBed, async, ComponentFixture} from '@angular/core/testing';
+import {Component, ViewChild} from '@angular/core';
+import {By} from '@angular/platform-browser';
+import {MdAutocompleteModule, MdAutocompleteTrigger} from './index';
+import {OverlayContainer} from '../core/overlay/overlay-container';
+import {MdInputModule} from '../input/index';
describe('MdAutocomplete', () => {
+ let overlayContainerElement: HTMLElement;
beforeEach(async(() => {
TestBed.configureTestingModule({
- imports: [MdAutocompleteModule.forRoot()],
+ imports: [MdAutocompleteModule.forRoot(), MdInputModule.forRoot()],
declarations: [SimpleAutocomplete],
- providers: []
+ providers: [
+ {provide: OverlayContainer, useFactory: () => {
+ overlayContainerElement = document.createElement('div');
+
+ // add fixed positioning to match real overlay container styles
+ overlayContainerElement.style.position = 'fixed';
+ overlayContainerElement.style.top = '0';
+ overlayContainerElement.style.left = '0';
+ document.body.appendChild(overlayContainerElement);
+
+ // remove body padding to keep consistent cross-browser
+ document.body.style.padding = '0';
+ document.body.style.margin = '0';
+
+ return {getContainerElement: () => overlayContainerElement};
+ }},
+ ]
});
TestBed.compileComponents();
}));
- it('should have a test', () => {
- expect(true).toBe(true);
+ describe('panel toggling', () => {
+ let fixture: ComponentFixture;
+ let trigger: HTMLElement;
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(SimpleAutocomplete);
+ fixture.detectChanges();
+
+ trigger = fixture.debugElement.query(By.css('input')).nativeElement;
+ });
+
+ it('should open the panel when the input is focused', () => {
+ expect(fixture.componentInstance.trigger.panelOpen).toBe(false);
+ dispatchEvent('focus', trigger);
+ fixture.detectChanges();
+
+ expect(fixture.componentInstance.trigger.panelOpen)
+ .toBe(true, `Expected panel state to read open when input is focused.`);
+ expect(overlayContainerElement.textContent)
+ .toContain('Alabama', `Expected panel to display when input is focused.`);
+ expect(overlayContainerElement.textContent)
+ .toContain('California', `Expected panel to display when input is focused.`);
+ });
+
+ it('should open the panel programmatically', () => {
+ expect(fixture.componentInstance.trigger.panelOpen).toBe(false);
+ fixture.componentInstance.trigger.openPanel();
+ fixture.detectChanges();
+
+ expect(fixture.componentInstance.trigger.panelOpen)
+ .toBe(true, `Expected panel state to read open when opened programmatically.`);
+ expect(overlayContainerElement.textContent)
+ .toContain('Alabama', `Expected panel to display when opened programmatically.`);
+ expect(overlayContainerElement.textContent)
+ .toContain('California', `Expected panel to display when opened programmatically.`);
+ });
+
+ it('should close the panel when a click occurs outside it', async(() => {
+ dispatchEvent('focus', trigger);
+ fixture.detectChanges();
+
+ const backdrop = overlayContainerElement.querySelector('.cdk-overlay-backdrop');
+ backdrop.click();
+ fixture.detectChanges();
+
+ fixture.whenStable().then(() => {
+ expect(fixture.componentInstance.trigger.panelOpen)
+ .toBe(false, `Expected clicking outside the panel to set its state to closed.`);
+ expect(overlayContainerElement.textContent)
+ .toEqual('', `Expected clicking outside the panel to close the panel.`);
+ });
+ }));
+
+ it('should close the panel when an option is clicked', async(() => {
+ dispatchEvent('focus', trigger);
+ fixture.detectChanges();
+
+ const option = overlayContainerElement.querySelector('md-option');
+ option.click();
+ fixture.detectChanges();
+
+ fixture.whenStable().then(() => {
+ expect(fixture.componentInstance.trigger.panelOpen)
+ .toBe(false, `Expected clicking an option to set the panel state to closed.`);
+ expect(overlayContainerElement.textContent)
+ .toEqual('', `Expected clicking an option to close the panel.`);
+ });
+ }));
+
+ it('should close the panel when a newly created option is clicked', async(() => {
+ fixture.componentInstance.states.unshift({code: 'TEST', name: 'test'});
+ fixture.detectChanges();
+
+ dispatchEvent('focus', trigger);
+ fixture.detectChanges();
+
+ const option = overlayContainerElement.querySelector('md-option');
+ option.click();
+ fixture.detectChanges();
+
+ fixture.whenStable().then(() => {
+ expect(fixture.componentInstance.trigger.panelOpen)
+ .toBe(false, `Expected clicking a new option to set the panel state to closed.`);
+ expect(overlayContainerElement.textContent)
+ .toEqual('', `Expected clicking a new option to close the panel.`);
+ });
+ }));
+
+ it('should close the panel programmatically', async(() => {
+ fixture.componentInstance.trigger.openPanel();
+ fixture.detectChanges();
+
+ fixture.componentInstance.trigger.closePanel();
+ fixture.detectChanges();
+
+ fixture.whenStable().then(() => {
+ expect(fixture.componentInstance.trigger.panelOpen)
+ .toBe(false, `Expected closing programmatically to set the panel state to closed.`);
+ expect(overlayContainerElement.textContent)
+ .toEqual('', `Expected closing programmatically to close the panel.`);
+ });
+ }));
+
});
});
@Component({
template: `
-
+
+
+
+
+
+ {{ state.name }}
+
`
})
-class SimpleAutocomplete {}
+class SimpleAutocomplete {
+ @ViewChild(MdAutocompleteTrigger) trigger: MdAutocompleteTrigger;
+
+ states = [
+ {code: 'AL', name: 'Alabama'},
+ {code: 'CA', name: 'California'},
+ {code: 'FL', name: 'Florida'},
+ {code: 'KS', name: 'Kansas'},
+ {code: 'MA', name: 'Massachusetts'},
+ {code: 'NY', name: 'New York'},
+ {code: 'OR', name: 'Oregon'},
+ {code: 'PA', name: 'Pennsylvania'},
+ {code: 'TN', name: 'Tennessee'},
+ {code: 'VA', name: 'Virginia'},
+ {code: 'WY', name: 'Wyoming'},
+ ];
+}
+
+
+/**
+ * TODO: Move this to core testing utility until Angular has event faking
+ * support.
+ *
+ * Dispatches an event from an element.
+ * @param eventName Name of the event
+ * @param element The element from which the event will be dispatched.
+ */
+function dispatchEvent(eventName: string, element: HTMLElement): void {
+ let event = document.createEvent('Event');
+ event.initEvent(eventName, true, true);
+ element.dispatchEvent(event);
+}
+
diff --git a/src/lib/autocomplete/autocomplete.ts b/src/lib/autocomplete/autocomplete.ts
index 18545cb2c6e2..bb2abbca20b4 100644
--- a/src/lib/autocomplete/autocomplete.ts
+++ b/src/lib/autocomplete/autocomplete.ts
@@ -1,4 +1,12 @@
-import {Component, ViewEncapsulation} from '@angular/core';
+import {
+ Component,
+ ContentChildren,
+ QueryList,
+ TemplateRef,
+ ViewChild,
+ ViewEncapsulation
+} from '@angular/core';
+import {MdOption} from '../core';
@Component({
moduleId: module.id,
@@ -6,6 +14,11 @@ import {Component, ViewEncapsulation} from '@angular/core';
templateUrl: 'autocomplete.html',
styleUrls: ['autocomplete.css'],
encapsulation: ViewEncapsulation.None,
+ exportAs: 'mdAutocomplete'
})
-export class MdAutocomplete {}
+export class MdAutocomplete {
+
+ @ViewChild(TemplateRef) template: TemplateRef;
+ @ContentChildren(MdOption) options: QueryList;
+}
diff --git a/src/lib/autocomplete/index.ts b/src/lib/autocomplete/index.ts
index 92bb41a39a65..a9c4eec768fc 100644
--- a/src/lib/autocomplete/index.ts
+++ b/src/lib/autocomplete/index.ts
@@ -1,18 +1,24 @@
import {ModuleWithProviders, NgModule} from '@angular/core';
-import {DefaultStyleCompatibilityModeModule} from '../core';
+import {
+ MdOptionModule, OverlayModule, OVERLAY_PROVIDERS, DefaultStyleCompatibilityModeModule
+} from '../core';
import {MdAutocomplete} from './autocomplete';
+import {MdAutocompleteTrigger} from './autocomplete-trigger';
export * from './autocomplete';
+export * from './autocomplete-trigger';
@NgModule({
- imports: [DefaultStyleCompatibilityModeModule],
- exports: [MdAutocomplete, DefaultStyleCompatibilityModeModule],
- declarations: [MdAutocomplete],
+ imports: [MdOptionModule, OverlayModule, DefaultStyleCompatibilityModeModule],
+ exports: [
+ MdAutocomplete, MdOptionModule, MdAutocompleteTrigger, DefaultStyleCompatibilityModeModule
+ ],
+ declarations: [MdAutocomplete, MdAutocompleteTrigger],
})
export class MdAutocompleteModule {
static forRoot(): ModuleWithProviders {
return {
ngModule: MdAutocompleteModule,
- providers: []
+ providers: [OVERLAY_PROVIDERS]
};
}
}
diff --git a/src/lib/core/_core.scss b/src/lib/core/_core.scss
index c573e0f44438..0df852437e9e 100644
--- a/src/lib/core/_core.scss
+++ b/src/lib/core/_core.scss
@@ -3,7 +3,8 @@
@import 'style/elevation';
@import 'overlay/overlay';
@import 'ripple/ripple';
-
+@import 'option/option';
+@import 'option/option-theme';
// Mixin that renders all of the core styles that are not theme-dependent.
@mixin md-core() {
@@ -17,6 +18,7 @@
}
@include md-ripple();
+ @include md-option();
@include cdk-a11y();
@include cdk-overlay();
}
@@ -24,4 +26,5 @@
// Mixin that renders all of the core styles that depend on the theme.
@mixin md-core-theme($theme) {
@include md-ripple-theme($theme);
+ @include md-option-theme($theme);
}
diff --git a/src/lib/core/core.ts b/src/lib/core/core.ts
index 8943114da921..062c430e90fb 100644
--- a/src/lib/core/core.ts
+++ b/src/lib/core/core.ts
@@ -2,6 +2,7 @@ import {NgModule, ModuleWithProviders} from '@angular/core';
import {MdLineModule} from './line/line';
import {RtlModule} from './rtl/dir';
import {ObserveContentModule} from './observe-content/observe-content';
+import {MdOptionModule} from './option/option';
import {MdRippleModule} from './ripple/ripple';
import {PortalModule} from './portal/portal-directives';
import {OverlayModule} from './overlay/overlay-directives';
@@ -15,6 +16,8 @@ export {Dir, LayoutDirection, RtlModule} from './rtl/dir';
// Mutation Observer
export {ObserveContentModule, ObserveContent} from './observe-content/observe-content';
+export {MdOptionModule, MdOption} from './option/option';
+
// Portals
export {
Portal,
@@ -125,6 +128,7 @@ export {NoConflictStyleCompatibilityMode} from './compatibility/no-conflict-mode
PortalModule,
OverlayModule,
A11yModule,
+ MdOptionModule
],
exports: [
MdLineModule,
@@ -134,6 +138,7 @@ export {NoConflictStyleCompatibilityMode} from './compatibility/no-conflict-mode
PortalModule,
OverlayModule,
A11yModule,
+ MdOptionModule
],
})
export class MdCoreModule {
diff --git a/src/lib/core/option/_option-theme.scss b/src/lib/core/option/_option-theme.scss
new file mode 100644
index 000000000000..019f1eb46847
--- /dev/null
+++ b/src/lib/core/option/_option-theme.scss
@@ -0,0 +1,23 @@
+@import '../theming/palette';
+@import '../theming/theming';
+
+@mixin md-option-theme($theme) {
+ $foreground: map-get($theme, foreground);
+ $background: map-get($theme, background);
+
+ md-option {
+ &:hover:not(.md-option-disabled), &:focus:not(.md-option-disabled) {
+ background: md-color($background, hover);
+ }
+
+ &.md-selected {
+ background: md-color($background, hover);
+ color: md-color($primary);
+ }
+
+ &.md-option-disabled {
+ color: md-color($foreground, hint-text);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/lib/select/option.html b/src/lib/core/option/option.html
similarity index 100%
rename from src/lib/select/option.html
rename to src/lib/core/option/option.html
diff --git a/src/lib/core/option/option.scss b/src/lib/core/option/option.scss
new file mode 100644
index 000000000000..3409bf886c3a
--- /dev/null
+++ b/src/lib/core/option/option.scss
@@ -0,0 +1,30 @@
+@import '../style/menu-common';
+@import '../a11y/a11y';
+
+@mixin md-option() {
+ md-option {
+ @include md-menu-item-base();
+ position: relative;
+ cursor: pointer;
+ outline: none;
+
+ &[aria-disabled='true'] {
+ cursor: default;
+ user-select: none;
+ }
+ }
+
+ .md-option-ripple {
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+
+ // In high contrast mode this completely covers the text.
+ @include cdk-high-contrast {
+ opacity: 0.5;
+ }
+ }
+}
+
diff --git a/src/lib/select/option.ts b/src/lib/core/option/option.ts
similarity index 83%
rename from src/lib/select/option.ts
rename to src/lib/core/option/option.ts
index ef56987a39b6..5eaae88e4ced 100644
--- a/src/lib/select/option.ts
+++ b/src/lib/core/option/option.ts
@@ -4,11 +4,15 @@ import {
EventEmitter,
Input,
Output,
+ NgModule,
+ ModuleWithProviders,
Renderer,
ViewEncapsulation
} from '@angular/core';
-import {ENTER, SPACE} from '../core/keyboard/keycodes';
-import {coerceBooleanProperty} from '../core/coercion/boolean-property';
+import {CommonModule} from '@angular/common';
+import {ENTER, SPACE} from '../keyboard/keycodes';
+import {coerceBooleanProperty} from '../coercion/boolean-property';
+import {MdRippleModule} from '../ripple/ripple';
/**
* Option IDs need to be unique across components, so this counter exists outside of
@@ -34,7 +38,7 @@ let _uniqueIdCounter = 0;
'(keydown)': '_handleKeydown($event)'
},
templateUrl: 'option.html',
- styleUrls: ['select.css'],
+ styleUrls: ['option.css'],
encapsulation: ViewEncapsulation.None
})
export class MdOption {
@@ -43,7 +47,7 @@ export class MdOption {
/** Whether the option is disabled. */
private _disabled: boolean = false;
- private _id: string = `md-select-option-${_uniqueIdCounter++}`;
+ private _id: string = `md-option-${_uniqueIdCounter++}`;
/** The unique ID of the option. */
get id() { return this._id; }
@@ -98,7 +102,6 @@ export class MdOption {
}
}
-
/**
* Selects the option while indicating the selection came from the user. Used to
* determine if the select's view -> model callback should be invoked.
@@ -120,3 +123,17 @@ export class MdOption {
}
}
+
+@NgModule({
+ imports: [MdRippleModule, CommonModule],
+ exports: [MdOption],
+ declarations: [MdOption]
+})
+export class MdOptionModule {
+ static forRoot(): ModuleWithProviders {
+ return {
+ ngModule: MdOptionModule,
+ providers: []
+ };
+ }
+}
diff --git a/src/lib/select/_select-theme.scss b/src/lib/select/_select-theme.scss
index 43a046737cca..847a0728e474 100644
--- a/src/lib/select/_select-theme.scss
+++ b/src/lib/select/_select-theme.scss
@@ -45,20 +45,4 @@
color: md-color($foreground, hint-text);
}
}
-
- md-option {
- &:hover:not(.md-option-disabled), &:focus:not(.md-option-disabled) {
- background: md-color($background, hover);
- }
-
- &.md-selected {
- background: md-color($background, hover);
- color: md-color($primary);
- }
-
- &.md-option-disabled {
- color: md-color($foreground, hint-text);
- }
-
- }
}
diff --git a/src/lib/select/index.ts b/src/lib/select/index.ts
index a79c2a571bd7..a709f6792ead 100644
--- a/src/lib/select/index.ts
+++ b/src/lib/select/index.ts
@@ -1,22 +1,20 @@
import {NgModule, ModuleWithProviders} from '@angular/core';
import {CommonModule} from '@angular/common';
import {MdSelect} from './select';
-import {MdOption} from './option';
+import {MdOptionModule} from '../core/option/option';
import {
DefaultStyleCompatibilityModeModule,
OVERLAY_PROVIDERS,
- MdRippleModule,
OverlayModule,
} from '../core';
export * from './select';
-export {MdOption} from './option';
export {fadeInContent, transformPanel, transformPlaceholder} from './select-animations';
@NgModule({
- imports: [CommonModule, OverlayModule, MdRippleModule, DefaultStyleCompatibilityModeModule],
- exports: [MdSelect, MdOption, DefaultStyleCompatibilityModeModule],
- declarations: [MdSelect, MdOption],
+ imports: [CommonModule, OverlayModule, MdOptionModule, DefaultStyleCompatibilityModeModule],
+ exports: [MdSelect, MdOptionModule, DefaultStyleCompatibilityModeModule],
+ declarations: [MdSelect],
})
export class MdSelectModule {
static forRoot(): ModuleWithProviders {
diff --git a/src/lib/select/select.scss b/src/lib/select/select.scss
index 04ca67077ebd..5c05072044a8 100644
--- a/src/lib/select/select.scss
+++ b/src/lib/select/select.scss
@@ -105,27 +105,3 @@ md-select {
}
}
-md-option {
- @include md-menu-item-base();
- position: relative;
- cursor: pointer;
- outline: none;
-
- &[aria-disabled='true'] {
- cursor: default;
- user-select: none;
- }
-}
-
-.md-option-ripple {
- position: absolute;
- top: 0;
- left: 0;
- bottom: 0;
- right: 0;
-
- // In high contrast mode this completely covers the text.
- @include cdk-high-contrast {
- opacity: 0.5;
- }
-}
diff --git a/src/lib/select/select.spec.ts b/src/lib/select/select.spec.ts
index f5e3f6b9f40b..c2e3e6a12d53 100644
--- a/src/lib/select/select.spec.ts
+++ b/src/lib/select/select.spec.ts
@@ -4,7 +4,7 @@ import {Component, DebugElement, QueryList, ViewChild, ViewChildren} from '@angu
import {MdSelectModule} from './index';
import {OverlayContainer} from '../core/overlay/overlay-container';
import {MdSelect} from './select';
-import {MdOption} from './option';
+import {MdOption} from '../core/option/option';
import {Dir} from '../core/rtl/dir';
import {FormControl, FormsModule, ReactiveFormsModule} from '@angular/forms';
import {ViewportRuler} from '../core/overlay/position/viewport-ruler';
@@ -587,6 +587,7 @@ describe('MdSelect', () => {
select.style.marginRight = '20px';
});
+
it('should align the first option with the trigger text if no option is selected', () => {
trigger.click();
fixture.detectChanges();
@@ -1081,7 +1082,7 @@ describe('MdSelect', () => {
let firstOptionID = options[0].id;
expect(options[0].id)
- .toContain('md-select-option', `Expected option ID to have the correct prefix.`);
+ .toContain('md-option', `Expected option ID to have the correct prefix.`);
expect(options[0].id).not.toEqual(options[1].id, `Expected option IDs to be unique.`);
const backdrop =
@@ -1096,7 +1097,7 @@ describe('MdSelect', () => {
options =
overlayContainerElement.querySelectorAll('md-option') as NodeListOf;
expect(options[0].id)
- .toContain('md-select-option', `Expected option ID to have the correct prefix.`);
+ .toContain('md-option', `Expected option ID to have the correct prefix.`);
expect(options[0].id).not.toEqual(firstOptionID, `Expected option IDs to be unique.`);
expect(options[0].id).not.toEqual(options[1].id, `Expected option IDs to be unique.`);
});
@@ -1219,7 +1220,7 @@ class ManySelects {}
- `
+ `,
})
class NgIfSelect {
diff --git a/src/lib/select/select.ts b/src/lib/select/select.ts
index e2de6c548829..8f408c1f15fa 100644
--- a/src/lib/select/select.ts
+++ b/src/lib/select/select.ts
@@ -13,7 +13,7 @@ import {
ViewEncapsulation,
ViewChild,
} from '@angular/core';
-import {MdOption} from './option';
+import {MdOption} from '../core/option/option';
import {ENTER, SPACE} from '../core/keyboard/keycodes';
import {ListKeyManager} from '../core/a11y/list-key-manager';
import {Dir} from '../core/rtl/dir';
diff --git a/tools/gulp/tasks/components.ts b/tools/gulp/tasks/components.ts
index 8e71f9d921a1..64102458d0d1 100644
--- a/tools/gulp/tasks/components.ts
+++ b/tools/gulp/tasks/components.ts
@@ -75,6 +75,7 @@ task(':build:components:rollup', () => {
'rxjs/add/observable/fromEvent': 'Rx.Observable',
'rxjs/add/observable/forkJoin': 'Rx.Observable',
'rxjs/add/observable/of': 'Rx.Observable',
+ 'rxjs/add/observable/merge': 'Rx.Observable',
'rxjs/add/observable/throw': 'Rx.Observable',
'rxjs/add/operator/toPromise': 'Rx.Observable.prototype',
'rxjs/add/operator/map': 'Rx.Observable.prototype',