diff --git a/src/lib/autocomplete/autocomplete-trigger.ts b/src/lib/autocomplete/autocomplete-trigger.ts index 475a45fa5b0c..6ce9c1040cc8 100644 --- a/src/lib/autocomplete/autocomplete-trigger.ts +++ b/src/lib/autocomplete/autocomplete-trigger.ts @@ -18,10 +18,21 @@ import { ViewContainerRef, Inject, ChangeDetectorRef, + InjectionToken, } from '@angular/core'; import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'; import {DOCUMENT} from '@angular/platform-browser'; -import {Overlay, OverlayRef, OverlayState, TemplatePortal} from '../core'; +import { + Overlay, + OverlayRef, + OverlayState, + TemplatePortal, + RepositionScrollStrategy, + // This import is only used to define a generic type. The current TypeScript version incorrectly + // considers such imports as unused (https://github.com/Microsoft/TypeScript/issues/14953) + // tslint:disable-next-line:no-unused-variable + ScrollStrategy, +} from '../core'; import {MdAutocomplete} from './autocomplete'; import {PositionStrategy} from '../core/overlay/position/position-strategy'; import {ConnectedPositionStrategy} from '../core/overlay/position/connected-position-strategy'; @@ -48,6 +59,22 @@ export const AUTOCOMPLETE_OPTION_HEIGHT = 48; /** The total height of the autocomplete panel. */ export const AUTOCOMPLETE_PANEL_HEIGHT = 256; +/** Injection token that determines the scroll handling while the autocomplete panel is open. */ +export const MD_AUTOCOMPLETE_SCROLL_STRATEGY = + new InjectionToken<() => ScrollStrategy>('md-autocomplete-scroll-strategy'); + +/** @docs-private */ +export function MD_AUTOCOMPLETE_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay: Overlay) { + return () => overlay.scrollStrategies.reposition(); +} + +/** @docs-private */ +export const MD_AUTOCOMPLETE_SCROLL_STRATEGY_PROVIDER = { + provide: MD_AUTOCOMPLETE_SCROLL_STRATEGY, + deps: [Overlay], + useFactory: MD_AUTOCOMPLETE_SCROLL_STRATEGY_PROVIDER_FACTORY, +}; + /** * Provider that allows the autocomplete to register as a ControlValueAccessor. * @docs-private @@ -127,6 +154,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy { private _viewContainerRef: ViewContainerRef, private _zone: NgZone, private _changeDetectorRef: ChangeDetectorRef, + @Inject(MD_AUTOCOMPLETE_SCROLL_STRATEGY) private _scrollStrategy, @Optional() private _dir: Directionality, @Optional() @Host() private _inputContainer: MdInputContainer, @Optional() @Inject(DOCUMENT) private _document: any) {} @@ -419,7 +447,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy { overlayState.positionStrategy = this._getOverlayPosition(); overlayState.width = this._getHostWidth(); overlayState.direction = this._dir ? this._dir.value : 'ltr'; - overlayState.scrollStrategy = this._overlay.scrollStrategies.reposition(); + overlayState.scrollStrategy = this._scrollStrategy(); return overlayState; } diff --git a/src/lib/autocomplete/index.ts b/src/lib/autocomplete/index.ts index 1ec27f6d292e..ff52c4b6a0ee 100644 --- a/src/lib/autocomplete/index.ts +++ b/src/lib/autocomplete/index.ts @@ -10,12 +10,16 @@ import {NgModule} from '@angular/core'; import {MdOptionModule, OverlayModule, MdCommonModule} from '../core'; import {CommonModule} from '@angular/common'; import {MdAutocomplete} from './autocomplete'; -import {MdAutocompleteTrigger} from './autocomplete-trigger'; +import { + MdAutocompleteTrigger, + MD_AUTOCOMPLETE_SCROLL_STRATEGY_PROVIDER, +} from './autocomplete-trigger'; @NgModule({ imports: [MdOptionModule, OverlayModule, MdCommonModule, CommonModule], exports: [MdAutocomplete, MdOptionModule, MdAutocompleteTrigger, MdCommonModule], declarations: [MdAutocomplete, MdAutocompleteTrigger], + providers: [MD_AUTOCOMPLETE_SCROLL_STRATEGY_PROVIDER], }) export class MdAutocompleteModule {} diff --git a/src/lib/core/overlay/index.ts b/src/lib/core/overlay/index.ts index 4b25874c6a11..bb6945f68f3f 100644 --- a/src/lib/core/overlay/index.ts +++ b/src/lib/core/overlay/index.ts @@ -9,7 +9,11 @@ import {NgModule, Provider} from '@angular/core'; import {Overlay} from './overlay'; import {ScrollDispatchModule} from './scroll/index'; import {PortalModule} from '../portal/portal-directives'; -import {ConnectedOverlayDirective, OverlayOrigin} from './overlay-directives'; +import { + ConnectedOverlayDirective, + OverlayOrigin, + MD_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER, +} from './overlay-directives'; import {OverlayPositionBuilder} from './position/overlay-position-builder'; import {VIEWPORT_RULER_PROVIDER} from './position/viewport-ruler'; import {OVERLAY_CONTAINER_PROVIDER} from './overlay-container'; @@ -20,6 +24,7 @@ export const OVERLAY_PROVIDERS: Provider[] = [ OverlayPositionBuilder, VIEWPORT_RULER_PROVIDER, OVERLAY_CONTAINER_PROVIDER, + MD_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER, ]; @NgModule({ diff --git a/src/lib/core/overlay/overlay-directives.ts b/src/lib/core/overlay/overlay-directives.ts index 57ec25d30eba..643d06b60ec0 100644 --- a/src/lib/core/overlay/overlay-directives.ts +++ b/src/lib/core/overlay/overlay-directives.ts @@ -19,6 +19,8 @@ import { Renderer2, OnChanges, SimpleChanges, + InjectionToken, + Inject, } from '@angular/core'; import {Overlay} from './overlay'; import {OverlayRef} from './overlay-ref'; @@ -33,14 +35,14 @@ import { } from './position/connected-position'; import {ConnectedPositionStrategy} from './position/connected-position-strategy'; import {Directionality, Direction} from '../bidi/index'; -import {ScrollStrategy} from './scroll/scroll-strategy'; import {coerceBooleanProperty} from '@angular/cdk'; +import {ScrollStrategy, RepositionScrollStrategy} from './scroll/index'; import {ESCAPE} from '../keyboard/keycodes'; import {Subscription} from 'rxjs/Subscription'; /** Default set of positions for the overlay. Follows the behavior of a dropdown. */ -let defaultPositionList = [ +const defaultPositionList = [ new ConnectionPositionPair( {originX: 'start', originY: 'bottom'}, {overlayX: 'start', overlayY: 'top'}), @@ -49,6 +51,23 @@ let defaultPositionList = [ {overlayX: 'start', overlayY: 'bottom'}), ]; +/** Injection token that determines the scroll handling while the connected overlay is open. */ +export const MD_CONNECTED_OVERLAY_SCROLL_STRATEGY = + new InjectionToken<() => ScrollStrategy>('md-connected-overlay-scroll-strategy'); + +/** @docs-private */ +export function MD_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay: Overlay) { + return () => overlay.scrollStrategies.reposition(); +} + +/** @docs-private */ +export const MD_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER = { + provide: MD_CONNECTED_OVERLAY_SCROLL_STRATEGY, + deps: [Overlay], + useFactory: MD_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER_FACTORY, +}; + + /** * Directive applied to an element to make it usable as an origin for an Overlay using a @@ -130,7 +149,7 @@ export class ConnectedOverlayDirective implements OnDestroy, OnChanges { @Input() backdropClass: string; /** Strategy to be used when handling scroll events while the overlay is open. */ - @Input() scrollStrategy: ScrollStrategy = this._overlay.scrollStrategies.reposition(); + @Input() scrollStrategy: ScrollStrategy = this._scrollStrategy(); /** Whether the overlay is open. */ @Input() open: boolean = false; @@ -164,6 +183,7 @@ export class ConnectedOverlayDirective implements OnDestroy, OnChanges { private _renderer: Renderer2, templateRef: TemplateRef, viewContainerRef: ViewContainerRef, + @Inject(MD_CONNECTED_OVERLAY_SCROLL_STRATEGY) private _scrollStrategy, @Optional() private _dir: Directionality) { this._templatePortal = new TemplatePortal(templateRef, viewContainerRef); } diff --git a/src/lib/datepicker/datepicker.ts b/src/lib/datepicker/datepicker.ts index 0f68427a6200..7d2fd8226401 100644 --- a/src/lib/datepicker/datepicker.ts +++ b/src/lib/datepicker/datepicker.ts @@ -21,12 +21,20 @@ import { ViewEncapsulation, NgZone, Inject, + InjectionToken, } from '@angular/core'; import {DOCUMENT} from '@angular/platform-browser'; -import {Overlay} from '../core/overlay/overlay'; -import {OverlayRef} from '../core/overlay/overlay-ref'; +import { + Overlay, + OverlayRef, + OverlayState, + RepositionScrollStrategy, + // This import is only used to define a generic type. The current TypeScript version incorrectly + // considers such imports as unused (https://github.com/Microsoft/TypeScript/issues/14953) + // tslint:disable-next-line:no-unused-variable + ScrollStrategy, +} from '../core/overlay/index'; import {ComponentPortal} from '../core/portal/portal'; -import {OverlayState} from '../core/overlay/overlay-state'; import {Directionality} from '../core/bidi/index'; import {MdDialog} from '../dialog/dialog'; import {MdDialogRef} from '../dialog/dialog-ref'; @@ -44,6 +52,22 @@ import {coerceBooleanProperty} from '@angular/cdk'; /** Used to generate a unique ID for each datepicker instance. */ let datepickerUid = 0; +/** Injection token that determines the scroll handling while the calendar is open. */ +export const MD_DATEPICKER_SCROLL_STRATEGY = + new InjectionToken<() => ScrollStrategy>('md-datepicker-scroll-strategy'); + +/** @docs-private */ +export function MD_DATEPICKER_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay: Overlay) { + return () => overlay.scrollStrategies.reposition(); +} + +/** @docs-private */ +export const MD_DATEPICKER_SCROLL_STRATEGY_PROVIDER = { + provide: MD_DATEPICKER_SCROLL_STRATEGY, + deps: [Overlay], + useFactory: MD_DATEPICKER_SCROLL_STRATEGY_PROVIDER_FACTORY, +}; + /** * Component used as the content for the datepicker dialog and popup. We use this instead of using @@ -174,6 +198,7 @@ export class MdDatepicker implements OnDestroy { private _overlay: Overlay, private _ngZone: NgZone, private _viewContainerRef: ViewContainerRef, + @Inject(MD_DATEPICKER_SCROLL_STRATEGY) private _scrollStrategy, @Optional() private _dateAdapter: DateAdapter, @Optional() private _dir: Directionality, @Optional() @Inject(DOCUMENT) private _document: any) { @@ -293,7 +318,7 @@ export class MdDatepicker implements OnDestroy { overlayState.hasBackdrop = true; overlayState.backdropClass = 'md-overlay-transparent-backdrop'; overlayState.direction = this._dir ? this._dir.value : 'ltr'; - overlayState.scrollStrategy = this._overlay.scrollStrategies.reposition(); + overlayState.scrollStrategy = this._scrollStrategy(); this._popupRef = this._overlay.create(overlayState); } diff --git a/src/lib/datepicker/index.ts b/src/lib/datepicker/index.ts index 5c453e874734..4961fa27345a 100644 --- a/src/lib/datepicker/index.ts +++ b/src/lib/datepicker/index.ts @@ -12,7 +12,11 @@ import {CommonModule} from '@angular/common'; import {A11yModule, OverlayModule, StyleModule} from '../core'; import {MdCalendarBody} from './calendar-body'; import {MdYearView} from './year-view'; -import {MdDatepicker, MdDatepickerContent} from './datepicker'; +import { + MdDatepicker, + MdDatepickerContent, + MD_DATEPICKER_SCROLL_STRATEGY_PROVIDER, +} from './datepicker'; import {MdDatepickerInput} from './datepicker-input'; import {MdDialogModule} from '../dialog/index'; import {MdCalendar} from './calendar'; @@ -58,6 +62,7 @@ export * from './year-view'; ], providers: [ MdDatepickerIntl, + MD_DATEPICKER_SCROLL_STRATEGY_PROVIDER, ], entryComponents: [ MdDatepickerContent, diff --git a/src/lib/dialog/dialog.ts b/src/lib/dialog/dialog.ts index 2a5b830c882a..23f1a56e6c06 100644 --- a/src/lib/dialog/dialog.ts +++ b/src/lib/dialog/dialog.ts @@ -8,12 +8,13 @@ import { Injector, - InjectionToken, ComponentRef, Injectable, Optional, SkipSelf, TemplateRef, + Inject, + InjectionToken, } from '@angular/core'; import {Location} from '@angular/common'; import {Observable} from 'rxjs/Observable'; @@ -24,6 +25,11 @@ import { ComponentType, OverlayState, ComponentPortal, + BlockScrollStrategy, + // This import is only used to define a generic type. The current TypeScript version incorrectly + // considers such imports as unused (https://github.com/Microsoft/TypeScript/issues/14953) + // tslint:disable-next-line:no-unused-variable + ScrollStrategy, } from '../core'; import {PortalInjector} from '../core/portal/portal-injector'; import {extendObject} from '../core/util/object-extend'; @@ -36,6 +42,23 @@ import {TemplatePortal} from '../core/portal/portal'; export const MD_DIALOG_DATA = new InjectionToken('MdDialogData'); +/** Injection token that determines the scroll handling while the dialog is open. */ +export const MD_DIALOG_SCROLL_STRATEGY = + new InjectionToken<() => ScrollStrategy>('md-dialog-scroll-strategy'); + +/** @docs-private */ +export function MD_DIALOG_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay: Overlay) { + return () => overlay.scrollStrategies.block(); +} + +/** @docs-private */ +export const MD_DIALOG_SCROLL_STRATEGY_PROVIDER = { + provide: MD_DIALOG_SCROLL_STRATEGY, + deps: [Overlay], + useFactory: MD_DIALOG_SCROLL_STRATEGY_PROVIDER_FACTORY, +}; + + /** * Service to open Material Design modal dialogs. */ @@ -71,6 +94,7 @@ export class MdDialog { constructor( private _overlay: Overlay, private _injector: Injector, + @Inject(MD_DIALOG_SCROLL_STRATEGY) private _scrollStrategy, @Optional() private _location: Location, @Optional() @SkipSelf() private _parentDialog: MdDialog) { @@ -143,7 +167,7 @@ export class MdDialog { let overlayState = new OverlayState(); overlayState.panelClass = dialogConfig.panelClass; overlayState.hasBackdrop = dialogConfig.hasBackdrop; - overlayState.scrollStrategy = this._overlay.scrollStrategies.block(); + overlayState.scrollStrategy = this._scrollStrategy(); overlayState.direction = dialogConfig.direction; if (dialogConfig.backdropClass) { overlayState.backdropClass = dialogConfig.backdropClass; diff --git a/src/lib/dialog/index.ts b/src/lib/dialog/index.ts index 7dd3b29dd8db..76572bcf9e24 100644 --- a/src/lib/dialog/index.ts +++ b/src/lib/dialog/index.ts @@ -14,7 +14,7 @@ import { A11yModule, MdCommonModule, } from '../core'; -import {MdDialog} from './dialog'; +import {MdDialog, MD_DIALOG_SCROLL_STRATEGY_PROVIDER} from './dialog'; import {MdDialogContainer} from './dialog-container'; import { MdDialogClose, @@ -49,6 +49,7 @@ import { ], providers: [ MdDialog, + MD_DIALOG_SCROLL_STRATEGY_PROVIDER, ], entryComponents: [MdDialogContainer], }) diff --git a/src/lib/menu/index.ts b/src/lib/menu/index.ts index 41a9e9f5672a..e2a8fae18c6d 100644 --- a/src/lib/menu/index.ts +++ b/src/lib/menu/index.ts @@ -11,7 +11,7 @@ import {CommonModule} from '@angular/common'; import {OverlayModule, MdCommonModule} from '../core'; import {MdMenu} from './menu-directive'; import {MdMenuItem} from './menu-item'; -import {MdMenuTrigger} from './menu-trigger'; +import {MdMenuTrigger, MD_MENU_SCROLL_STRATEGY_PROVIDER} from './menu-trigger'; import {MdRippleModule} from '../core/ripple/index'; @@ -24,6 +24,7 @@ import {MdRippleModule} from '../core/ripple/index'; ], exports: [MdMenu, MdMenuItem, MdMenuTrigger, MdCommonModule], declarations: [MdMenu, MdMenuItem, MdMenuTrigger], + providers: [MD_MENU_SCROLL_STRATEGY_PROVIDER], }) export class MdMenuModule {} diff --git a/src/lib/menu/menu-trigger.ts b/src/lib/menu/menu-trigger.ts index e70c42658050..23a83e4ed20c 100644 --- a/src/lib/menu/menu-trigger.ts +++ b/src/lib/menu/menu-trigger.ts @@ -16,6 +16,8 @@ import { Optional, Output, ViewContainerRef, + InjectionToken, + Inject, } from '@angular/core'; import {MdMenuPanel} from './menu-panel'; import {throwMdMenuMissingError} from './menu-errors'; @@ -30,10 +32,32 @@ import { ConnectedPositionStrategy, HorizontalConnectionPos, VerticalConnectionPos, + RepositionScrollStrategy, + // This import is only used to define a generic type. The current TypeScript version incorrectly + // considers such imports as unused (https://github.com/Microsoft/TypeScript/issues/14953) + // tslint:disable-next-line:no-unused-variable + ScrollStrategy, } from '../core'; import {Subscription} from 'rxjs/Subscription'; import {MenuPositionX, MenuPositionY} from './menu-positions'; +/** Injection token that determines the scroll handling while the menu is open. */ +export const MD_MENU_SCROLL_STRATEGY = + new InjectionToken<() => ScrollStrategy>('md-menu-scroll-strategy'); + +/** @docs-private */ +export function MD_MENU_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay: Overlay) { + return () => overlay.scrollStrategies.reposition(); +} + +/** @docs-private */ +export const MD_MENU_SCROLL_STRATEGY_PROVIDER = { + provide: MD_MENU_SCROLL_STRATEGY, + deps: [Overlay], + useFactory: MD_MENU_SCROLL_STRATEGY_PROVIDER_FACTORY, +}; + + // TODO(andrewseguin): Remove the kebab versions in favor of camelCased attribute selectors /** @@ -87,6 +111,7 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy { constructor(private _overlay: Overlay, private _element: ElementRef, private _viewContainerRef: ViewContainerRef, + @Inject(MD_MENU_SCROLL_STRATEGY) private _scrollStrategy, @Optional() private _dir: Directionality) { } ngAfterViewInit() { @@ -228,7 +253,7 @@ export class MdMenuTrigger implements AfterViewInit, OnDestroy { overlayState.hasBackdrop = true; overlayState.backdropClass = 'cdk-overlay-transparent-backdrop'; overlayState.direction = this.dir; - overlayState.scrollStrategy = this._overlay.scrollStrategies.reposition(); + overlayState.scrollStrategy = this._scrollStrategy(); return overlayState; } diff --git a/src/lib/select/index.ts b/src/lib/select/index.ts index de0337e3a297..bf5f903905fb 100644 --- a/src/lib/select/index.ts +++ b/src/lib/select/index.ts @@ -8,7 +8,7 @@ import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; -import {MdSelect} from './select'; +import {MdSelect, MD_SELECT_SCROLL_STRATEGY_PROVIDER} from './select'; import {MdCommonModule, OverlayModule, MdOptionModule} from '../core'; @@ -21,6 +21,7 @@ import {MdCommonModule, OverlayModule, MdOptionModule} from '../core'; ], exports: [MdSelect, MdOptionModule, MdCommonModule], declarations: [MdSelect], + providers: [MD_SELECT_SCROLL_STRATEGY_PROVIDER] }) export class MdSelectModule {} diff --git a/src/lib/select/select.html b/src/lib/select/select.html index 6549ea92568b..250d37f2079e 100644 --- a/src/lib/select/select.html +++ b/src/lib/select/select.html @@ -23,6 +23,7 @@ cdk-connected-overlay hasBackdrop backdropClass="cdk-overlay-transparent-backdrop" + [scrollStrategy]="_scrollStrategy" [origin]="origin" [open]="panelOpen" [positions]="_positions" diff --git a/src/lib/select/select.ts b/src/lib/select/select.ts index 4a1ef59c5009..0ee23309ac06 100644 --- a/src/lib/select/select.ts +++ b/src/lib/select/select.ts @@ -26,6 +26,7 @@ import { OnInit, Inject, ChangeDetectionStrategy, + InjectionToken, } from '@angular/core'; import {MdOption, MdOptionSelectionChange, MdOptgroup} from '../core/option/index'; import {ENTER, SPACE, UP_ARROW, DOWN_ARROW, HOME, END} from '../core/keyboard/keycodes'; @@ -39,6 +40,7 @@ import {coerceBooleanProperty} from '@angular/cdk'; import {ConnectedOverlayDirective} from '../core/overlay/overlay-directives'; import {ViewportRuler} from '../core/overlay/position/viewport-ruler'; import {SelectionModel} from '../core/selection/selection'; +import {Overlay} from '../core/overlay/overlay'; import {getMdSelectDynamicMultipleError, getMdSelectNonArrayValueError} from './select-errors'; import {startWith, filter} from '../core/rxjs/index'; import {merge} from 'rxjs/observable/merge'; @@ -49,6 +51,10 @@ import { PlaceholderOptions, MD_PLACEHOLDER_GLOBAL_OPTIONS } from '../core/placeholder/placeholder-options'; +// This import is only used to define a generic type. The current TypeScript version incorrectly +// considers such imports as unused (https://github.com/Microsoft/TypeScript/issues/14953) +// tslint:disable-next-line:no-unused-variable +import {ScrollStrategy, RepositionScrollStrategy} from '../core/overlay/scroll'; /** * The following style constants are necessary to save here in order @@ -104,6 +110,22 @@ export const SELECT_PANEL_PADDING_Y = 16; */ export const SELECT_PANEL_VIEWPORT_PADDING = 8; +/** Injection token that determines the scroll handling while a select is open. */ +export const MD_SELECT_SCROLL_STRATEGY = + new InjectionToken<() => ScrollStrategy>('md-select-scroll-strategy'); + +/** @docs-private */ +export function MD_SELECT_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay: Overlay) { + return () => overlay.scrollStrategies.reposition(); +} + +/** @docs-private */ +export const MD_SELECT_SCROLL_STRATEGY_PROVIDER = { + provide: MD_SELECT_SCROLL_STRATEGY, + deps: [Overlay], + useFactory: MD_SELECT_SCROLL_STRATEGY_PROVIDER_FACTORY, +}; + /** Change event object that is emitted when the select value has changed. */ export class MdSelectChange { constructor(public source: MdSelect, public value: any) { } @@ -214,6 +236,9 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On /** Whether the panel's animation is done. */ _panelDoneAnimating: boolean = false; + /** Strategy that will be used to handle scrolling while the select panel is open. */ + _scrollStrategy = this._scrollStrategyFactory(); + /** * The y-offset of the overlay panel in relation to the trigger's top start corner. * This must be adjusted to align the selected option text over the trigger text. @@ -323,12 +348,14 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On constructor( private _viewportRuler: ViewportRuler, private _changeDetectorRef: ChangeDetectorRef, + private _overlay: Overlay, renderer: Renderer2, elementRef: ElementRef, @Optional() private _dir: Directionality, @Self() @Optional() public _control: NgControl, @Attribute('tabindex') tabIndex: string, - @Optional() @Inject(MD_PLACEHOLDER_GLOBAL_OPTIONS) placeholderOptions: PlaceholderOptions) { + @Optional() @Inject(MD_PLACEHOLDER_GLOBAL_OPTIONS) placeholderOptions: PlaceholderOptions, + @Inject(MD_SELECT_SCROLL_STRATEGY) private _scrollStrategyFactory) { super(renderer, elementRef); if (this._control) { diff --git a/src/lib/tooltip/index.ts b/src/lib/tooltip/index.ts index 3c28451acfba..9b6095568de8 100644 --- a/src/lib/tooltip/index.ts +++ b/src/lib/tooltip/index.ts @@ -10,7 +10,7 @@ import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; import {OverlayModule, MdCommonModule} from '../core'; import {PlatformModule} from '../core/platform/index'; -import {MdTooltip, TooltipComponent} from './tooltip'; +import {MdTooltip, TooltipComponent, MD_TOOLTIP_SCROLL_STRATEGY_PROVIDER} from './tooltip'; @NgModule({ @@ -23,6 +23,7 @@ import {MdTooltip, TooltipComponent} from './tooltip'; exports: [MdTooltip, TooltipComponent, MdCommonModule], declarations: [MdTooltip, TooltipComponent], entryComponents: [TooltipComponent], + providers: [MD_TOOLTIP_SCROLL_STRATEGY_PROVIDER], }) export class MdTooltipModule {} diff --git a/src/lib/tooltip/tooltip.ts b/src/lib/tooltip/tooltip.ts index ae6b78bdf5b1..b12a68438687 100644 --- a/src/lib/tooltip/tooltip.ts +++ b/src/lib/tooltip/tooltip.ts @@ -19,6 +19,8 @@ import { ChangeDetectorRef, ChangeDetectionStrategy, ViewEncapsulation, + InjectionToken, + Inject, } from '@angular/core'; import { style, @@ -35,6 +37,11 @@ import { ComponentPortal, OverlayConnectionPosition, OriginConnectionPosition, + RepositionScrollStrategy, + // This import is only used to define a generic type. The current TypeScript version incorrectly + // considers such imports as unused (https://github.com/Microsoft/TypeScript/issues/14953) + // tslint:disable-next-line:no-unused-variable + ScrollStrategy, } from '../core'; import {Observable} from 'rxjs/Observable'; import {Subject} from 'rxjs/Subject'; @@ -60,6 +67,23 @@ export function getMdTooltipInvalidPositionError(position: string) { return Error(`Tooltip position "${position}" is invalid.`); } +/** Injection token that determines the scroll handling while a tooltip is visible. */ +export const MD_TOOLTIP_SCROLL_STRATEGY = + new InjectionToken<() => ScrollStrategy>('md-tooltip-scroll-strategy'); + +/** @docs-private */ +export function MD_TOOLTIP_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay: Overlay) { + return () => overlay.scrollStrategies.reposition({ scrollThrottle: SCROLL_THROTTLE_MS }); +} + +/** @docs-private */ +export const MD_TOOLTIP_SCROLL_STRATEGY_PROVIDER = { + provide: MD_TOOLTIP_SCROLL_STRATEGY, + deps: [Overlay], + useFactory: MD_TOOLTIP_SCROLL_STRATEGY_PROVIDER_FACTORY +}; + + /** * Directive that attaches a material design tooltip to the host element. Animates the showing and * hiding of a tooltip provided position (defaults to below the element). @@ -185,6 +209,7 @@ export class MdTooltip implements OnDestroy { private _ngZone: NgZone, private _renderer: Renderer2, private _platform: Platform, + @Inject(MD_TOOLTIP_SCROLL_STRATEGY) private _scrollStrategy, @Optional() private _dir: Directionality) { // The mouse events shouldn't be bound on iOS devices, because @@ -279,9 +304,7 @@ export class MdTooltip implements OnDestroy { config.direction = this._dir ? this._dir.value : 'ltr'; config.positionStrategy = strategy; config.panelClass = TOOLTIP_PANEL_CLASS; - config.scrollStrategy = this._overlay.scrollStrategies.reposition({ - scrollThrottle: SCROLL_THROTTLE_MS - }); + config.scrollStrategy = this._scrollStrategy(); this._overlayRef = this._overlay.create(config);