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

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 committed Jun 15, 2017
1 parent 734eccc commit b89d167
Show file tree
Hide file tree
Showing 14 changed files with 216 additions and 35 deletions.
43 changes: 35 additions & 8 deletions src/lib/autocomplete/autocomplete-trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,18 @@ 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,
ScrollStrategy,
RepositionScrollStrategy,
} 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 +56,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 @@ -118,12 +142,15 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
this.autocomplete = autocomplete;
}

constructor(private _element: ElementRef, private _overlay: Overlay,
private _viewContainerRef: ViewContainerRef,
private _changeDetectorRef: ChangeDetectorRef,
@Optional() private _dir: Dir, private _zone: NgZone,
@Optional() @Host() private _inputContainer: MdInputContainer,
@Optional() @Inject(DOCUMENT) private _document: any) {}
constructor(
private _element: ElementRef,
private _overlay: Overlay,
private _viewContainerRef: ViewContainerRef,
private _changeDetectorRef: ChangeDetectorRef,
@Inject(MD_AUTOCOMPLETE_SCROLL_STRATEGY) private _scrollStrategy,
@Optional() private _dir: Dir, private _zone: NgZone,
@Optional() @Host() private _inputContainer: MdInputContainer,
@Optional() @Inject(DOCUMENT) private _document: any) {}

ngOnDestroy() {
if (this._panelPositionSubscription) {
Expand Down Expand Up @@ -388,7 +415,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
Original file line number Diff line number Diff line change
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
37 changes: 30 additions & 7 deletions src/lib/core/overlay/overlay-directives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import {
Renderer2,
OnChanges,
SimpleChanges,
InjectionToken,
Inject,
} from '@angular/core';
import {Overlay, OVERLAY_PROVIDERS} from './overlay';
import {OverlayRef} from './overlay-ref';
Expand All @@ -32,16 +34,19 @@ import {
import {PortalModule} from '../portal/portal-directives';
import {ConnectedPositionStrategy} from './position/connected-position-strategy';
import {Dir, LayoutDirection} from '../rtl/dir';
import {Scrollable} from './scroll/scrollable';
import {ScrollStrategy} from './scroll/scroll-strategy';
import {coerceBooleanProperty} from '../coercion/boolean-property';
import {ESCAPE} from '../keyboard/keycodes';
import {Subscription} from 'rxjs/Subscription';
import {ScrollDispatchModule} from './scroll/index';
import {
ScrollStrategy,
RepositionScrollStrategy,
Scrollable,
ScrollDispatchModule,
} from './scroll/index';


/** 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 @@ -50,6 +55,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 @@ -131,7 +153,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 @@ -165,7 +187,8 @@ export class ConnectedOverlayDirective implements OnDestroy, OnChanges {
private _renderer: Renderer2,
templateRef: TemplateRef<any>,
viewContainerRef: ViewContainerRef,
@Optional() private _dir: Dir) {
@Optional() private _dir: Dir,
@Inject(MD_CONNECTED_OVERLAY_SCROLL_STRATEGY) private _scrollStrategy) {
this._templatePortal = new TemplatePortal(templateRef, viewContainerRef);
}

Expand Down Expand Up @@ -332,6 +355,6 @@ export class ConnectedOverlayDirective implements OnDestroy, OnChanges {
imports: [PortalModule, ScrollDispatchModule],
exports: [ConnectedOverlayDirective, OverlayOrigin, ScrollDispatchModule],
declarations: [ConnectedOverlayDirective, OverlayOrigin],
providers: [OVERLAY_PROVIDERS],
providers: [OVERLAY_PROVIDERS, MD_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER],
})
export class OverlayModule {}
31 changes: 27 additions & 4 deletions src/lib/datepicker/datepicker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,17 @@ import {
ViewContainerRef,
ViewEncapsulation,
NgZone,
Inject,
InjectionToken,
} from '@angular/core';
import {Overlay} from '../core/overlay/overlay';
import {OverlayRef} from '../core/overlay/overlay-ref';
import {
Overlay,
OverlayRef,
OverlayState,
ScrollStrategy,
RepositionScrollStrategy,
} from '../core/overlay/index';
import {ComponentPortal} from '../core/portal/portal';
import {OverlayState} from '../core/overlay/overlay-state';
import {Dir} from '../core/rtl/dir';
import {MdDialog} from '../dialog/dialog';
import {MdDialogRef} from '../dialog/dialog-ref';
Expand All @@ -42,6 +48,22 @@ import 'rxjs/add/operator/first';
/** 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 @@ -164,6 +186,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: Dir) {
if (!this._dateAdapter) {
Expand Down Expand Up @@ -275,7 +298,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
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ import {CommonModule} from '@angular/common';
import {StyleModule, OverlayModule, A11yModule} 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
33 changes: 31 additions & 2 deletions src/lib/dialog/dialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,16 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Injector, ComponentRef, Injectable, Optional, SkipSelf, TemplateRef} from '@angular/core';
import {
Injector,
ComponentRef,
Injectable,
Optional,
SkipSelf,
TemplateRef,
Inject,
InjectionToken,
} from '@angular/core';
import {Location} from '@angular/common';
import {Observable} from 'rxjs/Observable';
import {Subject} from 'rxjs/Subject';
Expand All @@ -16,6 +25,8 @@ import {
ComponentType,
OverlayState,
ComponentPortal,
ScrollStrategy,
BlockScrollStrategy,
} from '../core';
import {extendObject} from '../core/util/object-extend';
import {ESCAPE} from '../core/keyboard/keycodes';
Expand All @@ -26,6 +37,23 @@ import {MdDialogContainer} from './dialog-container';
import {TemplatePortal} from '../core/portal/portal';


/** 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 @@ -61,6 +89,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 @@ -133,7 +162,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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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 b89d167

Please sign in to comment.