Skip to content

Commit

Permalink
feat(overlay): add providers for overriding the scroll strategies per…
Browse files Browse the repository at this point in the history
… component (#5134)

Adds providers to the autocomplete, connected overlay, datepicker, dialog, menu, select and tooltip components, that allow for the default scroll strategy to be overwritten.

Fixes #4093.
  • Loading branch information
crisbeto authored and jelbourn committed Jul 17, 2017
1 parent c20bec8 commit 184a6e4
Show file tree
Hide file tree
Showing 15 changed files with 214 additions and 23 deletions.
32 changes: 30 additions & 2 deletions src/lib/autocomplete/autocomplete-trigger.ts
Expand Up @@ -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';
Expand All @@ -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
Expand Down Expand Up @@ -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) {}
Expand Down Expand Up @@ -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;
}

Expand Down
6 changes: 5 additions & 1 deletion src/lib/autocomplete/index.ts
Expand Up @@ -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 {}

Expand Down
7 changes: 6 additions & 1 deletion src/lib/core/overlay/index.ts
Expand Up @@ -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';
Expand All @@ -20,6 +24,7 @@ export const OVERLAY_PROVIDERS: Provider[] = [
OverlayPositionBuilder,
VIEWPORT_RULER_PROVIDER,
OVERLAY_CONTAINER_PROVIDER,
MD_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER,
];

@NgModule({
Expand Down
26 changes: 23 additions & 3 deletions src/lib/core/overlay/overlay-directives.ts
Expand Up @@ -19,6 +19,8 @@ import {
Renderer2,
OnChanges,
SimpleChanges,
InjectionToken,
Inject,
} from '@angular/core';
import {Overlay} from './overlay';
import {OverlayRef} from './overlay-ref';
Expand All @@ -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'}),
Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -164,6 +183,7 @@ export class ConnectedOverlayDirective implements OnDestroy, OnChanges {
private _renderer: Renderer2,
templateRef: TemplateRef<any>,
viewContainerRef: ViewContainerRef,
@Inject(MD_CONNECTED_OVERLAY_SCROLL_STRATEGY) private _scrollStrategy,
@Optional() private _dir: Directionality) {
this._templatePortal = new TemplatePortal(templateRef, viewContainerRef);
}
Expand Down
33 changes: 29 additions & 4 deletions src/lib/datepicker/datepicker.ts
Expand Up @@ -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';
Expand All @@ -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
Expand Down Expand Up @@ -174,6 +198,7 @@ export class MdDatepicker<D> implements OnDestroy {
private _overlay: Overlay,
private _ngZone: NgZone,
private _viewContainerRef: ViewContainerRef,
@Inject(MD_DATEPICKER_SCROLL_STRATEGY) private _scrollStrategy,
@Optional() private _dateAdapter: DateAdapter<D>,
@Optional() private _dir: Directionality,
@Optional() @Inject(DOCUMENT) private _document: any) {
Expand Down Expand Up @@ -293,7 +318,7 @@ export class MdDatepicker<D> 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);
}
Expand Down
7 changes: 6 additions & 1 deletion src/lib/datepicker/index.ts
Expand Up @@ -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';
Expand Down Expand Up @@ -58,6 +62,7 @@ export * from './year-view';
],
providers: [
MdDatepickerIntl,
MD_DATEPICKER_SCROLL_STRATEGY_PROVIDER,
],
entryComponents: [
MdDatepickerContent,
Expand Down
28 changes: 26 additions & 2 deletions src/lib/dialog/dialog.ts
Expand Up @@ -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';
Expand All @@ -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';
Expand All @@ -36,6 +42,23 @@ import {TemplatePortal} from '../core/portal/portal';
export const MD_DIALOG_DATA = new InjectionToken<any>('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.
*/
Expand Down Expand Up @@ -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) {

Expand Down Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion src/lib/dialog/index.ts
Expand Up @@ -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,
Expand Down Expand Up @@ -49,6 +49,7 @@ import {
],
providers: [
MdDialog,
MD_DIALOG_SCROLL_STRATEGY_PROVIDER,
],
entryComponents: [MdDialogContainer],
})
Expand Down
3 changes: 2 additions & 1 deletion src/lib/menu/index.ts
Expand Up @@ -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';


Expand All @@ -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 {}

Expand Down

0 comments on commit 184a6e4

Please sign in to comment.