mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
feat(reposition-scroll-strategy): add option for closing once the user scrolls away (#8233)
Adds an option to the `RepositionScrollStrategy` that tells it to close the overlay once the user has scrolled away. This is a steps towards moving the scroll clipping logic away from the `ConnectedPositionStrategy`.
This commit is contained in:
committed by
Jeremy Elbourn
parent
dad5922025
commit
58598c457f
@@ -14,6 +14,7 @@ import {
|
||||
|
||||
describe('RepositionScrollStrategy', () => {
|
||||
let overlayRef: OverlayRef;
|
||||
let overlay: Overlay;
|
||||
let componentPortal: ComponentPortal<PastaMsg>;
|
||||
let scrolledSubject = new Subject();
|
||||
|
||||
@@ -30,9 +31,8 @@ describe('RepositionScrollStrategy', () => {
|
||||
TestBed.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(inject([Overlay], (overlay: Overlay) => {
|
||||
let overlayConfig = new OverlayConfig({scrollStrategy: overlay.scrollStrategies.reposition()});
|
||||
overlayRef = overlay.create(overlayConfig);
|
||||
beforeEach(inject([Overlay], (o: Overlay) => {
|
||||
overlay = o;
|
||||
componentPortal = new ComponentPortal(PastaMsg);
|
||||
}));
|
||||
|
||||
@@ -42,6 +42,11 @@ describe('RepositionScrollStrategy', () => {
|
||||
}));
|
||||
|
||||
it('should update the overlay position when the page is scrolled', () => {
|
||||
const overlayConfig = new OverlayConfig({
|
||||
scrollStrategy: overlay.scrollStrategies.reposition()
|
||||
});
|
||||
|
||||
overlayRef = overlay.create(overlayConfig);
|
||||
overlayRef.attach(componentPortal);
|
||||
spyOn(overlayRef, 'updatePosition');
|
||||
|
||||
@@ -53,6 +58,11 @@ describe('RepositionScrollStrategy', () => {
|
||||
});
|
||||
|
||||
it('should not be updating the position after the overlay is detached', () => {
|
||||
const overlayConfig = new OverlayConfig({
|
||||
scrollStrategy: overlay.scrollStrategies.reposition()
|
||||
});
|
||||
|
||||
overlayRef = overlay.create(overlayConfig);
|
||||
overlayRef.attach(componentPortal);
|
||||
spyOn(overlayRef, 'updatePosition');
|
||||
|
||||
@@ -63,6 +73,11 @@ describe('RepositionScrollStrategy', () => {
|
||||
});
|
||||
|
||||
it('should not be updating the position after the overlay is destroyed', () => {
|
||||
const overlayConfig = new OverlayConfig({
|
||||
scrollStrategy: overlay.scrollStrategies.reposition()
|
||||
});
|
||||
|
||||
overlayRef = overlay.create(overlayConfig);
|
||||
overlayRef.attach(componentPortal);
|
||||
spyOn(overlayRef, 'updatePosition');
|
||||
|
||||
@@ -72,6 +87,30 @@ describe('RepositionScrollStrategy', () => {
|
||||
expect(overlayRef.updatePosition).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should be able to close the overlay once it is out of view', () => {
|
||||
const overlayConfig = new OverlayConfig({
|
||||
scrollStrategy: overlay.scrollStrategies.reposition({
|
||||
autoClose: true
|
||||
})
|
||||
});
|
||||
|
||||
overlayRef = overlay.create(overlayConfig);
|
||||
overlayRef.attach(componentPortal);
|
||||
spyOn(overlayRef, 'updatePosition');
|
||||
spyOn(overlayRef, 'detach');
|
||||
spyOn(overlayRef.overlayElement, 'getBoundingClientRect').and.returnValue({
|
||||
top: -1000,
|
||||
bottom: -900,
|
||||
left: 0,
|
||||
right: 100,
|
||||
width: 100,
|
||||
height: 100
|
||||
});
|
||||
|
||||
scrolledSubject.next();
|
||||
expect(overlayRef.detach).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -6,16 +6,22 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {NgZone} from '@angular/core';
|
||||
import {Subscription} from 'rxjs/Subscription';
|
||||
import {ScrollStrategy, getMatScrollStrategyAlreadyAttachedError} from './scroll-strategy';
|
||||
import {OverlayRef} from '../overlay-ref';
|
||||
import {ScrollDispatcher} from '@angular/cdk/scrolling';
|
||||
import {ScrollDispatcher, ViewportRuler} from '@angular/cdk/scrolling';
|
||||
import {isElementScrolledOutsideView} from '../position/scroll-clip';
|
||||
|
||||
/**
|
||||
* Config options for the RepositionScrollStrategy.
|
||||
*/
|
||||
export interface RepositionScrollStrategyConfig {
|
||||
/** Time in milliseconds to throttle the scroll events. */
|
||||
scrollThrottle?: number;
|
||||
|
||||
/** Whether to close the overlay once the user has scrolled away completely. */
|
||||
autoClose?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -27,6 +33,8 @@ export class RepositionScrollStrategy implements ScrollStrategy {
|
||||
|
||||
constructor(
|
||||
private _scrollDispatcher: ScrollDispatcher,
|
||||
private _viewportRuler: ViewportRuler,
|
||||
private _ngZone: NgZone,
|
||||
private _config?: RepositionScrollStrategyConfig) { }
|
||||
|
||||
/** Attaches this scroll strategy to an overlay. */
|
||||
@@ -41,10 +49,25 @@ export class RepositionScrollStrategy implements ScrollStrategy {
|
||||
/** Enables repositioning of the attached overlay on scroll. */
|
||||
enable() {
|
||||
if (!this._scrollSubscription) {
|
||||
let throttle = this._config ? this._config.scrollThrottle : 0;
|
||||
const throttle = this._config ? this._config.scrollThrottle : 0;
|
||||
|
||||
this._scrollSubscription = this._scrollDispatcher.scrolled(throttle).subscribe(() => {
|
||||
this._overlayRef.updatePosition();
|
||||
|
||||
// TODO(crisbeto): make `close` on by default once all components can handle it.
|
||||
if (this._config && this._config.autoClose) {
|
||||
const overlayRect = this._overlayRef.overlayElement.getBoundingClientRect();
|
||||
const {width, height} = this._viewportRuler.getViewportSize();
|
||||
|
||||
// TODO(crisbeto): include all ancestor scroll containers here once
|
||||
// we have a way of exposing the trigger element to the scroll strategy.
|
||||
const parentRects = [{width, height, bottom: height, right: width, top: 0, left: 0}];
|
||||
|
||||
if (isElementScrolledOutsideView(overlayRect, parentRects)) {
|
||||
this.disable();
|
||||
this._ngZone.run(() => this._overlayRef.detach());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,6 @@ export class ScrollStrategyOptions {
|
||||
* @param config Configuration to be used inside the scroll strategy.
|
||||
* Allows debouncing the reposition calls.
|
||||
*/
|
||||
reposition = (config?: RepositionScrollStrategyConfig) =>
|
||||
new RepositionScrollStrategy(this._scrollDispatcher, config)
|
||||
reposition = (config?: RepositionScrollStrategyConfig) => new RepositionScrollStrategy(
|
||||
this._scrollDispatcher, this._viewportRuler, this._ngZone, config)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user