From 1f25d2c17d5733789ff360e616bc29155632cc59 Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Sat, 21 May 2022 10:10:17 +0200 Subject: [PATCH] refactor(material/bottom-sheet): use CDK dialog internally Switches the internals of the bottom sheet to use the CDK dialog. --- src/material/bottom-sheet/BUILD.bazel | 1 + .../bottom-sheet/bottom-sheet-container.ts | 233 +++--------------- .../bottom-sheet/bottom-sheet-module.ts | 4 +- src/material/bottom-sheet/bottom-sheet-ref.ts | 87 +++---- src/material/bottom-sheet/bottom-sheet.ts | 159 +++--------- .../public_api_guard/material/bottom-sheet.md | 36 ++- 6 files changed, 127 insertions(+), 393 deletions(-) diff --git a/src/material/bottom-sheet/BUILD.bazel b/src/material/bottom-sheet/BUILD.bazel index ddd62691eb0f..392926012289 100644 --- a/src/material/bottom-sheet/BUILD.bazel +++ b/src/material/bottom-sheet/BUILD.bazel @@ -24,6 +24,7 @@ ng_module( "//src/cdk/a11y", "//src/cdk/bidi", "//src/cdk/coercion", + "//src/cdk/dialog", "//src/cdk/keycodes", "//src/cdk/layout", "//src/cdk/overlay", diff --git a/src/material/bottom-sheet/bottom-sheet-container.ts b/src/material/bottom-sheet/bottom-sheet-container.ts index 390d63885277..16d31898f348 100644 --- a/src/material/bottom-sheet/bottom-sheet-container.ts +++ b/src/material/bottom-sheet/bottom-sheet-container.ts @@ -7,38 +7,26 @@ */ import {AnimationEvent} from '@angular/animations'; -import {FocusTrap, FocusTrapFactory, InteractivityChecker} from '@angular/cdk/a11y'; -import {coerceArray} from '@angular/cdk/coercion'; +import {CdkDialogContainer, DialogConfig} from '@angular/cdk/dialog'; +import {FocusMonitor, FocusTrapFactory, InteractivityChecker} from '@angular/cdk/a11y'; import {BreakpointObserver, Breakpoints} from '@angular/cdk/layout'; +import {OverlayRef} from '@angular/cdk/overlay'; import {_getFocusedElementPierceShadowDom} from '@angular/cdk/platform'; -import { - BasePortalOutlet, - CdkPortalOutlet, - ComponentPortal, - DomPortal, - TemplatePortal, -} from '@angular/cdk/portal'; import {DOCUMENT} from '@angular/common'; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, - ComponentRef, ElementRef, - EmbeddedViewRef, EventEmitter, Inject, NgZone, OnDestroy, Optional, - ViewChild, ViewEncapsulation, } from '@angular/core'; import {Subscription} from 'rxjs'; import {matBottomSheetAnimations} from './bottom-sheet-animations'; -import {MatBottomSheetConfig} from './bottom-sheet-config'; - -// TODO(crisbeto): consolidate some logic between this, MatDialog and MatSnackBar /** * Internal component that wraps user-provided bottom sheet content. @@ -58,52 +46,49 @@ import {MatBottomSheetConfig} from './bottom-sheet-config'; host: { 'class': 'mat-bottom-sheet-container', 'tabindex': '-1', - 'role': 'dialog', - 'aria-modal': 'true', - '[attr.aria-label]': 'bottomSheetConfig?.ariaLabel', + '[attr.role]': '_config.role', + '[attr.aria-modal]': '_config.isModal', + '[attr.aria-label]': '_config.ariaLabel', '[@state]': '_animationState', '(@state.start)': '_onAnimationStart($event)', '(@state.done)': '_onAnimationDone($event)', }, }) -export class MatBottomSheetContainer extends BasePortalOutlet implements OnDestroy { +export class MatBottomSheetContainer extends CdkDialogContainer implements OnDestroy { private _breakpointSubscription: Subscription; - /** The portal outlet inside of this container into which the content will be loaded. */ - @ViewChild(CdkPortalOutlet, {static: true}) _portalOutlet: CdkPortalOutlet; - /** The state of the bottom sheet animations. */ _animationState: 'void' | 'visible' | 'hidden' = 'void'; /** Emits whenever the state of the animation changes. */ _animationStateChanged = new EventEmitter(); - /** The class that traps and manages focus within the bottom sheet. */ - private _focusTrap: FocusTrap; - - /** Element that was focused before the bottom sheet was opened. */ - private _elementFocusedBeforeOpened: HTMLElement | null = null; - - /** Server-side rendering-compatible reference to the global document object. */ - private _document: Document; - /** Whether the component has been destroyed. */ private _destroyed: boolean; constructor( - private _elementRef: ElementRef, - private _changeDetectorRef: ChangeDetectorRef, - private _focusTrapFactory: FocusTrapFactory, - private readonly _interactivityChecker: InteractivityChecker, - private readonly _ngZone: NgZone, - breakpointObserver: BreakpointObserver, + elementRef: ElementRef, + focusTrapFactory: FocusTrapFactory, @Optional() @Inject(DOCUMENT) document: any, - /** The bottom sheet configuration. */ - public bottomSheetConfig: MatBottomSheetConfig, + config: DialogConfig, + checker: InteractivityChecker, + ngZone: NgZone, + overlayRef: OverlayRef, + breakpointObserver: BreakpointObserver, + private _changeDetectorRef: ChangeDetectorRef, + focusMonitor?: FocusMonitor, ) { - super(); + super( + elementRef, + focusTrapFactory, + document, + config, + checker, + ngZone, + overlayRef, + focusMonitor, + ); - this._document = document; this._breakpointSubscription = breakpointObserver .observe([Breakpoints.Medium, Breakpoints.Large, Breakpoints.XLarge]) .subscribe(() => { @@ -122,34 +107,6 @@ export class MatBottomSheetContainer extends BasePortalOutlet implements OnDestr }); } - /** Attach a component portal as content to this bottom sheet container. */ - attachComponentPortal(portal: ComponentPortal): ComponentRef { - this._validatePortalAttached(); - this._setPanelClass(); - this._savePreviouslyFocusedElement(); - return this._portalOutlet.attachComponentPortal(portal); - } - - /** Attach a template portal as content to this bottom sheet container. */ - attachTemplatePortal(portal: TemplatePortal): EmbeddedViewRef { - this._validatePortalAttached(); - this._setPanelClass(); - this._savePreviouslyFocusedElement(); - return this._portalOutlet.attachTemplatePortal(portal); - } - - /** - * Attaches a DOM portal to the bottom sheet container. - * @deprecated To be turned into a method. - * @breaking-change 10.0.0 - */ - override attachDomPortal = (portal: DomPortal) => { - this._validatePortalAttached(); - this._setPanelClass(); - this._savePreviouslyFocusedElement(); - return this._portalOutlet.attachDomPortal(portal); - }; - /** Begin animation of bottom sheet entrance into view. */ enter(): void { if (!this._destroyed) { @@ -166,15 +123,14 @@ export class MatBottomSheetContainer extends BasePortalOutlet implements OnDestr } } - ngOnDestroy() { + override ngOnDestroy() { + super.ngOnDestroy(); this._breakpointSubscription.unsubscribe(); this._destroyed = true; } _onAnimationDone(event: AnimationEvent) { - if (event.toState === 'hidden') { - this._restoreFocus(); - } else if (event.toState === 'visible') { + if (event.toState === 'visible') { this._trapFocus(); } @@ -185,136 +141,9 @@ export class MatBottomSheetContainer extends BasePortalOutlet implements OnDestr this._animationStateChanged.emit(event); } + protected override _captureInitialFocus(): void {} + private _toggleClass(cssClass: string, add: boolean) { this._elementRef.nativeElement.classList.toggle(cssClass, add); } - - private _validatePortalAttached() { - if (this._portalOutlet.hasAttached() && (typeof ngDevMode === 'undefined' || ngDevMode)) { - throw Error('Attempting to attach bottom sheet content after content is already attached'); - } - } - - private _setPanelClass() { - const element: HTMLElement = this._elementRef.nativeElement; - element.classList.add(...coerceArray(this.bottomSheetConfig.panelClass || [])); - } - - /** - * Focuses the provided element. If the element is not focusable, it will add a tabIndex - * attribute to forcefully focus it. The attribute is removed after focus is moved. - * @param element The element to focus. - */ - private _forceFocus(element: HTMLElement, options?: FocusOptions) { - if (!this._interactivityChecker.isFocusable(element)) { - element.tabIndex = -1; - // The tabindex attribute should be removed to avoid navigating to that element again - this._ngZone.runOutsideAngular(() => { - const callback = () => { - element.removeEventListener('blur', callback); - element.removeEventListener('mousedown', callback); - element.removeAttribute('tabindex'); - }; - - element.addEventListener('blur', callback); - element.addEventListener('mousedown', callback); - }); - } - element.focus(options); - } - - /** - * Focuses the first element that matches the given selector within the focus trap. - * @param selector The CSS selector for the element to set focus to. - */ - private _focusByCssSelector(selector: string, options?: FocusOptions) { - let elementToFocus = this._elementRef.nativeElement.querySelector( - selector, - ) as HTMLElement | null; - if (elementToFocus) { - this._forceFocus(elementToFocus, options); - } - } - - /** - * Moves the focus inside the focus trap. When autoFocus is not set to 'bottom-sheet', - * if focus cannot be moved then focus will go to the bottom sheet container. - */ - private _trapFocus() { - const element = this._elementRef.nativeElement; - - if (!this._focusTrap) { - this._focusTrap = this._focusTrapFactory.create(element); - } - - // If were to attempt to focus immediately, then the content of the bottom sheet would not - // yet be ready in instances where change detection has to run first. To deal with this, - // we simply wait for the microtask queue to be empty when setting focus when autoFocus - // isn't set to bottom sheet. If the element inside the bottom sheet can't be focused, - // then the container is focused so the user can't tab into other elements behind it. - switch (this.bottomSheetConfig.autoFocus) { - case false: - case 'dialog': - const activeElement = _getFocusedElementPierceShadowDom(); - // Ensure that focus is on the bottom sheet container. It's possible that a different - // component tried to move focus while the open animation was running. See: - // https://github.com/angular/components/issues/16215. Note that we only want to do this - // if the focus isn't inside the bottom sheet already, because it's possible that the - // consumer specified `autoFocus` in order to move focus themselves. - if (activeElement !== element && !element.contains(activeElement)) { - element.focus(); - } - break; - case true: - case 'first-tabbable': - this._focusTrap.focusInitialElementWhenReady(); - break; - case 'first-heading': - this._focusByCssSelector('h1, h2, h3, h4, h5, h6, [role="heading"]'); - break; - default: - this._focusByCssSelector(this.bottomSheetConfig.autoFocus!); - break; - } - } - - /** Restores focus to the element that was focused before the bottom sheet was opened. */ - private _restoreFocus() { - const toFocus = this._elementFocusedBeforeOpened; - - // We need the extra check, because IE can set the `activeElement` to null in some cases. - if (this.bottomSheetConfig.restoreFocus && toFocus && typeof toFocus.focus === 'function') { - const activeElement = _getFocusedElementPierceShadowDom(); - const element = this._elementRef.nativeElement; - - // Make sure that focus is still inside the bottom sheet or is on the body (usually because a - // non-focusable element like the backdrop was clicked) before moving it. It's possible that - // the consumer moved it themselves before the animation was done, in which case we shouldn't - // do anything. - if ( - !activeElement || - activeElement === this._document.body || - activeElement === element || - element.contains(activeElement) - ) { - toFocus.focus(); - } - } - - if (this._focusTrap) { - this._focusTrap.destroy(); - } - } - - /** Saves a reference to the element that was focused before the bottom sheet was opened. */ - private _savePreviouslyFocusedElement() { - this._elementFocusedBeforeOpened = _getFocusedElementPierceShadowDom(); - - // The `focus` method isn't available during server-side rendering. - if (this._elementRef.nativeElement.focus) { - this._ngZone.runOutsideAngular(() => { - Promise.resolve().then(() => this._elementRef.nativeElement.focus()); - }); - } - } } diff --git a/src/material/bottom-sheet/bottom-sheet-module.ts b/src/material/bottom-sheet/bottom-sheet-module.ts index 38a5c3957291..690a554a319f 100644 --- a/src/material/bottom-sheet/bottom-sheet-module.ts +++ b/src/material/bottom-sheet/bottom-sheet-module.ts @@ -6,14 +6,14 @@ * found in the LICENSE file at https://angular.io/license */ -import {OverlayModule} from '@angular/cdk/overlay'; +import {DialogModule} from '@angular/cdk/dialog'; import {PortalModule} from '@angular/cdk/portal'; import {NgModule} from '@angular/core'; import {MatCommonModule} from '@angular/material/core'; import {MatBottomSheetContainer} from './bottom-sheet-container'; @NgModule({ - imports: [OverlayModule, MatCommonModule, PortalModule], + imports: [DialogModule, MatCommonModule, PortalModule], exports: [MatBottomSheetContainer, MatCommonModule], declarations: [MatBottomSheetContainer], }) diff --git a/src/material/bottom-sheet/bottom-sheet-ref.ts b/src/material/bottom-sheet/bottom-sheet-ref.ts index d3aa2de16f62..a2e47a4ab8fd 100644 --- a/src/material/bottom-sheet/bottom-sheet-ref.ts +++ b/src/material/bottom-sheet/bottom-sheet-ref.ts @@ -6,10 +6,11 @@ * found in the LICENSE file at https://angular.io/license */ +import {DialogRef} from '@angular/cdk/dialog'; import {ESCAPE, hasModifierKey} from '@angular/cdk/keycodes'; -import {OverlayRef} from '@angular/cdk/overlay'; import {merge, Observable, Subject} from 'rxjs'; import {filter, take} from 'rxjs/operators'; +import {MatBottomSheetConfig} from './bottom-sheet-config'; import {MatBottomSheetContainer} from './bottom-sheet-container'; /** @@ -17,7 +18,9 @@ import {MatBottomSheetContainer} from './bottom-sheet-container'; */ export class MatBottomSheetRef { /** Instance of the component making up the content of the bottom sheet. */ - instance: T; + get instance(): T { + return this._ref.componentInstance!; + } /** * Instance of the component into which the bottom sheet content is projected. @@ -28,9 +31,6 @@ export class MatBottomSheetRef { /** Whether the user is allowed to close the bottom sheet. */ disableClose: boolean | undefined; - /** Subject for notifying the user that the bottom sheet has been dismissed. */ - private readonly _afterDismissed = new Subject(); - /** Subject for notifying the user that the bottom sheet has opened and appeared. */ private readonly _afterOpened = new Subject(); @@ -40,9 +40,13 @@ export class MatBottomSheetRef { /** Handle to the timeout that's running as a fallback in case the exit animation doesn't fire. */ private _closeFallbackTimeout: number; - constructor(containerInstance: MatBottomSheetContainer, private _overlayRef: OverlayRef) { + constructor( + private _ref: DialogRef, + config: MatBottomSheetConfig, + containerInstance: MatBottomSheetContainer, + ) { this.containerInstance = containerInstance; - this.disableClose = containerInstance.bottomSheetConfig.disableClose; + this.disableClose = config.disableClose; // Emit when opening animation completes containerInstance._animationStateChanged @@ -63,20 +67,16 @@ export class MatBottomSheetRef { ) .subscribe(() => { clearTimeout(this._closeFallbackTimeout); - _overlayRef.dispose(); + this._ref.close(this._result); }); - _overlayRef - .detachments() - .pipe(take(1)) - .subscribe(() => { - this._afterDismissed.next(this._result); - this._afterDismissed.complete(); - }); + _ref.overlayRef.detachments().subscribe(() => { + this._ref.close(this._result); + }); merge( - _overlayRef.backdropClick(), - _overlayRef.keydownEvents().pipe(filter(event => event.keyCode === ESCAPE)), + this.backdropClick(), + this.keydownEvents().pipe(filter(event => event.keyCode === ESCAPE)), ).subscribe(event => { if ( !this.disableClose && @@ -93,34 +93,37 @@ export class MatBottomSheetRef { * @param result Data to be passed back to the bottom sheet opener. */ dismiss(result?: R): void { - if (!this._afterDismissed.closed) { - // Transition the backdrop in parallel to the bottom sheet. - this.containerInstance._animationStateChanged - .pipe( - filter(event => event.phaseName === 'start'), - take(1), - ) - .subscribe(event => { - // The logic that disposes of the overlay depends on the exit animation completing, however - // it isn't guaranteed if the parent view is destroyed while it's running. Add a fallback - // timeout which will clean everything up if the animation hasn't fired within the specified - // amount of time plus 100ms. We don't need to run this outside the NgZone, because for the - // vast majority of cases the timeout will have been cleared before it has fired. - this._closeFallbackTimeout = setTimeout(() => { - this._overlayRef.dispose(); - }, event.totalTime + 100); - - this._overlayRef.detachBackdrop(); - }); - - this._result = result; - this.containerInstance.exit(); + if (!this.containerInstance) { + return; } + + // Transition the backdrop in parallel to the bottom sheet. + this.containerInstance._animationStateChanged + .pipe( + filter(event => event.phaseName === 'start'), + take(1), + ) + .subscribe(event => { + // The logic that disposes of the overlay depends on the exit animation completing, however + // it isn't guaranteed if the parent view is destroyed while it's running. Add a fallback + // timeout which will clean everything up if the animation hasn't fired within the specified + // amount of time plus 100ms. We don't need to run this outside the NgZone, because for the + // vast majority of cases the timeout will have been cleared before it has fired. + this._closeFallbackTimeout = setTimeout(() => { + this._ref.close(this._result); + }, event.totalTime + 100); + + this._ref.overlayRef.detachBackdrop(); + }); + + this._result = result; + this.containerInstance.exit(); + this.containerInstance = null!; } /** Gets an observable that is notified when the bottom sheet is finished closing. */ afterDismissed(): Observable { - return this._afterDismissed; + return this._ref.closed; } /** Gets an observable that is notified when the bottom sheet has opened and appeared. */ @@ -132,13 +135,13 @@ export class MatBottomSheetRef { * Gets an observable that emits when the overlay's backdrop has been clicked. */ backdropClick(): Observable { - return this._overlayRef.backdropClick(); + return this._ref.backdropClick; } /** * Gets an observable that emits when keydown events are targeted on the overlay. */ keydownEvents(): Observable { - return this._overlayRef.keydownEvents(); + return this._ref.keydownEvents; } } diff --git a/src/material/bottom-sheet/bottom-sheet.ts b/src/material/bottom-sheet/bottom-sheet.ts index 2b7033e38496..af410998f155 100644 --- a/src/material/bottom-sheet/bottom-sheet.ts +++ b/src/material/bottom-sheet/bottom-sheet.ts @@ -6,23 +6,19 @@ * found in the LICENSE file at https://angular.io/license */ -import {Directionality} from '@angular/cdk/bidi'; -import {Overlay, OverlayConfig, OverlayRef} from '@angular/cdk/overlay'; -import {ComponentPortal, ComponentType, TemplatePortal} from '@angular/cdk/portal'; +import {Dialog} from '@angular/cdk/dialog'; +import {Overlay} from '@angular/cdk/overlay'; +import {ComponentType} from '@angular/cdk/portal'; import { - ComponentRef, Injectable, - Injector, Optional, SkipSelf, TemplateRef, InjectionToken, Inject, OnDestroy, - StaticProvider, - InjectFlags, + Injector, } from '@angular/core'; -import {of as observableOf} from 'rxjs'; import {MAT_BOTTOM_SHEET_DATA, MatBottomSheetConfig} from './bottom-sheet-config'; import {MatBottomSheetContainer} from './bottom-sheet-container'; import {MatBottomSheetModule} from './bottom-sheet-module'; @@ -39,6 +35,7 @@ export const MAT_BOTTOM_SHEET_DEFAULT_OPTIONS = new InjectionToken | null = null; + private _dialog: Dialog; /** Reference to the currently opened bottom sheet. */ get _openedBottomSheetRef(): MatBottomSheetRef | null { @@ -56,12 +53,14 @@ export class MatBottomSheet implements OnDestroy { constructor( private _overlay: Overlay, - private _injector: Injector, + injector: Injector, @Optional() @SkipSelf() private _parentBottomSheet: MatBottomSheet, @Optional() @Inject(MAT_BOTTOM_SHEET_DEFAULT_OPTIONS) private _defaultOptions?: MatBottomSheetConfig, - ) {} + ) { + this._dialog = injector.get(Dialog); + } /** * Opens a bottom sheet containing the given component. @@ -89,35 +88,31 @@ export class MatBottomSheet implements OnDestroy { componentOrTemplateRef: ComponentType | TemplateRef, config?: MatBottomSheetConfig, ): MatBottomSheetRef { - const _config = _applyConfigDefaults( - this._defaultOptions || new MatBottomSheetConfig(), - config, - ); - const overlayRef = this._createOverlay(_config); - const container = this._attachContainer(overlayRef, _config); - const ref = new MatBottomSheetRef(container, overlayRef); + const _config = {...(this._defaultOptions || new MatBottomSheetConfig()), ...config}; + let ref: MatBottomSheetRef; - if (componentOrTemplateRef instanceof TemplateRef) { - container.attachTemplatePortal( - new TemplatePortal(componentOrTemplateRef, null!, { - $implicit: _config.data, - bottomSheetRef: ref, - } as any), - ); - } else { - const portal = new ComponentPortal( - componentOrTemplateRef, - undefined, - this._createInjector(_config, ref), - ); - const contentRef = container.attachComponentPortal(portal); - ref.instance = contentRef.instance; - } + this._dialog.open(componentOrTemplateRef, { + ..._config, + // Disable closing since we need to sync it up to the animation ourselves. + disableClose: true, + maxWidth: '100%', + container: MatBottomSheetContainer, + scrollStrategy: _config.scrollStrategy || this._overlay.scrollStrategies.block(), + positionStrategy: this._overlay.position().global().centerHorizontally().bottom('0'), + templateContext: () => ({bottomSheetRef: ref}), + providers: (cdkRef, _cdkConfig, container) => { + ref = new MatBottomSheetRef(cdkRef, _config, container as MatBottomSheetContainer); + return [ + {provide: MatBottomSheetRef, useValue: ref}, + {provide: MAT_BOTTOM_SHEET_DATA, useValue: _config.data}, + ]; + }, + }); // When the bottom sheet is dismissed, clear the reference to it. - ref.afterDismissed().subscribe(() => { + ref!.afterDismissed().subscribe(() => { // Clear the bottom sheet ref if it hasn't already been replaced by a newer one. - if (this._openedBottomSheetRef == ref) { + if (this._openedBottomSheetRef === ref) { this._openedBottomSheetRef = null; } }); @@ -125,16 +120,15 @@ export class MatBottomSheet implements OnDestroy { if (this._openedBottomSheetRef) { // If a bottom sheet is already in view, dismiss it and enter the // new bottom sheet after exit animation is complete. - this._openedBottomSheetRef.afterDismissed().subscribe(() => ref.containerInstance.enter()); + this._openedBottomSheetRef.afterDismissed().subscribe(() => ref.containerInstance?.enter()); this._openedBottomSheetRef.dismiss(); } else { // If no bottom sheet is in view, enter the new bottom sheet. - ref.containerInstance.enter(); + ref!.containerInstance.enter(); } - this._openedBottomSheetRef = ref; - - return ref; + this._openedBottomSheetRef = ref!; + return ref!; } /** @@ -152,89 +146,4 @@ export class MatBottomSheet implements OnDestroy { this._bottomSheetRefAtThisLevel.dismiss(); } } - - /** - * Attaches the bottom sheet container component to the overlay. - */ - private _attachContainer( - overlayRef: OverlayRef, - config: MatBottomSheetConfig, - ): MatBottomSheetContainer { - const userInjector = config && config.viewContainerRef && config.viewContainerRef.injector; - const injector = Injector.create({ - parent: userInjector || this._injector, - providers: [{provide: MatBottomSheetConfig, useValue: config}], - }); - - const containerPortal = new ComponentPortal( - MatBottomSheetContainer, - config.viewContainerRef, - injector, - ); - const containerRef: ComponentRef = overlayRef.attach(containerPortal); - return containerRef.instance; - } - - /** - * Creates a new overlay and places it in the correct location. - * @param config The user-specified bottom sheet config. - */ - private _createOverlay(config: MatBottomSheetConfig): OverlayRef { - const overlayConfig = new OverlayConfig({ - direction: config.direction, - hasBackdrop: config.hasBackdrop, - disposeOnNavigation: config.closeOnNavigation, - maxWidth: '100%', - scrollStrategy: config.scrollStrategy || this._overlay.scrollStrategies.block(), - positionStrategy: this._overlay.position().global().centerHorizontally().bottom('0'), - }); - - if (config.backdropClass) { - overlayConfig.backdropClass = config.backdropClass; - } - - return this._overlay.create(overlayConfig); - } - - /** - * Creates an injector to be used inside of a bottom sheet component. - * @param config Config that was used to create the bottom sheet. - * @param bottomSheetRef Reference to the bottom sheet. - */ - private _createInjector( - config: MatBottomSheetConfig, - bottomSheetRef: MatBottomSheetRef, - ): Injector { - const userInjector = config && config.viewContainerRef && config.viewContainerRef.injector; - const providers: StaticProvider[] = [ - {provide: MatBottomSheetRef, useValue: bottomSheetRef}, - {provide: MAT_BOTTOM_SHEET_DATA, useValue: config.data}, - ]; - - if ( - config.direction && - (!userInjector || - !userInjector.get(Directionality, null, InjectFlags.Optional)) - ) { - providers.push({ - provide: Directionality, - useValue: {value: config.direction, change: observableOf()}, - }); - } - - return Injector.create({parent: userInjector || this._injector, providers}); - } -} - -/** - * Applies default options to the bottom sheet config. - * @param defaults Object containing the default values to which to fall back. - * @param config The configuration to which the defaults will be applied. - * @returns The new configuration object with defaults applied. - */ -function _applyConfigDefaults( - defaults: MatBottomSheetConfig, - config?: MatBottomSheetConfig, -): MatBottomSheetConfig { - return {...defaults, ...config}; } diff --git a/tools/public_api_guard/material/bottom-sheet.md b/tools/public_api_guard/material/bottom-sheet.md index 358edadd4e9b..e54e21a37c13 100644 --- a/tools/public_api_guard/material/bottom-sheet.md +++ b/tools/public_api_guard/material/bottom-sheet.md @@ -6,21 +6,19 @@ import { AnimationEvent as AnimationEvent_2 } from '@angular/animations'; import { AnimationTriggerMetadata } from '@angular/animations'; -import { BasePortalOutlet } from '@angular/cdk/portal'; import { BreakpointObserver } from '@angular/cdk/layout'; -import { CdkPortalOutlet } from '@angular/cdk/portal'; +import { CdkDialogContainer } from '@angular/cdk/dialog'; import { ChangeDetectorRef } from '@angular/core'; -import { ComponentPortal } from '@angular/cdk/portal'; -import { ComponentRef } from '@angular/core'; import { ComponentType } from '@angular/cdk/portal'; +import { DialogConfig } from '@angular/cdk/dialog'; +import { DialogRef } from '@angular/cdk/dialog'; import { Direction } from '@angular/cdk/bidi'; -import { DomPortal } from '@angular/cdk/portal'; import { ElementRef } from '@angular/core'; -import { EmbeddedViewRef } from '@angular/core'; import { EventEmitter } from '@angular/core'; +import { FocusMonitor } from '@angular/cdk/a11y'; import { FocusTrapFactory } from '@angular/cdk/a11y'; import * as i0 from '@angular/core'; -import * as i2 from '@angular/cdk/overlay'; +import * as i2 from '@angular/cdk/dialog'; import * as i3 from '@angular/material/core'; import * as i4 from '@angular/cdk/portal'; import { InjectionToken } from '@angular/core'; @@ -32,7 +30,6 @@ import { OnDestroy } from '@angular/core'; import { Overlay } from '@angular/cdk/overlay'; import { OverlayRef } from '@angular/cdk/overlay'; import { ScrollStrategy } from '@angular/cdk/overlay'; -import { TemplatePortal } from '@angular/cdk/portal'; import { TemplateRef } from '@angular/core'; import { ViewContainerRef } from '@angular/core'; @@ -47,7 +44,7 @@ export const MAT_BOTTOM_SHEET_DEFAULT_OPTIONS: InjectionToken | undefined); + constructor(_overlay: Overlay, injector: Injector, _parentBottomSheet: MatBottomSheet, _defaultOptions?: MatBottomSheetConfig | undefined); dismiss(result?: R): void; // (undocumented) ngOnDestroy(): void; @@ -83,16 +80,12 @@ export class MatBottomSheetConfig { } // @public -export class MatBottomSheetContainer extends BasePortalOutlet implements OnDestroy { - constructor(_elementRef: ElementRef, _changeDetectorRef: ChangeDetectorRef, _focusTrapFactory: FocusTrapFactory, _interactivityChecker: InteractivityChecker, _ngZone: NgZone, breakpointObserver: BreakpointObserver, document: any, - bottomSheetConfig: MatBottomSheetConfig); +export class MatBottomSheetContainer extends CdkDialogContainer implements OnDestroy { + constructor(elementRef: ElementRef, focusTrapFactory: FocusTrapFactory, document: any, config: DialogConfig, checker: InteractivityChecker, ngZone: NgZone, overlayRef: OverlayRef, breakpointObserver: BreakpointObserver, _changeDetectorRef: ChangeDetectorRef, focusMonitor?: FocusMonitor); _animationState: 'void' | 'visible' | 'hidden'; _animationStateChanged: EventEmitter; - attachComponentPortal(portal: ComponentPortal): ComponentRef; - // @deprecated - attachDomPortal: (portal: DomPortal) => void; - attachTemplatePortal(portal: TemplatePortal): EmbeddedViewRef; - bottomSheetConfig: MatBottomSheetConfig; + // (undocumented) + protected _captureInitialFocus(): void; enter(): void; exit(): void; // (undocumented) @@ -101,11 +94,10 @@ export class MatBottomSheetContainer extends BasePortalOutlet implements OnDestr _onAnimationDone(event: AnimationEvent_2): void; // (undocumented) _onAnimationStart(event: AnimationEvent_2): void; - _portalOutlet: CdkPortalOutlet; // (undocumented) static ɵcmp: i0.ɵɵComponentDeclaration; // (undocumented) - static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵfac: i0.ɵɵFactoryDeclaration; } // @public (undocumented) @@ -115,19 +107,19 @@ export class MatBottomSheetModule { // (undocumented) static ɵinj: i0.ɵɵInjectorDeclaration; // (undocumented) - static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; } // @public export class MatBottomSheetRef { - constructor(containerInstance: MatBottomSheetContainer, _overlayRef: OverlayRef); + constructor(_ref: DialogRef, config: MatBottomSheetConfig, containerInstance: MatBottomSheetContainer); afterDismissed(): Observable; afterOpened(): Observable; backdropClick(): Observable; containerInstance: MatBottomSheetContainer; disableClose: boolean | undefined; dismiss(result?: R): void; - instance: T; + get instance(): T; keydownEvents(): Observable; }