Skip to content

Commit 5bad898

Browse files
committed
feat(overlay): add providers for overriding the scroll strategies per 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.
1 parent 81af449 commit 5bad898

File tree

15 files changed

+196
-23
lines changed

15 files changed

+196
-23
lines changed

src/lib/autocomplete/autocomplete-trigger.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,18 @@ import {
1818
ViewContainerRef,
1919
Inject,
2020
ChangeDetectorRef,
21+
InjectionToken,
2122
} from '@angular/core';
2223
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
2324
import {DOCUMENT} from '@angular/platform-browser';
24-
import {Overlay, OverlayRef, OverlayState, TemplatePortal} from '../core';
25+
import {
26+
Overlay,
27+
OverlayRef,
28+
OverlayState,
29+
TemplatePortal,
30+
ScrollStrategy,
31+
RepositionScrollStrategy,
32+
} from '../core';
2533
import {MdAutocomplete} from './autocomplete';
2634
import {PositionStrategy} from '../core/overlay/position/position-strategy';
2735
import {ConnectedPositionStrategy} from '../core/overlay/position/connected-position-strategy';
@@ -48,6 +56,22 @@ export const AUTOCOMPLETE_OPTION_HEIGHT = 48;
4856
/** The total height of the autocomplete panel. */
4957
export const AUTOCOMPLETE_PANEL_HEIGHT = 256;
5058

