Skip to content

Commit 100e123

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 100e123

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)