import * as i0 from '@angular/core';
import { InjectionToken, Directive, inject, Component, ViewEncapsulation, ChangeDetectionStrategy, NgZone, ElementRef, ChangeDetectorRef, ANIMATION_MODULE_TYPE, afterRender, ViewChild, Injector, TemplateRef, Injectable, NgModule } from '@angular/core';
import { Subject, of } from 'rxjs';
import { MatButton, MatButtonModule } from './button.mjs';
import { DOCUMENT } from '@angular/common';
import { BasePortalOutlet, CdkPortalOutlet, ComponentPortal, TemplatePortal, PortalModule } from '@angular/cdk/portal';
import { _IdGenerator, LiveAnnouncer } from '@angular/cdk/a11y';
import { Platform } from '@angular/cdk/platform';
import { take, takeUntil } from 'rxjs/operators';
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
import { Overlay, OverlayConfig, OverlayModule } from '@angular/cdk/overlay';
import { M as MatCommonModule } from './common-module-WayjW0Pb.mjs';
import './icon-button-D1J0zeqv.mjs';
import '@angular/cdk/private';
import './ripple-loader-Ce3DAhPW.mjs';
import './ripple-BT3tzh6F.mjs';
import '@angular/cdk/coercion';
import './structural-styles-BQUT6wsL.mjs';
import './index-SYVYjXwK.mjs';
import '@angular/cdk/bidi';
/** Maximum amount of milliseconds that can be passed into setTimeout. */
const MAX_TIMEOUT = Math.pow(2, 31) - 1;
/**
* Reference to a snack bar dispatched from the snack bar service.
*/
class MatSnackBarRef {
_overlayRef;
/** The instance of the component making up the content of the snack bar. */
instance;
/**
* The instance of the component making up the content of the snack bar.
* @docs-private
*/
containerInstance;
/** Subject for notifying the user that the snack bar has been dismissed. */
_afterDismissed = new Subject();
/** Subject for notifying the user that the snack bar has opened and appeared. */
_afterOpened = new Subject();
/** Subject for notifying the user that the snack bar action was called. */
_onAction = new Subject();
/**
* Timeout ID for the duration setTimeout call. Used to clear the timeout if the snackbar is
* dismissed before the duration passes.
*/
_durationTimeoutId;
/** Whether the snack bar was dismissed using the action button. */
_dismissedByAction = false;
constructor(containerInstance, _overlayRef) {
this._overlayRef = _overlayRef;
this.containerInstance = containerInstance;
containerInstance._onExit.subscribe(() => this._finishDismiss());
}
/** Dismisses the snack bar. */
dismiss() {
if (!this._afterDismissed.closed) {
this.containerInstance.exit();
}
clearTimeout(this._durationTimeoutId);
}
/** Marks the snackbar action clicked. */
dismissWithAction() {
if (!this._onAction.closed) {
this._dismissedByAction = true;
this._onAction.next();
this._onAction.complete();
this.dismiss();
}
clearTimeout(this._durationTimeoutId);
}
/**
* Marks the snackbar action clicked.
* @deprecated Use `dismissWithAction` instead.
* @breaking-change 8.0.0
*/
closeWithAction() {
this.dismissWithAction();
}
/** Dismisses the snack bar after some duration */
_dismissAfter(duration) {
// Note that we need to cap the duration to the maximum value for setTimeout, because
// it'll revert to 1 if somebody passes in something greater (e.g. `Infinity`). See #17234.
this._durationTimeoutId = setTimeout(() => this.dismiss(), Math.min(duration, MAX_TIMEOUT));
}
/** Marks the snackbar as opened */
_open() {
if (!this._afterOpened.closed) {
this._afterOpened.next();
this._afterOpened.complete();
}
}
/** Cleans up the DOM after closing. */
_finishDismiss() {
this._overlayRef.dispose();
if (!this._onAction.closed) {
this._onAction.complete();
}
this._afterDismissed.next({ dismissedByAction: this._dismissedByAction });
this._afterDismissed.complete();
this._dismissedByAction = false;
}
/** Gets an observable that is notified when the snack bar is finished closing. */
afterDismissed() {
return this._afterDismissed;
}
/** Gets an observable that is notified when the snack bar has opened and appeared. */
afterOpened() {
return this.containerInstance._onEnter;
}
/** Gets an observable that is notified when the snack bar action is called. */
onAction() {
return this._onAction;
}
}
/** Injection token that can be used to access the data that was passed in to a snack bar. */
const MAT_SNACK_BAR_DATA = new InjectionToken('MatSnackBarData');
/**
* Configuration used when opening a snack-bar.
*/
class MatSnackBarConfig {
/** The politeness level for the MatAriaLiveAnnouncer announcement. */
politeness = 'assertive';
/**
* Message to be announced by the LiveAnnouncer. When opening a snackbar without a custom
* component or template, the announcement message will default to the specified message.
*/
announcementMessage = '';
/**
* The view container that serves as the parent for the snackbar for the purposes of dependency
* injection. Note: this does not affect where the snackbar is inserted in the DOM.
*/
viewContainerRef;
/** The length of time in milliseconds to wait before automatically dismissing the snack bar. */
duration = 0;
/** Extra CSS classes to be added to the snack bar container. */
panelClass;
/** Text layout direction for the snack bar. */
direction;
/** Data being injected into the child component. */
data = null;
/** The horizontal position to place the snack bar. */
horizontalPosition = 'center';
/** The vertical position to place the snack bar. */
verticalPosition = 'bottom';
}
/** Directive that should be applied to the text element to be rendered in the snack bar. */
class MatSnackBarLabel {
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: MatSnackBarLabel, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.6", type: MatSnackBarLabel, isStandalone: true, selector: "[matSnackBarLabel]", host: { classAttribute: "mat-mdc-snack-bar-label mdc-snackbar__label" }, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: MatSnackBarLabel, decorators: [{
type: Directive,
args: [{
selector: `[matSnackBarLabel]`,
host: {
'class': 'mat-mdc-snack-bar-label mdc-snackbar__label',
},
}]
}] });
/** Directive that should be applied to the element containing the snack bar's action buttons. */
class MatSnackBarActions {
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: MatSnackBarActions, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.6", type: MatSnackBarActions, isStandalone: true, selector: "[matSnackBarActions]", host: { classAttribute: "mat-mdc-snack-bar-actions mdc-snackbar__actions" }, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: MatSnackBarActions, decorators: [{
type: Directive,
args: [{
selector: `[matSnackBarActions]`,
host: {
'class': 'mat-mdc-snack-bar-actions mdc-snackbar__actions',
},
}]
}] });
/** Directive that should be applied to each of the snack bar's action buttons. */
class MatSnackBarAction {
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: MatSnackBarAction, deps: [], target: i0.ɵɵFactoryTarget.Directive });
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.6", type: MatSnackBarAction, isStandalone: true, selector: "[matSnackBarAction]", host: { classAttribute: "mat-mdc-snack-bar-action mdc-snackbar__action" }, ngImport: i0 });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: MatSnackBarAction, decorators: [{
type: Directive,
args: [{
selector: `[matSnackBarAction]`,
host: {
'class': 'mat-mdc-snack-bar-action mdc-snackbar__action',
},
}]
}] });
class SimpleSnackBar {
snackBarRef = inject(MatSnackBarRef);
data = inject(MAT_SNACK_BAR_DATA);
constructor() { }
/** Performs the action on the snack bar. */
action() {
this.snackBarRef.dismissWithAction();
}
/** If the action button should be shown. */
get hasAction() {
return !!this.data.action;
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: SimpleSnackBar, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.6", type: SimpleSnackBar, isStandalone: true, selector: "simple-snack-bar", host: { classAttribute: "mat-mdc-simple-snack-bar" }, exportAs: ["matSnackBar"], ngImport: i0, template: "
\n}\n", styles: [".mat-mdc-simple-snack-bar{display:flex}\n"] }]
}], ctorParameters: () => [] });
const ENTER_ANIMATION = '_mat-snack-bar-enter';
const EXIT_ANIMATION = '_mat-snack-bar-exit';
/**
* Internal component that wraps user-provided snack bar content.
* @docs-private
*/
class MatSnackBarContainer extends BasePortalOutlet {
_ngZone = inject(NgZone);
_elementRef = inject(ElementRef);
_changeDetectorRef = inject(ChangeDetectorRef);
_platform = inject(Platform);
_rendersRef;
_animationsDisabled = inject(ANIMATION_MODULE_TYPE, { optional: true }) === 'NoopAnimations';
snackBarConfig = inject(MatSnackBarConfig);
_document = inject(DOCUMENT);
_trackedModals = new Set();
_enterFallback;
_exitFallback;
_renders = new Subject();
/** The number of milliseconds to wait before announcing the snack bar's content. */
_announceDelay = 150;
/** The timeout for announcing the snack bar's content. */
_announceTimeoutId;
/** Whether the component has been destroyed. */
_destroyed = false;
/** The portal outlet inside of this container into which the snack bar content will be loaded. */
_portalOutlet;
/** Subject for notifying that the snack bar has announced to screen readers. */
_onAnnounce = new Subject();
/** Subject for notifying that the snack bar has exited from view. */
_onExit = new Subject();
/** Subject for notifying that the snack bar has finished entering the view. */
_onEnter = new Subject();
/** The state of the snack bar animations. */
_animationState = 'void';
/** aria-live value for the live region. */
_live;
/**
* Element that will have the `mdc-snackbar__label` class applied if the attached component
* or template does not have it. This ensures that the appropriate structure, typography, and
* color is applied to the attached view.
*/
_label;
/**
* Role of the live region. This is only for Firefox as there is a known issue where Firefox +
* JAWS does not read out aria-live message.
*/
_role;
/** Unique ID of the aria-live element. */
_liveElementId = inject(_IdGenerator).getId('mat-snack-bar-container-live-');
constructor() {
super();
const config = this.snackBarConfig;
// Use aria-live rather than a live role like 'alert' or 'status'
// because NVDA and JAWS have show inconsistent behavior with live roles.
if (config.politeness === 'assertive' && !config.announcementMessage) {
this._live = 'assertive';
}
else if (config.politeness === 'off') {
this._live = 'off';
}
else {
this._live = 'polite';
}
// Only set role for Firefox. Set role based on aria-live because setting role="alert" implies
// aria-live="assertive" which may cause issues if aria-live is set to "polite" above.
if (this._platform.FIREFOX) {
if (this._live === 'polite') {
this._role = 'status';
}
if (this._live === 'assertive') {
this._role = 'alert';
}
}
// Note: ideally we'd just do an `afterNextRender` in the places where we need to delay
// something, however in some cases (TestBed teardown) the injector can be destroyed at an
// unexpected time, causing the `afterRender` to fail.
this._rendersRef = afterRender(() => this._renders.next(), { manualCleanup: true });
}
/** Attach a component portal as content to this snack bar container. */
attachComponentPortal(portal) {
this._assertNotAttached();
const result = this._portalOutlet.attachComponentPortal(portal);
this._afterPortalAttached();
return result;
}
/** Attach a template portal as content to this snack bar container. */
attachTemplatePortal(portal) {
this._assertNotAttached();
const result = this._portalOutlet.attachTemplatePortal(portal);
this._afterPortalAttached();
return result;
}
/**
* Attaches a DOM portal to the snack bar container.
* @deprecated To be turned into a method.
* @breaking-change 10.0.0
*/
attachDomPortal = (portal) => {
this._assertNotAttached();
const result = this._portalOutlet.attachDomPortal(portal);
this._afterPortalAttached();
return result;
};
/** Handle end of animations, updating the state of the snackbar. */
onAnimationEnd(animationName) {
if (animationName === EXIT_ANIMATION) {
this._completeExit();
}
else if (animationName === ENTER_ANIMATION) {
clearTimeout(this._enterFallback);
this._ngZone.run(() => {
this._onEnter.next();
this._onEnter.complete();
});
}
}
/** Begin animation of snack bar entrance into view. */
enter() {
if (!this._destroyed) {
this._animationState = 'visible';
// _animationState lives in host bindings and `detectChanges` does not refresh host bindings
// so we have to call `markForCheck` to ensure the host view is refreshed eventually.
this._changeDetectorRef.markForCheck();
this._changeDetectorRef.detectChanges();
this._screenReaderAnnounce();
if (this._animationsDisabled) {
this._renders.pipe(take(1)).subscribe(() => {
this._ngZone.run(() => queueMicrotask(() => this.onAnimationEnd(ENTER_ANIMATION)));
});
}
else {
clearTimeout(this._enterFallback);
this._enterFallback = setTimeout(() => {
// The snack bar will stay invisible if it fails to animate. Add a fallback class so it
// becomes visible. This can happen in some apps that do `* {animation: none !important}`.
this._elementRef.nativeElement.classList.add('mat-snack-bar-fallback-visible');
this.onAnimationEnd(ENTER_ANIMATION);
}, 200);
}
}
}
/** Begin animation of the snack bar exiting from view. */
exit() {
if (this._destroyed) {
return of(undefined);
}
// It's common for snack bars to be opened by random outside calls like HTTP requests or
// errors. Run inside the NgZone to ensure that it functions correctly.
this._ngZone.run(() => {
// Note: this one transitions to `hidden`, rather than `void`, in order to handle the case
// where multiple snack bars are opened in quick succession (e.g. two consecutive calls to
// `MatSnackBar.open`).
this._animationState = 'hidden';
this._changeDetectorRef.markForCheck();
// Mark this element with an 'exit' attribute to indicate that the snackbar has
// been dismissed and will soon be removed from the DOM. This is used by the snackbar
// test harness.
this._elementRef.nativeElement.setAttribute('mat-exit', '');
// If the snack bar hasn't been announced by the time it exits it wouldn't have been open
// long enough to visually read it either, so clear the timeout for announcing.
clearTimeout(this._announceTimeoutId);
if (this._animationsDisabled) {
this._renders.pipe(take(1)).subscribe(() => {
this._ngZone.run(() => queueMicrotask(() => this.onAnimationEnd(EXIT_ANIMATION)));
});
}
else {
clearTimeout(this._exitFallback);
this._exitFallback = setTimeout(() => this.onAnimationEnd(EXIT_ANIMATION), 200);
}
});
return this._onExit;
}
/** Makes sure the exit callbacks have been invoked when the element is destroyed. */
ngOnDestroy() {
this._destroyed = true;
this._clearFromModals();
this._completeExit();
this._renders.complete();
this._rendersRef.destroy();
}
_completeExit() {
clearTimeout(this._exitFallback);
queueMicrotask(() => {
this._onExit.next();
this._onExit.complete();
});
}
/**
* Called after the portal contents have been attached. Can be
* used to modify the DOM once it's guaranteed to be in place.
*/
_afterPortalAttached() {
const element = this._elementRef.nativeElement;
const panelClasses = this.snackBarConfig.panelClass;
if (panelClasses) {
if (Array.isArray(panelClasses)) {
// Note that we can't use a spread here, because IE doesn't support multiple arguments.
panelClasses.forEach(cssClass => element.classList.add(cssClass));
}
else {
element.classList.add(panelClasses);
}
}
this._exposeToModals();
// Check to see if the attached component or template uses the MDC template structure,
// specifically the MDC label. If not, the container should apply the MDC label class to this
// component's label container, which will apply MDC's label styles to the attached view.
const label = this._label.nativeElement;
const labelClass = 'mdc-snackbar__label';
label.classList.toggle(labelClass, !label.querySelector(`.${labelClass}`));
}
/**
* Some browsers won't expose the accessibility node of the live element if there is an
* `aria-modal` and the live element is outside of it. This method works around the issue by
* pointing the `aria-owns` of all modals to the live element.
*/
_exposeToModals() {
// TODO(http://github.com/angular/components/issues/26853): consider de-duplicating this with the
// `LiveAnnouncer` and any other usages.
//
// Note that the selector here is limited to CDK overlays at the moment in order to reduce the
// section of the DOM we need to look through. This should cover all the cases we support, but
// the selector can be expanded if it turns out to be too narrow.
const id = this._liveElementId;
const modals = this._document.querySelectorAll('body > .cdk-overlay-container [aria-modal="true"]');
for (let i = 0; i < modals.length; i++) {
const modal = modals[i];
const ariaOwns = modal.getAttribute('aria-owns');
this._trackedModals.add(modal);
if (!ariaOwns) {
modal.setAttribute('aria-owns', id);
}
else if (ariaOwns.indexOf(id) === -1) {
modal.setAttribute('aria-owns', ariaOwns + ' ' + id);
}
}
}
/** Clears the references to the live element from any modals it was added to. */
_clearFromModals() {
this._trackedModals.forEach(modal => {
const ariaOwns = modal.getAttribute('aria-owns');
if (ariaOwns) {
const newValue = ariaOwns.replace(this._liveElementId, '').trim();
if (newValue.length > 0) {
modal.setAttribute('aria-owns', newValue);
}
else {
modal.removeAttribute('aria-owns');
}
}
});
this._trackedModals.clear();
}
/** Asserts that no content is already attached to the container. */
_assertNotAttached() {
if (this._portalOutlet.hasAttached() && (typeof ngDevMode === 'undefined' || ngDevMode)) {
throw Error('Attempting to attach snack bar content after content is already attached');
}
}
/**
* Starts a timeout to move the snack bar content to the live region so screen readers will
* announce it.
*/
_screenReaderAnnounce() {
if (this._announceTimeoutId) {
return;
}
this._ngZone.runOutsideAngular(() => {
this._announceTimeoutId = setTimeout(() => {
if (this._destroyed) {
return;
}
const element = this._elementRef.nativeElement;
const inertElement = element.querySelector('[aria-hidden]');
const liveElement = element.querySelector('[aria-live]');
if (inertElement && liveElement) {
// If an element in the snack bar content is focused before being moved
// track it and restore focus after moving to the live region.
let focusedElement = null;
if (this._platform.isBrowser &&
document.activeElement instanceof HTMLElement &&
inertElement.contains(document.activeElement)) {
focusedElement = document.activeElement;
}
inertElement.removeAttribute('aria-hidden');
liveElement.appendChild(inertElement);
focusedElement?.focus();
this._onAnnounce.next();
this._onAnnounce.complete();
}
}, this._announceDelay);
});
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.6", ngImport: i0, type: MatSnackBarContainer, deps: [], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.6", type: MatSnackBarContainer, isStandalone: true, selector: "mat-snack-bar-container", host: { listeners: { "animationend": "onAnimationEnd($event.animationName)", "animationcancel": "onAnimationEnd($event.animationName)" }, properties: { "class.mat-snack-bar-container-enter": "_animationState === \"visible\"", "class.mat-snack-bar-container-exit": "_animationState === \"hidden\"", "class.mat-snack-bar-container-animations-enabled": "!_animationsDisabled" }, classAttribute: "mdc-snackbar mat-mdc-snack-bar-container" }, viewQueries: [{ propertyName: "_portalOutlet", first: true, predicate: CdkPortalOutlet, descendants: true, static: true }, { propertyName: "_label", first: true, predicate: ["label"], descendants: true, static: true }], usesInheritance: true, ngImport: i0, template: "