59+
/** Injection token that determines the scroll handling while the autocomplete panel is open. */
60+
export const MD_AUTOCOMPLETE_SCROLL_STRATEGY =
61+
new InjectionToken<() => ScrollStrategy>('md-autocomplete-scroll-strategy');
62+
63+
/** @docs-private */
64+
export function MD_AUTOCOMPLETE_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay: Overlay) {
65+
return () => overlay.scrollStrategies.reposition();
66+
}
67+
68+
/** @docs-private */
69+
export const MD_AUTOCOMPLETE_SCROLL_STRATEGY_PROVIDER = {
70+
provide: MD_AUTOCOMPLETE_SCROLL_STRATEGY,
71+
deps: [Overlay],
72+
useFactory: MD_AUTOCOMPLETE_SCROLL_STRATEGY_PROVIDER_FACTORY,
73+
};
74+
5175
/**
5276
* Provider that allows the autocomplete to register as a ControlValueAccessor.
5377
* @docs-private
@@ -127,6 +151,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
127151
private _viewContainerRef: ViewContainerRef,
128152
private _zone: NgZone,
129153
private _changeDetectorRef: ChangeDetectorRef,
154+
@Inject(MD_AUTOCOMPLETE_SCROLL_STRATEGY) private _scrollStrategy,
130155
@Optional() private _dir: Directionality,
131156
@Optional() @Host() private _inputContainer: MdInputContainer,
132157
@Optional() @Inject(DOCUMENT) private _document: any) {}
@@ -419,7 +444,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
419444
overlayState.positionStrategy = this._getOverlayPosition();
420445
overlayState.width = this._getHostWidth();
421446
overlayState.direction = this._dir ? this._dir.value : 'ltr';
422-
overlayState.scrollStrategy = this._overlay.scrollStrategies.reposition();
447+
overlayState.scrollStrategy = this._scrollStrategy();
423448
return overlayState;
424449
}
425450

src/lib/autocomplete/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,16 @@ import {NgModule} from '@angular/core';
1010
import {MdOptionModule, OverlayModule, MdCommonModule} from '../core';
1111
import {CommonModule} from '@angular/common';
1212
import {MdAutocomplete} from './autocomplete';
13-
import {MdAutocompleteTrigger} from './autocomplete-trigger';
13+
import {
14+
MdAutocompleteTrigger,
15+
MD_AUTOCOMPLETE_SCROLL_STRATEGY_PROVIDER,
16+
} from './autocomplete-trigger';
1417

1518
@NgModule({
1619
imports: [MdOptionModule, OverlayModule, MdCommonModule, CommonModule],
1720
exports: [MdAutocomplete, MdOptionModule, MdAutocompleteTrigger, MdCommonModule],
1821
declarations: [MdAutocomplete, MdAutocompleteTrigger],
22+
providers: [MD_AUTOCOMPLETE_SCROLL_STRATEGY_PROVIDER],
1923
})
2024
export class MdAutocompleteModule {}
2125

src/lib/core/overlay/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ import {NgModule, Provider} from '@angular/core';
99
import {Overlay} from './overlay';
1010
import {ScrollDispatchModule} from './scroll/index';
1111
import {PortalModule} from '../portal/portal-directives';
12-
import {ConnectedOverlayDirective, OverlayOrigin} from './overlay-directives';
12+
import {
13+
ConnectedOverlayDirective,
14+
OverlayOrigin,
15+
MD_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER,
16+
} from './overlay-directives';
1317
import {OverlayPositionBuilder} from './position/overlay-position-builder';
1418
import {VIEWPORT_RULER_PROVIDER} from './position/viewport-ruler';
1519
import {OVERLAY_CONTAINER_PROVIDER} from './overlay-container';
@@ -20,6 +24,7 @@ export const OVERLAY_PROVIDERS: Provider[] = [
2024
OverlayPositionBuilder,
2125
VIEWPORT_RULER_PROVIDER,
2226
OVERLAY_CONTAINER_PROVIDER,
27+
MD_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER,
2328
];
2429

2530
@NgModule({

src/lib/core/overlay/overlay-directives.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import {
1919
Renderer2,
2020
OnChanges,
2121
SimpleChanges,
22+
InjectionToken,
23+
Inject,
2224
} from '@angular/core';
2325
import {Overlay} from './overlay';
2426
import {OverlayRef} from './overlay-ref';
@@ -33,14 +35,14 @@ import {
3335
} from './position/connected-position';
3436
import {ConnectedPositionStrategy} from './position/connected-position-strategy';
3537
import {Directionality, Direction} from '../bidi/index';
36-
import {ScrollStrategy} from './scroll/scroll-strategy';
3738
import {coerceBooleanProperty} from '@angular/cdk';
39+
import {ScrollStrategy, Scrollable, RepositionScrollStrategy} from './scroll/index';
3840
import {ESCAPE} from '../keyboard/keycodes';
3941
import {Subscription} from 'rxjs/Subscription';
4042

4143

4244
/** Default set of positions for the overlay. Follows the behavior of a dropdown. */
43-
let defaultPositionList = [
45+
const defaultPositionList = [
4446
new ConnectionPositionPair(
4547
{originX: 'start', originY: 'bottom'},
4648
{overlayX: 'start', overlayY: 'top'}),
@@ -49,6 +51,23 @@ let defaultPositionList = [
4951
{overlayX: 'start', overlayY: 'bottom'}),
5052
];
5153

54+
/** Injection token that determines the scroll handling while the connected overlay is open. */
55+
export const MD_CONNECTED_OVERLAY_SCROLL_STRATEGY =
56+
new InjectionToken<() => ScrollStrategy>('md-connected-overlay-scroll-strategy');
57+
58+
/** @docs-private */
59+
export function MD_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay: Overlay) {
60+
return () => overlay.scrollStrategies.reposition();
61+
}
62+
63+
/** @docs-private */
64+
export const MD_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER = {
65+
provide: MD_CONNECTED_OVERLAY_SCROLL_STRATEGY,
66+
deps: [Overlay],
67+
useFactory: MD_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER_FACTORY,
68+
};
69+
70+
5271

5372
/**
5473
* 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 {
130149
@Input() backdropClass: string;
131150

132151
/** Strategy to be used when handling scroll events while the overlay is open. */
133-
@Input() scrollStrategy: ScrollStrategy = this._overlay.scrollStrategies.reposition();
152+
@Input() scrollStrategy: ScrollStrategy = this._scrollStrategy();
134153

135154
/** Whether the overlay is open. */
136155
@Input() open: boolean = false;
@@ -164,6 +183,7 @@ export class ConnectedOverlayDirective implements OnDestroy, OnChanges {
164183
private _renderer: Renderer2,
165184
templateRef: TemplateRef<any>,
166185
viewContainerRef: ViewContainerRef,
186+
@Inject(MD_CONNECTED_OVERLAY_SCROLL_STRATEGY) private _scrollStrategy,
167187
@Optional() private _dir: Directionality) {
168188
this._templatePortal = new TemplatePortal(templateRef, viewContainerRef);
169189
}

