diff --git a/goldens/cdk/dialog/index.api.md b/goldens/cdk/dialog/index.api.md index 32eb409ced99..842f90728e83 100644 --- a/goldens/cdk/dialog/index.api.md +++ b/goldens/cdk/dialog/index.api.md @@ -123,7 +123,7 @@ export class DialogConfig(result: Result | undefined, config: Config, componentInstance: Component | null) => boolean; container?: Type | { diff --git a/goldens/cdk/overlay/index.api.md b/goldens/cdk/overlay/index.api.md index d98387f9038d..26d4e8491729 100644 --- a/goldens/cdk/overlay/index.api.md +++ b/goldens/cdk/overlay/index.api.md @@ -55,7 +55,7 @@ export class CdkConnectedOverlay implements OnDestroy, OnChanges { detachOverlay(): void; get dir(): Direction; disableClose: boolean; - disposeOnNavigation: boolean; + disposeOnNavigation: OverlayDisposeOnNavigation; flexibleDimensions: boolean; growAfterOpen: boolean; hasBackdrop: boolean; @@ -114,7 +114,7 @@ export interface CdkConnectedOverlayConfig { // (undocumented) disableClose?: boolean; // (undocumented) - disposeOnNavigation?: boolean; + disposeOnNavigation?: OverlayDisposeOnNavigation; // (undocumented) flexibleDimensions?: boolean; // (undocumented) @@ -391,7 +391,7 @@ export class OverlayConfig { backdropClass?: string | string[]; direction?: Direction | Directionality; disableAnimations?: boolean; - disposeOnNavigation?: boolean; + disposeOnNavigation?: OverlayDisposeOnNavigation; eventPredicate?: (event: Event) => boolean; hasBackdrop?: boolean; height?: number | string; @@ -440,6 +440,9 @@ export interface OverlayDefaultConfig { usePopover?: boolean; } +// @public +export type OverlayDisposeOnNavigation = boolean | 'url-change' | 'pop-state'; + // @public export class OverlayKeyboardDispatcher extends BaseOverlayDispatcher { add(overlayRef: OverlayRef): void; diff --git a/goldens/material/bottom-sheet/index.api.md b/goldens/material/bottom-sheet/index.api.md index 3a001c66e7f2..8bd8d94b0c70 100644 --- a/goldens/material/bottom-sheet/index.api.md +++ b/goldens/material/bottom-sheet/index.api.md @@ -19,6 +19,7 @@ import { InjectionToken } from '@angular/core'; import { Injector } from '@angular/core'; import { Observable } from 'rxjs'; import { OnDestroy } from '@angular/core'; +import { OverlayDisposeOnNavigation } from '@angular/cdk/overlay'; import { RestoreFocusValue } from '@angular/cdk/dialog'; import { ScrollStrategy } from '@angular/cdk/overlay'; import { TemplateRef } from '@angular/core'; @@ -55,7 +56,7 @@ export class MatBottomSheetConfig { autoFocus?: AutoFocusTarget | string | boolean; backdropClass?: string; bindings?: Binding[]; - closeOnNavigation?: boolean; + closeOnNavigation?: OverlayDisposeOnNavigation; data?: D | null; direction?: Direction; disableClose?: boolean; diff --git a/goldens/material/dialog/index.api.md b/goldens/material/dialog/index.api.md index 68c242b3f75c..6042b2d32410 100644 --- a/goldens/material/dialog/index.api.md +++ b/goldens/material/dialog/index.api.md @@ -27,6 +27,7 @@ import { Observable } from 'rxjs'; import { OnChanges } from '@angular/core'; import { OnDestroy } from '@angular/core'; import { OnInit } from '@angular/core'; +import { OverlayDisposeOnNavigation } from '@angular/cdk/overlay'; import { RestoreFocusValue } from '@angular/cdk/dialog'; import { ScrollStrategy } from '@angular/cdk/overlay'; import { SimpleChanges } from '@angular/core'; @@ -127,7 +128,7 @@ export class MatDialogConfig { autoFocus?: AutoFocusTarget | string | boolean; backdropClass?: string | string[]; bindings?: Binding[]; - closeOnNavigation?: boolean; + closeOnNavigation?: OverlayDisposeOnNavigation; closePredicate?: (result: Result | undefined, config: Config, componentInstance: Component | null) => boolean; data?: D | null; delayFocusTrap?: boolean; diff --git a/goldens/material/dialog/testing/index.api.md b/goldens/material/dialog/testing/index.api.md index b9220f42762c..61fcb58cd0d7 100644 --- a/goldens/material/dialog/testing/index.api.md +++ b/goldens/material/dialog/testing/index.api.md @@ -23,6 +23,7 @@ import * as i0 from '@angular/core'; import { Injector } from '@angular/core'; import { Observable } from 'rxjs'; import { OnDestroy } from '@angular/core'; +import { OverlayDisposeOnNavigation } from '@angular/cdk/overlay'; import { RestoreFocusValue } from '@angular/cdk/dialog'; import { ScrollStrategy } from '@angular/cdk/overlay'; import { Subject } from 'rxjs'; diff --git a/src/cdk/dialog/dialog-config.ts b/src/cdk/dialog/dialog-config.ts index 345238760497..9bd7072dc2e7 100644 --- a/src/cdk/dialog/dialog-config.ts +++ b/src/cdk/dialog/dialog-config.ts @@ -8,7 +8,7 @@ import {ViewContainerRef, Injector, StaticProvider, Type, Binding} from '@angular/core'; import {Direction} from '../bidi'; -import {PositionStrategy, ScrollStrategy} from '../overlay'; +import {OverlayDisposeOnNavigation, PositionStrategy, ScrollStrategy} from '../overlay'; import {Observable} from 'rxjs'; import {BasePortalOutlet} from '../portal'; import {FocusOrigin} from '../a11y'; @@ -140,12 +140,8 @@ export class DialogConfig + value === 'url-change' || value === 'pop-state' ? value : booleanAttribute(value), + }) + disposeOnNavigation: OverlayDisposeOnNavigation = false; /** Whether the connected overlay should be rendered inside a popover element or the overlay container. */ @Input({alias: 'cdkConnectedOverlayUsePopover'}) diff --git a/src/cdk/overlay/overlay-ref.ts b/src/cdk/overlay/overlay-ref.ts index 439500d5b43c..15f935480ccf 100644 --- a/src/cdk/overlay/overlay-ref.ts +++ b/src/cdk/overlay/overlay-ref.ts @@ -18,7 +18,7 @@ import { signal, untracked, } from '@angular/core'; -import {Observable, Subject, Subscription, SubscriptionLike} from 'rxjs'; +import {Observable, Subject} from 'rxjs'; import {Direction, Directionality} from '../bidi'; import {coerceArray, coerceCssPixelValue} from '../coercion'; import {ComponentPortal, Portal, PortalOutlet, TemplatePortal} from '../portal'; @@ -54,7 +54,7 @@ export class OverlayRef implements PortalOutlet { private readonly _detachments = new Subject(); private _positionStrategy: PositionStrategy | undefined; private _scrollStrategy: ScrollStrategy | undefined; - private _locationChanges: SubscriptionLike = Subscription.EMPTY; + private _locationChanges: (() => void) | undefined; private _backdropRef: BackdropRef | null = null; private _detachContentMutationObserver: MutationObserver | undefined; private _detachContentAfterRenderRef: AfterRenderRef | undefined; @@ -194,8 +194,14 @@ export class OverlayRef implements PortalOutlet { // Track this overlay by the keyboard dispatcher this._keyboardDispatcher.add(this); - if (this._config.disposeOnNavigation) { - this._locationChanges = this._location.subscribe(() => this.dispose()); + if ( + this._config.disposeOnNavigation === true || + this._config.disposeOnNavigation === 'pop-state' + ) { + const subscription = this._location.subscribe(() => this.dispose()); + this._locationChanges = () => subscription.unsubscribe(); + } else if (this._config.disposeOnNavigation === 'url-change') { + this._locationChanges = this._location.onUrlChange(() => this.dispose()); } this._outsideClickDispatcher.add(this); @@ -258,7 +264,7 @@ export class OverlayRef implements PortalOutlet { // Keeping the host element in the DOM can cause scroll jank, because it still gets // rendered, even though it's transparent and unclickable which is why we remove it. this._detachContentWhenEmpty(); - this._locationChanges.unsubscribe(); + this._locationChanges?.(); this._outsideClickDispatcher.remove(this); untracked(() => { @@ -282,7 +288,7 @@ export class OverlayRef implements PortalOutlet { this._disposeScrollStrategy(); this._backdropRef?.dispose(); - this._locationChanges.unsubscribe(); + this._locationChanges?.(); this._keyboardDispatcher.remove(this); this._portalOutlet.dispose(); this._attachments.complete(); diff --git a/src/material/bottom-sheet/bottom-sheet-config.ts b/src/material/bottom-sheet/bottom-sheet-config.ts index 9f25f53c0f5c..8358389b81f6 100644 --- a/src/material/bottom-sheet/bottom-sheet-config.ts +++ b/src/material/bottom-sheet/bottom-sheet-config.ts @@ -8,7 +8,7 @@ import {Binding, InjectionToken, Injector, ViewContainerRef} from '@angular/core'; import {Direction} from '@angular/cdk/bidi'; -import {ScrollStrategy} from '@angular/cdk/overlay'; +import {OverlayDisposeOnNavigation, ScrollStrategy} from '@angular/cdk/overlay'; import {RestoreFocusValue} from '@angular/cdk/dialog'; /** Options for where to set focus to automatically on dialog open */ @@ -58,12 +58,8 @@ export class MatBottomSheetConfig { */ ariaModal?: boolean = false; - /** - * Whether the bottom sheet should close when the user goes backwards/forwards in history. - * Note that this usually doesn't include clicking on links (unless the user is using - * the `HashLocationStrategy`). - */ - closeOnNavigation?: boolean = true; + /** Whether the bottom sheet should be disposed of when the user navigates. */ + closeOnNavigation?: OverlayDisposeOnNavigation = true; /** * Where the bottom sheet should focus on open. diff --git a/src/material/dialog/dialog-config.ts b/src/material/dialog/dialog-config.ts index 0ebf4ee37b68..ea9f5428981d 100644 --- a/src/material/dialog/dialog-config.ts +++ b/src/material/dialog/dialog-config.ts @@ -8,7 +8,7 @@ import {ViewContainerRef, Injector, Binding} from '@angular/core'; import {Direction} from '@angular/cdk/bidi'; -import {ScrollStrategy} from '@angular/cdk/overlay'; +import {OverlayDisposeOnNavigation, ScrollStrategy} from '@angular/cdk/overlay'; import {DialogConfig, RestoreFocusValue} from '@angular/cdk/dialog'; /** Options for where to set focus to automatically on dialog open */ @@ -138,12 +138,8 @@ export class MatDialogConfig { /** Scroll strategy to be used for the dialog. */ scrollStrategy?: ScrollStrategy; - /** - * Whether the dialog should close when the user goes backwards/forwards in history. - * Note that this usually doesn't include clicking on links (unless the user is using - * the `HashLocationStrategy`). - */ - closeOnNavigation?: boolean = true; + /** Whether the bottom sheet should be disposed of when the user navigates. */ + closeOnNavigation?: OverlayDisposeOnNavigation = true; /** * Duration of the enter animation in ms.