Skip to content

Commit 184a6e4

Browse files
crisbetojelbourn
authored andcommitted
feat(overlay): add providers for overriding the scroll strategies per 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.
1 parent c20bec8 commit 184a6e4

File tree

15 files changed

+214
-23
lines changed

15 files changed

+214
-23
lines changed

src/lib/autocomplete/autocomplete-trigger.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,21 @@ 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+
RepositionScrollStrategy,
31+
// This import is only used to define a generic type. The current TypeScript version incorrectly
32+
// considers such imports as unused (https://github.com/Microsoft/TypeScript/issues/14953)
33+
// tslint:disable-next-line:no-unused-variable
34+
ScrollStrategy,
35+
} from '../core';
2536
import {MdAutocomplete} from './autocomplete';
2637
import {PositionStrategy} from '../core/overlay/position/position-strategy';
2738
import {ConnectedPositionStrategy} from '../core/overlay/position/connected-position-strategy';
@@ -48,6 +59,22 @@ export const AUTOCOMPLETE_OPTION_HEIGHT = 48;
4859
/** The total height of the autocomplete panel. */
4960
export const AUTOCOMPLETE_PANEL_HEIGHT = 256;
5061

62+
/** Injection token that determines the scroll handling while the autocomplete panel is open. */
63+
export const MD_AUTOCOMPLETE_SCROLL_STRATEGY =
64+
new InjectionToken<() => ScrollStrategy>('md-autocomplete-scroll-strategy');
65+
66+
/** @docs-private */
67+
export function MD_AUTOCOMPLETE_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay: Overlay) {
68+
return () => overlay.scrollStrategies.reposition();
69+
}
70+
71+
/** @docs-private */
72+
export const MD_AUTOCOMPLETE_SCROLL_STRATEGY_PROVIDER = {
73+
provide: MD_AUTOCOMPLETE_SCROLL_STRATEGY,
74+
deps: [Overlay],
75+
useFactory: MD_AUTOCOMPLETE_SCROLL_STRATEGY_PROVIDER_FACTORY,
76+
};
77+
5178
/**
5279
* Provider that allows the autocomplete to register as a ControlValueAccessor.
5380
* @docs-private
@@ -127,6 +154,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
127154
private _viewContainerRef: ViewContainerRef,
128155
private _zone: NgZone,
129156
private _changeDetectorRef: ChangeDetectorRef,
157+
@Inject(MD_AUTOCOMPLETE_SCROLL_STRATEGY) private _scrollStrategy,
130158
@Optional() private _dir: Directionality,
131159
@Optional() @Host() private _inputContainer: MdInputContainer,
132160
@Optional() @Inject(DOCUMENT) private _document: any) {}
@@ -419,7 +447,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
419447
overlayState.positionStrategy = this._getOverlayPosition();
420448
overlayState.width = this._getHostWidth();
421449
overlayState.direction = this._dir ? this._dir.value : 'ltr';
422-
overlayState.scrollStrategy = this._overlay.scrollStrategies.reposition();
450+
overlayState.scrollStrategy = this._scrollStrategy();
423451
return overlayState;
424452
}
425453

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, 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: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,20 @@ 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+
RepositionScrollStrategy,
32+
// This import is only used to define a generic type. The current TypeScript version incorrectly
33+
// considers such imports as unused (https://github.com/Microsoft/TypeScript/issues/14953)
34+
// tslint:disable-next-line:no-unused-variable
35+
ScrollStrategy,
36+
} from '../core/overlay/index';
2837
import {ComponentPortal} from '../core/portal/portal';
29-
import {OverlayState} from '../core/overlay/overlay-state';
3038
import {Directionality} from '../core/bidi/index';
3139
import {MdDialog} from '../dialog/dialog';
3240
import {MdDialogRef} from '../dialog/dialog-ref';
@@ -44,6 +52,22 @@ import {coerceBooleanProperty} from '@angular/cdk';
4452
/** Used to generate a unique ID for each datepicker instance. */
4553
let datepickerUid = 0;
4654

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

4872
/**
4973
* Component used as the content for the datepicker dialog and popup. We use this instead of using
@@ -174,6 +198,7 @@ export class MdDatepicker<D> implements OnDestroy {
174198
private _overlay: Overlay,
175199
private _ngZone: NgZone,
176200
private _viewContainerRef: ViewContainerRef,
201+
@Inject(MD_DATEPICKER_SCROLL_STRATEGY) private _scrollStrategy,
177202
@Optional() private _dateAdapter: DateAdapter<D>,
178203
@Optional() private _dir: Directionality,
179204
@Optional() @Inject(DOCUMENT) private _document: any) {
@@ -293,7 +318,7 @@ export class MdDatepicker<D> implements OnDestroy {
293318
overlayState.hasBackdrop = true;
294319
overlayState.backdropClass = 'md-overlay-transparent-backdrop';
295320
overlayState.direction = this._dir ? this._dir.value : 'ltr';
296-
overlayState.scrollStrategy = this._overlay.scrollStrategies.reposition();
321+
overlayState.scrollStrategy = this._scrollStrategy();
297322

298323
this._popupRef = this._overlay.create(overlayState);
299324
}

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: 26 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,11 @@ import {
2425
ComponentType,
2526
OverlayState,
2627
ComponentPortal,
28+
BlockScrollStrategy,
29+
// This import is only used to define a generic type. The current TypeScript version incorrectly
30+
// considers such imports as unused (https://github.com/Microsoft/TypeScript/issues/14953)
31+
// tslint:disable-next-line:no-unused-variable
32+
ScrollStrategy,
2733
} from '../core';
2834
import {PortalInjector} from '../core/portal/portal-injector';
2935
import {extendObject} from '../core/util/object-extend';
@@ -36,6 +42,23 @@ import {TemplatePortal} from '../core/portal/portal';
3642
export const MD_DIALOG_DATA = new InjectionToken<any>('MdDialogData');
3743

3844

45+
/** Injection token that determines the scroll handling while the dialog is open. */
46+
export const MD_DIALOG_SCROLL_STRATEGY =
47+
new InjectionToken<() => ScrollStrategy>('md-dialog-scroll-strategy');
48+
49+
/** @docs-private */
50+
export function MD_DIALOG_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay: Overlay) {
51+
return () => overlay.scrollStrategies.block();
52+
}
53+
54+
/** @docs-private */
55+
export const MD_DIALOG_SCROLL_STRATEGY_PROVIDER = {
56+
provide: MD_DIALOG_SCROLL_STRATEGY,
57+
deps: [Overlay],
58+
useFactory: MD_DIALOG_SCROLL_STRATEGY_PROVIDER_FACTORY,
59+
};
60+
61+
3962
/**
4063
* Service to open Material Design modal dialogs.
4164
*/
@@ -71,6 +94,7 @@ export class MdDialog {
7194
constructor(
7295
private _overlay: Overlay,
7396
private _injector: Injector,
97+
@Inject(MD_DIALOG_SCROLL_STRATEGY) private _scrollStrategy,
7498
@Optional() private _location: Location,
7599
@Optional() @SkipSelf() private _parentDialog: MdDialog) {
76100

@@ -143,7 +167,7 @@ export class MdDialog {
143167
let overlayState = new OverlayState();
144168
overlayState.panelClass = dialogConfig.panelClass;
145169
overlayState.hasBackdrop = dialogConfig.hasBackdrop;
146-
overlayState.scrollStrategy = this._overlay.scrollStrategies.block();
170+
overlayState.scrollStrategy = this._scrollStrategy();
147171
overlayState.direction = dialogConfig.direction;
148172
if (dialogConfig.backdropClass) {
149173
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)