Skip to content

Commit 7b169e8

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 c81e608 commit 7b169e8

File tree

15 files changed

+197
-25
lines changed

15 files changed

+197
-25
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';
@@ -49,6 +57,22 @@ export const AUTOCOMPLETE_OPTION_HEIGHT = 48;
4957
/** The total height of the autocomplete panel. */
5058
export const AUTOCOMPLETE_PANEL_HEIGHT = 256;
5159

60+
/** Injection token that determines the scroll handling while the autocomplete panel is open. */
61+
export const MD_AUTOCOMPLETE_SCROLL_STRATEGY =
62+
new InjectionToken<() => ScrollStrategy>('md-autocomplete-scroll-strategy');
63+
64+
/** @docs-private */
65+
export function MD_AUTOCOMPLETE_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay: Overlay) {
66+
return () => overlay.scrollStrategies.reposition();
67+
}
68+
69+
/** @docs-private */
70+
export const MD_AUTOCOMPLETE_SCROLL_STRATEGY_PROVIDER = {
71+
provide: MD_AUTOCOMPLETE_SCROLL_STRATEGY,
72+
deps: [Overlay],
73+
useFactory: MD_AUTOCOMPLETE_SCROLL_STRATEGY_PROVIDER_FACTORY,
74+
};
75+
5276
/**
5377
* Provider that allows the autocomplete to register as a ControlValueAccessor.
5478
* @docs-private
@@ -125,6 +149,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
125149
private _viewContainerRef: ViewContainerRef,
126150
private _zone: NgZone,
127151
private _changeDetectorRef: ChangeDetectorRef,
152+
@Inject(MD_AUTOCOMPLETE_SCROLL_STRATEGY) private _scrollStrategy,
128153
@Optional() private _dir: Directionality,
129154
@Optional() @Host() private _inputContainer: MdInputContainer,
130155
@Optional() @Inject(DOCUMENT) private _document: any) {}
@@ -399,7 +424,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
399424
overlayState.positionStrategy = this._getOverlayPosition();
400425
overlayState.width = this._getHostWidth();
401426
overlayState.direction = this._dir ? this._dir.value : 'ltr';
402-
overlayState.scrollStrategy = this._overlay.scrollStrategies.reposition();
427+
overlayState.scrollStrategy = this._scrollStrategy();
403428
return overlayState;
404429
}
405430

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 & 4 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';
@@ -30,15 +32,14 @@ import {
3032
} from './position/connected-position';
3133
import {ConnectedPositionStrategy} from './position/connected-position-strategy';
3234
import {Directionality, Direction} from '../bidi/index';
33-
import {Scrollable} from './scroll/scrollable';
34-
import {ScrollStrategy} from './scroll/scroll-strategy';
3535
import {coerceBooleanProperty} from '@angular/cdk';
36+
import {ScrollStrategy, Scrollable, RepositionScrollStrategy} from './scroll/index';
3637
import {ESCAPE} from '../keyboard/keycodes';
3738
import {Subscription} from 'rxjs/Subscription';
3839

3940

4041
/** Default set of positions for the overlay. Follows the behavior of a dropdown. */
41-
let defaultPositionList = [
42+
const defaultPositionList = [
4243
new ConnectionPositionPair(
4344
{originX: 'start', originY: 'bottom'},
4445
{overlayX: 'start', overlayY: 'top'}),
@@ -47,6 +48,23 @@ let defaultPositionList = [
4748
{overlayX: 'start', overlayY: 'bottom'}),
4849
];
4950

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

5169
/**
5270
* Directive applied to an element to make it usable as an origin for an Overlay using a
@@ -128,7 +146,7 @@ export class ConnectedOverlayDirective implements OnDestroy, OnChanges {
128146
@Input() backdropClass: string;
129147

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

133151
/** Whether the overlay is open. */
134152
@Input() open: boolean = false;
@@ -162,6 +180,7 @@ export class ConnectedOverlayDirective implements OnDestroy, OnChanges {
162180
private _renderer: Renderer2,
163181
templateRef: TemplateRef<any>,
164182
viewContainerRef: ViewContainerRef,
183+
@Inject(MD_CONNECTED_OVERLAY_SCROLL_STRATEGY) private _scrollStrategy,
165184
@Optional() private _dir: Directionality) {
166185
this._templatePortal = new TemplatePortal(templateRef, viewContainerRef);
167186
}

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 'rxjs/add/operator/first';
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
@@ -163,6 +184,7 @@ export class MdDatepicker<D> implements OnDestroy {
163184
private _overlay: Overlay,
164185
private _ngZone: NgZone,
165186
private _viewContainerRef: ViewContainerRef,
187+
@Inject(MD_DATEPICKER_SCROLL_STRATEGY) private _scrollStrategy,
166188
@Optional() private _dateAdapter: DateAdapter<D>,
167189
@Optional() private _dir: Directionality,
168190
@Optional() @Inject(DOCUMENT) private _document: any) {
@@ -282,7 +304,7 @@ export class MdDatepicker<D> implements OnDestroy {
282304
overlayState.hasBackdrop = true;
283305
overlayState.backdropClass = 'md-overlay-transparent-backdrop';
284306
overlayState.direction = this._dir ? this._dir.value : 'ltr';
285-
overlayState.scrollStrategy = this._overlay.scrollStrategies.reposition();
307+
overlayState.scrollStrategy = this._scrollStrategy();
286308

287309
this._popupRef = this._overlay.create(overlayState);
288310
}

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 {StyleModule, OverlayModule, A11yModule} 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 {extendObject} from '../core/util/object-extend';
2932
import {ESCAPE} from '../core/keyboard/keycodes';
@@ -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)