src/lib/datepicker/datepicker.ts

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,17 @@ import {
2121
ViewEncapsulation,
2222
NgZone,
2323
Inject,
24+
InjectionToken,
2425
} from '@angular/core';
2526
import {DOCUMENT} from '@angular/platform-browser';
26-
import {Overlay} from '../core/overlay/overlay';
27-
import {OverlayRef} from '../core/overlay/overlay-ref';
27+
import {
28+
Overlay,
29+
OverlayRef,
30+
OverlayState,
31+
ScrollStrategy,
32+
RepositionScrollStrategy,
33+
} from '../core/overlay/index';
2834
import {ComponentPortal} from '../core/portal/portal';
29-
import {OverlayState} from '../core/overlay/overlay-state';
3035
import {Directionality} from '../core/bidi/index';
3136
import {MdDialog} from '../dialog/dialog';
3237
import {MdDialogRef} from '../dialog/dialog-ref';
@@ -44,6 +49,22 @@ import {coerceBooleanProperty} from '@angular/cdk';
4449
/** Used to generate a unique ID for each datepicker instance. */
4550
let datepickerUid = 0;
4651

52+
/** Injection token that determines the scroll handling while the calendar is open. */
53+
export const MD_DATEPICKER_SCROLL_STRATEGY =
54+
new InjectionToken<() => ScrollStrategy>('md-datepicker-scroll-strategy');
55+
56+
/** @docs-private */
57+
export function MD_DATEPICKER_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay: Overlay) {
58+
return () => overlay.scrollStrategies.reposition();
59+
}
60+
61+
/** @docs-private */
62+
export const MD_DATEPICKER_SCROLL_STRATEGY_PROVIDER = {
63+
provide: MD_DATEPICKER_SCROLL_STRATEGY,
64+
deps: [Overlay],
65+
useFactory: MD_DATEPICKER_SCROLL_STRATEGY_PROVIDER_FACTORY,
66+
};
67+
4768

4869
/**
4970
* Component used as the content for the datepicker dialog and popup. We use this instead of using
@@ -174,6 +195,7 @@ export class MdDatepicker<D> implements OnDestroy {
174195
private _overlay: Overlay,
175196
private _ngZone: NgZone,
176197
private _viewContainerRef: ViewContainerRef,
198+
@Inject(MD_DATEPICKER_SCROLL_STRATEGY) private _scrollStrategy,
177199
@Optional() private _dateAdapter: DateAdapter<D>,
178200
@Optional() private _dir: Directionality,
179201
@Optional() @Inject(DOCUMENT) private _document: any) {
@@ -293,7 +315,7 @@ export class MdDatepicker<D> implements OnDestroy {
293315
overlayState.hasBackdrop = true;
294316
overlayState.backdropClass = 'md-overlay-transparent-backdrop';
295317
overlayState.direction = this._dir ? this._dir.value : 'ltr';
296-
overlayState.scrollStrategy = this._overlay.scrollStrategies.reposition();
318+
overlayState.scrollStrategy = this._scrollStrategy();
297319

298320
this._popupRef = this._overlay.create(overlayState);
299321
}

src/lib/datepicker/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ import {CommonModule} from '@angular/common';
1212
import {A11yModule, OverlayModule, StyleModule} from '../core';
1313
import {MdCalendarBody} from './calendar-body';
1414
import {MdYearView} from './year-view';
15-
import {MdDatepicker, MdDatepickerContent} from './datepicker';
15+
import {
16+
MdDatepicker,
17+
MdDatepickerContent,
18+
MD_DATEPICKER_SCROLL_STRATEGY_PROVIDER,
19+
} from './datepicker';
1620
import {MdDatepickerInput} from './datepicker-input';
1721
import {MdDialogModule} from '../dialog/index';
1822
import {MdCalendar} from './calendar';
@@ -58,6 +62,7 @@ export * from './year-view';
5862
],
5963
providers: [
6064
MdDatepickerIntl,
65+
MD_DATEPICKER_SCROLL_STRATEGY_PROVIDER,
6166
],
6267
entryComponents: [
6368
MdDatepickerContent,

src/lib/dialog/dialog.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@
88

99
import {
1010
Injector,
11-
InjectionToken,
1211
ComponentRef,
1312
Injectable,
1413
Optional,
1514
SkipSelf,
1615
TemplateRef,
16+
Inject,
17+
InjectionToken,
1718
} from '@angular/core';
1819
import {Location} from '@angular/common';
1920
import {Observable} from 'rxjs/Observable';
@@ -24,6 +25,8 @@ import {
2425
ComponentType,
2526
OverlayState,
2627
ComponentPortal,
28+
ScrollStrategy,
29+
BlockScrollStrategy,
2730
} from '../core';
2831
import {PortalInjector} from '../core/portal/portal-injector';
2932
import {extendObject} from '../core/util/object-extend';
@@ -36,6 +39,23 @@ import {TemplatePortal} from '../core/portal/portal';
3639
export const MD_DIALOG_DATA = new InjectionToken<any>('MdDialogData');
3740

3841

42+
/** Injection token that determines the scroll handling while the dialog is open. */
43+
export const MD_DIALOG_SCROLL_STRATEGY =
44+
new InjectionToken<() => ScrollStrategy>('md-dialog-scroll-strategy');
45+
46+
/** @docs-private */
47+
export function MD_DIALOG_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay: Overlay) {
48+
return () => overlay.scrollStrategies.block();
49+
}
50+
51+
/** @docs-private */
52+
export const MD_DIALOG_SCROLL_STRATEGY_PROVIDER = {
53+
provide: MD_DIALOG_SCROLL_STRATEGY,
54+
deps: [Overlay],
55+
useFactory: MD_DIALOG_SCROLL_STRATEGY_PROVIDER_FACTORY,
56+
};
57+
58+
3959
/**
4060
* Service to open Material Design modal dialogs.
4161
*/
@@ -71,6 +91,7 @@ export class MdDialog {
7191
constructor(
7292
private _overlay: Overlay,
7393
private _injector: Injector,
94+
@Inject(MD_DIALOG_SCROLL_STRATEGY) private _scrollStrategy,
7495
@Optional() private _location: Location,
7596
@Optional() @SkipSelf() private _parentDialog: MdDialog) {
7697

@@ -143,7 +164,7 @@ export class MdDialog {
143164
let overlayState = new OverlayState();
144165
overlayState.panelClass = dialogConfig.panelClass;
145166
overlayState.hasBackdrop = dialogConfig.hasBackdrop;
146-
overlayState.scrollStrategy = this._overlay.scrollStrategies.block();
167+
overlayState.scrollStrategy = this._scrollStrategy();
147168
overlayState.direction = dialogConfig.direction;
148169
if (dialogConfig.backdropClass) {
149170
overlayState.backdropClass = dialogConfig.backdropClass;

src/lib/dialog/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
A11yModule,
1515
MdCommonModule,
1616
} from '../core';
17-
import {MdDialog} from './dialog';
17+
import {MdDialog, MD_DIALOG_SCROLL_STRATEGY_PROVIDER} from './dialog';
1818
import {MdDialogContainer} from './dialog-container';
1919
import {
2020
MdDialogClose,
@@ -49,6 +49,7 @@ import {
4949
],
5050
providers: [
5151
MdDialog,
52+
MD_DIALOG_SCROLL_STRATEGY_PROVIDER,
5253
],
5354
entryComponents: [MdDialogContainer],
5455
})

src/lib/menu/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {CommonModule} from '@angular/common';
1111
import {OverlayModule, MdCommonModule} from '../core';
1212
import {MdMenu} from './menu-directive';
1313
import {MdMenuItem} from './menu-item';
14-
import {MdMenuTrigger} from './menu-trigger';
14+
import {MdMenuTrigger, MD_MENU_SCROLL_STRATEGY_PROVIDER} from './menu-trigger';
1515
import {MdRippleModule} from '../core/ripple/index';
1616

1717

@@ -24,6 +24,7 @@ import {MdRippleModule} from '../core/ripple/index';
2424
],
2525
exports: [MdMenu, MdMenuItem, MdMenuTrigger, MdCommonModule],
2626
declarations: [MdMenu, MdMenuItem, MdMenuTrigger],
27+
providers: [MD_MENU_SCROLL_STRATEGY_PROVIDER],
2728
})
2829
export class MdMenuModule {}
2930

0 commit comments

Comments
 (0)