@almoamendev/ngx-md3 0.7.5 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -8,12 +8,12 @@ import * as i1 from '@angular/router';
8
8
  import { Router, NavigationEnd, RouterLinkActive } from '@angular/router';
9
9
  import { CdkPortalOutlet, ComponentPortal } from '@angular/cdk/portal';
10
10
  import { CdkDialogContainer, Dialog as Dialog$1 } from '@angular/cdk/dialog';
11
- import { Overlay, OverlayContainer, OverlayConfig } from '@angular/cdk/overlay';
11
+ import { Overlay, OverlayConfig, OverlayContainer } from '@angular/cdk/overlay';
12
12
  import { hasModifierKey } from '@angular/cdk/keycodes';
13
13
  import * as i2 from '@angular/cdk/scrolling';
14
14
  import { CdkScrollable } from '@angular/cdk/scrolling';
15
15
  import { Directionality } from '@angular/cdk/bidi';
16
- import { FormControlName } from '@angular/forms';
16
+ import { FormControlName, NgControl } from '@angular/forms';
17
17
 
18
18
  class TypeDisplay {
19
19
  el;
@@ -4353,7 +4353,6 @@ class Switch {
4353
4353
  inputDisabledState = signal(false, /* @ts-ignore */
4354
4354
  ...(ngDevMode ? [{ debugName: "inputDisabledState" }] : /* istanbul ignore next */ []));
4355
4355
  isDisabled = computed(() => {
4356
- console.log('Switch disabled');
4357
4356
  return this.inputDisabledState();
4358
4357
  }, /* @ts-ignore */
4359
4358
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
@@ -6019,7 +6018,7 @@ class TextField {
6019
6018
  provide: MD3_BUTTON_CONTEXT,
6020
6019
  useExisting: TextField,
6021
6020
  },
6022
- ], queries: [{ propertyName: "input", first: true, predicate: InputElement, descendants: true, isSignal: true }, { propertyName: "iconElements", predicate: IconElement, descendants: true, isSignal: true }, { propertyName: "iconButtons", predicate: IconButton, descendants: true, isSignal: true }, { propertyName: "controlName", first: true, predicate: FormControlName, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "inputContainer", first: true, predicate: ["inputContainer"], descendants: true, isSignal: true }], ngImport: i0, template: "<label #inputContainer class=\"md3-text-field-container\" [ngClass]=\"{\n 'md3-text-field-filled': fieldType() == 'filled',\n 'md3-text-field-outlined': fieldType() == 'outlined',\n}\">\n <ng-content></ng-content>\n <fieldset aria-hidden=\"true\" class=\"md3-text-field-outline\">\n @if (label() && fieldType() == 'outlined') {\n <legend class=\"md3-label-gap\">{{ label() }}</legend>\n }\n </fieldset>\n @if (label()) {\n <label class=\"md3-text-field-label\">{{ label() }}</label>\n }\n <ng-content select=\"[md3-icon-element=leading]\"></ng-content>\n <ng-content select=\"[md3-icon-button], [md3-icon-element=trailing]\"></ng-content>\n @if (fieldType() != 'outlined') {\n <div class=\"md3-text-field-state\"></div>\n }\n</label>\n<div class=\"md3-supporting-text-container\" [class.md3-has-error]=\"inputError()\">\n <ng-content select=\"[md3-supporting-text]\"></ng-content>\n @if (inputCounter()) {\n <div class=\"md3-text-input-counter\">{{ valueLength() + (maxLength() ? \"/\" + maxLength() : \"\") }}</div>\n }\n</div>\n", styles: [":host{--background-color: rgb(var(--md-scheme-surface-container-highest));--foreground-color: rgb(var(--md-scheme-on-surface));--label-color: rgb(var(--md-scheme-on-surface-variant));--caret-color: rgb(var(--md-scheme-primary));--outline-color: rgb(var(--md-scheme-on-surface-variant));font-size:1rem;width:100%}:host .md3-text-field-container{display:block;position:relative;border-radius:var(--md-border-radius-xsmall) var(--md-border-radius-xsmall) 0 0;overflow:hidden}:host .md3-text-field-container ::ng-deep [md3-icon-element=leading]{position:absolute;font-size:1.5em!important;height:1em!important;width:1em!important;top:.666667em;inset-inline-start:.5em;pointer-events:none;color:var(--label-color)}:host .md3-text-field-container ::ng-deep [md3-icon-element=trailing]{position:absolute;font-size:1.5em!important;height:1em!important;width:1em!important;top:.666667em;inset-inline-end:.5em;pointer-events:none;color:var(--label-color)}:host .md3-text-field-container ::ng-deep [md3-icon-button]{position:absolute;font-size:1em!important;top:.5em;inset-inline-end:.5em}:host .md3-text-field-container .md3-text-field-outline{position:absolute;inset:0;pointer-events:none;padding:0 .625em;margin:.0625em 0 0;border:none;border-bottom:.0625em solid var(--outline-color);transition:all var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing),border-width none}:host .md3-text-field-container .md3-text-field-outline .md3-label-gap{padding:0;max-width:0em;font-size:.75em;font-weight:400;line-height:1.333332em;letter-spacing:.041667em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;opacity:0;visibility:hidden;transition:all var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing);interpolate-size:allow-keywords}:host .md3-text-field-container .md3-text-field-label{position:absolute;top:1em;inset-inline-start:1em;inset-inline-end:1em;padding:0;font-size:1em;font-weight:400;line-height:1.5em;letter-spacing:.03125em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;pointer-events:none;color:var(--label-color);transform-origin:top left;transform:scale(1);transition:all var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing)}:host .md3-text-field-container .md3-text-field-label:dir(rtl){transform-origin:top right}:host .md3-text-field-container .md3-text-field-state{position:absolute;inset:0;pointer-events:none;border-radius:inherit;background-color:rgba(var(--md-scheme-on-surface));opacity:0;transition:all var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing)}:host .md3-text-field-container ::ng-deep .md3-input-element{display:block;width:100%;border:none;outline:none;padding:1.5em 1em .5em;font-size:1em;font-weight:400;line-height:1.5em;letter-spacing:.03125em;resize:none;background-color:var(--background-color);color:var(--foreground-color);caret-color:var(--caret-color)}:host .md3-text-field-container ::ng-deep .md3-input-element:not(:placeholder-shown)~.md3-text-field-label{top:.4375em;transform:scale(.75)}:host .md3-text-field-container ::ng-deep .md3-input-element:hover~.md3-text-field-state{opacity:.08}:host .md3-text-field-container ::ng-deep .md3-input-element:hover~.md3-text-field-outline{--outline-color: rgb(var(--md-scheme-on-surface))}:host .md3-text-field-container ::ng-deep .md3-input-element:focus~.md3-text-field-outline{--outline-color: rgb(var(--md-scheme-primary));border-bottom-width:.1875em}:host .md3-text-field-container ::ng-deep .md3-input-element:focus~.md3-text-field-label{color:var(--caret-color);top:.4375em;transform:scale(.75)}:host .md3-text-field-container ::ng-deep .md3-input-element:disabled{--background-color: rgba(var(--md-scheme-surface-container), .4);--foreground-color: rgba(var(--md-scheme-on-surface), .38)}:host .md3-text-field-container ::ng-deep .md3-input-element:disabled~.md3-text-field-state{display:none}:host .md3-text-field-container ::ng-deep .md3-input-element:disabled~.md3-text-field-outline{border-bottom:.0625em solid rgba(var(--md-scheme-on-surface),.38)}:host .md3-text-field-container ::ng-deep .md3-input-element:disabled~.md3-text-field-label{color:rgba(var(--md-scheme-on-surface),.38)}:host .md3-text-field-container ::ng-deep .md3-input-element:disabled~[md3-icon-element]{color:rgba(var(--md-scheme-on-surface),.38)}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]{caret-color:rgb(var(--md-scheme-error))}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]~.md3-text-field-outline{--outline-color: rgb(var(--md-scheme-error))}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]~.md3-text-field-label{color:rgb(var(--md-scheme-error))}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]~[md3-icon-element=trailing]{color:rgb(var(--md-scheme-error))}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]~[md3-icon-button].md3-filled{--background-color: rgb(var(--md-scheme-error));--foreground-color: rgb(var(--md-scheme-on-error));--state-color: var(--md-scheme-on-error)}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]~[md3-icon-button].md3-tonal{--background-color: rgb(var(--md-scheme-error-container));--foreground-color: rgb(var(--md-scheme-on-error-container));--state-color: var(--md-scheme-on-error-container)}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]~[md3-icon-button].md3-outlined{--background-color: transparent;--foreground-color: rgb(var(--md-scheme-error));--state-color: var(--md-scheme-error);border-color:var(--md-scheme-error)}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]~[md3-icon-button].md3-standard{--background-color: transparent;--foreground-color: rgb(var(--md-scheme-error));--state-color: var(--md-scheme-error)}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]:hover~.md3-text-field-outline{--outline-color: rgb(var(--md-scheme-on-error-container))}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]:hover~.md3-text-field-label{color:rgb(var(--md-scheme-on-error-container))}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]:hover~[md3-icon-element=trailing]{color:rgb(var(--md-scheme-on-error-container))}:host .md3-text-field-container.md3-has-leading-icon ::ng-deep .md3-input-element{padding-inline-start:3.25em}:host .md3-text-field-container.md3-has-leading-icon .md3-text-field-label{inset-inline-start:3.25em}:host .md3-text-field-container.md3-has-trailing-icon ::ng-deep .md3-input-element{padding-inline-end:3.25em}:host .md3-text-field-container.md3-has-trailing-icon .md3-text-field-label{inset-inline-end:3.25em}:host .md3-text-field-container.md3-text-field-outlined{--background-color: transparent;border-radius:var(--md-border-radius-xsmall);padding-top:.5em}:host .md3-text-field-container.md3-text-field-outlined .md3-text-field-outline{border-radius:inherit;border:.0625em solid var(--outline-color)}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep [md3-icon-element=leading],:host .md3-text-field-container.md3-text-field-outlined ::ng-deep [md3-icon-element=trailing],:host .md3-text-field-container.md3-text-field-outlined ::ng-deep [md3-icon-button]{top:1em}:host .md3-text-field-container.md3-text-field-outlined .md3-text-field-label{top:1.5em}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element{padding-block:1em}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:focus~.md3-text-field-outline{padding:0 .5em;border-width:.1875em}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:focus~.md3-text-field-outline .md3-label-gap,:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:not(:placeholder-shown)~.md3-text-field-outline .md3-label-gap{padding:0 .375em;max-width:fit-content}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:focus~.md3-text-field-label,:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:not(:placeholder-shown)~.md3-text-field-label{top:0;inset-inline:1em}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:disabled{--foreground-color: rgba(var(--md-scheme-on-surface), .38)}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:disabled~.md3-text-field-outline{border:.0625em solid rgba(var(--md-scheme-on-surface),.38)}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:disabled~.md3-text-field-label{color:rgba(var(--md-scheme-on-surface),.38)}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:disabled~[md3-icon-element]{color:rgba(var(--md-scheme-on-surface),.38)}:host .md3-supporting-text-container{display:flex;flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1.333332em;padding:0 1.333332em;font-size:.75em;font-weight:400;line-height:1.333332em;letter-spacing:.033332em;color:var(--label-color)}:host .md3-supporting-text-container.md3-has-error{color:rgb(var(--md-scheme-error))}:host .md3-supporting-text-container .md3-text-input-counter{margin-inline-start:auto}:host .md3-supporting-text-container>::ng-deep *{margin-top:.333332em}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
6021
+ ], queries: [{ propertyName: "input", first: true, predicate: InputElement, descendants: true, isSignal: true }, { propertyName: "iconElements", predicate: IconElement, descendants: true, isSignal: true }, { propertyName: "iconButtons", predicate: IconButton, descendants: true, isSignal: true }, { propertyName: "controlName", first: true, predicate: FormControlName, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "inputContainer", first: true, predicate: ["inputContainer"], descendants: true, isSignal: true }], ngImport: i0, template: "<label #inputContainer class=\"md3-text-field-container\" [ngClass]=\"{\n 'md3-text-field-filled': fieldType() == 'filled',\n 'md3-text-field-outlined': fieldType() == 'outlined',\n}\">\n <ng-content></ng-content>\n <fieldset aria-hidden=\"true\" class=\"md3-text-field-outline\">\n @if (label() && fieldType() == 'outlined') {\n <legend class=\"md3-label-gap\">{{ label() }}</legend>\n }\n </fieldset>\n @if (label()) {\n <span class=\"md3-text-field-label\">{{ label() }}</span>\n }\n <ng-content select=\"[md3-icon-element=leading]\"></ng-content>\n <ng-content select=\"[md3-icon-button], [md3-icon-element=trailing]\"></ng-content>\n @if (fieldType() != 'outlined') {\n <div class=\"md3-text-field-state\"></div>\n }\n</label>\n<div class=\"md3-supporting-text-container\" [class.md3-has-error]=\"inputError()\">\n <ng-content select=\"[md3-supporting-text]\"></ng-content>\n @if (inputCounter()) {\n <div class=\"md3-text-input-counter\">{{ valueLength() + (maxLength() ? \"/\" + maxLength() : \"\") }}</div>\n }\n</div>\n", styles: [":host{--background-color: rgb(var(--md-scheme-surface-container-highest));--foreground-color: rgb(var(--md-scheme-on-surface));--label-color: rgb(var(--md-scheme-on-surface-variant));--caret-color: rgb(var(--md-scheme-primary));--outline-color: rgb(var(--md-scheme-on-surface-variant));font-size:1rem;width:100%}:host .md3-text-field-container{display:block;position:relative;border-radius:var(--md-border-radius-xsmall) var(--md-border-radius-xsmall) 0 0;overflow:hidden}:host .md3-text-field-container>::ng-deep [md3-icon-element=leading]{position:absolute;font-size:1.5em!important;height:1em!important;width:1em!important;top:.666667em;inset-inline-start:.5em;pointer-events:none;color:var(--label-color)}:host .md3-text-field-container>::ng-deep [md3-icon-element=trailing]{position:absolute;font-size:1.5em!important;height:1em!important;width:1em!important;top:.666667em;inset-inline-end:.5em;pointer-events:none;color:var(--label-color)}:host .md3-text-field-container>::ng-deep [md3-icon-button]{position:absolute;font-size:1em!important;top:.5em;inset-inline-end:.5em}:host .md3-text-field-container .md3-text-field-outline{position:absolute;inset:0;pointer-events:none;padding:0 .625em;margin:.0625em 0 0;border:none;border-bottom:.0625em solid var(--outline-color);transition:all var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing),border-width none}:host .md3-text-field-container .md3-text-field-outline .md3-label-gap{padding:0;max-width:0em;font-size:.75em;font-weight:400;line-height:1.333332em;letter-spacing:.041667em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;opacity:0;visibility:hidden;transition:all var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing);interpolate-size:allow-keywords}:host .md3-text-field-container .md3-text-field-label{position:absolute;top:1em;inset-inline-start:1em;inset-inline-end:1em;padding:0;font-size:1em;font-weight:400;line-height:1.5em;letter-spacing:.03125em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;pointer-events:none;color:var(--label-color);transform-origin:top left;transform:scale(1);transition:all var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing)}:host .md3-text-field-container .md3-text-field-label:dir(rtl){transform-origin:top right}:host .md3-text-field-container .md3-text-field-state{position:absolute;inset:0;pointer-events:none;border-radius:inherit;background-color:rgba(var(--md-scheme-on-surface));opacity:0;transition:all var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing)}:host .md3-text-field-container ::ng-deep .md3-input-element{display:block;width:100%;border:none;outline:none;padding:1.5em 1em .5em;font-size:1em;font-weight:400;line-height:1.5em;letter-spacing:.03125em;resize:none;background-color:var(--background-color);color:var(--foreground-color);caret-color:var(--caret-color)}:host .md3-text-field-container ::ng-deep .md3-input-element:not(:placeholder-shown)~.md3-text-field-label{top:.4375em;transform:scale(.75)}:host .md3-text-field-container ::ng-deep .md3-input-element:hover~.md3-text-field-state{opacity:.08}:host .md3-text-field-container ::ng-deep .md3-input-element:hover~.md3-text-field-outline{--outline-color: rgb(var(--md-scheme-on-surface))}:host .md3-text-field-container ::ng-deep .md3-input-element:focus~.md3-text-field-outline{--outline-color: rgb(var(--md-scheme-primary));border-bottom-width:.1875em}:host .md3-text-field-container ::ng-deep .md3-input-element:focus:not(:read-only)~.md3-text-field-label{color:var(--caret-color);top:.4375em;transform:scale(.75)}:host .md3-text-field-container ::ng-deep .md3-input-element:disabled{--background-color: rgba(var(--md-scheme-surface-container), .4);--foreground-color: rgba(var(--md-scheme-on-surface), .38)}:host .md3-text-field-container ::ng-deep .md3-input-element:disabled~.md3-text-field-state{display:none}:host .md3-text-field-container ::ng-deep .md3-input-element:disabled~.md3-text-field-outline{border-bottom:.0625em solid rgba(var(--md-scheme-on-surface),.38)}:host .md3-text-field-container ::ng-deep .md3-input-element:disabled~.md3-text-field-label{color:rgba(var(--md-scheme-on-surface),.38)}:host .md3-text-field-container ::ng-deep .md3-input-element:disabled~[md3-icon-element]{color:rgba(var(--md-scheme-on-surface),.38)}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]{caret-color:rgb(var(--md-scheme-error))}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]~.md3-text-field-outline{--outline-color: rgb(var(--md-scheme-error))}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]~.md3-text-field-label{color:rgb(var(--md-scheme-error))}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]~[md3-icon-element=trailing]{color:rgb(var(--md-scheme-error))}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]~[md3-icon-button].md3-filled{--background-color: rgb(var(--md-scheme-error));--foreground-color: rgb(var(--md-scheme-on-error));--state-color: var(--md-scheme-on-error)}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]~[md3-icon-button].md3-tonal{--background-color: rgb(var(--md-scheme-error-container));--foreground-color: rgb(var(--md-scheme-on-error-container));--state-color: var(--md-scheme-on-error-container)}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]~[md3-icon-button].md3-outlined{--background-color: transparent;--foreground-color: rgb(var(--md-scheme-error));--state-color: var(--md-scheme-error);border-color:var(--md-scheme-error)}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]~[md3-icon-button].md3-standard{--background-color: transparent;--foreground-color: rgb(var(--md-scheme-error));--state-color: var(--md-scheme-error)}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]:hover~.md3-text-field-outline{--outline-color: rgb(var(--md-scheme-on-error-container))}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]:hover~.md3-text-field-label{color:rgb(var(--md-scheme-on-error-container))}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]:hover~[md3-icon-element=trailing]{color:rgb(var(--md-scheme-on-error-container))}:host .md3-text-field-container.md3-has-leading-icon ::ng-deep .md3-input-element{padding-inline-start:3.25em}:host .md3-text-field-container.md3-has-leading-icon .md3-text-field-label{inset-inline-start:3.25em}:host .md3-text-field-container.md3-has-trailing-icon ::ng-deep .md3-input-element{padding-inline-end:3.25em}:host .md3-text-field-container.md3-has-trailing-icon .md3-text-field-label{inset-inline-end:3.25em}:host .md3-text-field-container.md3-text-field-outlined{--background-color: transparent;border-radius:var(--md-border-radius-xsmall);padding-top:.5em}:host .md3-text-field-container.md3-text-field-outlined .md3-text-field-outline{border-radius:inherit;border:.0625em solid var(--outline-color)}:host .md3-text-field-container.md3-text-field-outlined>::ng-deep [md3-icon-element=leading],:host .md3-text-field-container.md3-text-field-outlined>::ng-deep [md3-icon-element=trailing],:host .md3-text-field-container.md3-text-field-outlined>::ng-deep [md3-icon-button]{top:1em}:host .md3-text-field-container.md3-text-field-outlined .md3-text-field-label{top:1.5em}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element{padding-block:1em}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:focus~.md3-text-field-outline{padding:0 .5em;border-width:.1875em}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:not(:read-only):focus~.md3-text-field-outline .md3-label-gap,:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:not(:placeholder-shown)~.md3-text-field-outline .md3-label-gap{padding:0 .375em;max-width:fit-content}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:not(:read-only):focus~.md3-text-field-label,:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:not(:placeholder-shown)~.md3-text-field-label{top:0;inset-inline:1em}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:disabled{--foreground-color: rgba(var(--md-scheme-on-surface), .38)}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:disabled~.md3-text-field-outline{border:.0625em solid rgba(var(--md-scheme-on-surface),.38)}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:disabled~.md3-text-field-label{color:rgba(var(--md-scheme-on-surface),.38)}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:disabled~[md3-icon-element]{color:rgba(var(--md-scheme-on-surface),.38)}:host .md3-supporting-text-container{display:flex;flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1.333332em;padding:0 1.333332em;font-size:.75em;font-weight:400;line-height:1.333332em;letter-spacing:.033332em;color:var(--label-color)}:host .md3-supporting-text-container.md3-has-error{color:rgb(var(--md-scheme-error))}:host .md3-supporting-text-container .md3-text-input-counter{margin-inline-start:auto}:host .md3-supporting-text-container>::ng-deep *{margin-top:.333332em}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] });
6023
6022
  }
6024
6023
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: TextField, decorators: [{
6025
6024
  type: Component,
@@ -6030,7 +6029,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
6030
6029
  provide: MD3_BUTTON_CONTEXT,
6031
6030
  useExisting: TextField,
6032
6031
  },
6033
- ], template: "<label #inputContainer class=\"md3-text-field-container\" [ngClass]=\"{\n 'md3-text-field-filled': fieldType() == 'filled',\n 'md3-text-field-outlined': fieldType() == 'outlined',\n}\">\n <ng-content></ng-content>\n <fieldset aria-hidden=\"true\" class=\"md3-text-field-outline\">\n @if (label() && fieldType() == 'outlined') {\n <legend class=\"md3-label-gap\">{{ label() }}</legend>\n }\n </fieldset>\n @if (label()) {\n <label class=\"md3-text-field-label\">{{ label() }}</label>\n }\n <ng-content select=\"[md3-icon-element=leading]\"></ng-content>\n <ng-content select=\"[md3-icon-button], [md3-icon-element=trailing]\"></ng-content>\n @if (fieldType() != 'outlined') {\n <div class=\"md3-text-field-state\"></div>\n }\n</label>\n<div class=\"md3-supporting-text-container\" [class.md3-has-error]=\"inputError()\">\n <ng-content select=\"[md3-supporting-text]\"></ng-content>\n @if (inputCounter()) {\n <div class=\"md3-text-input-counter\">{{ valueLength() + (maxLength() ? \"/\" + maxLength() : \"\") }}</div>\n }\n</div>\n", styles: [":host{--background-color: rgb(var(--md-scheme-surface-container-highest));--foreground-color: rgb(var(--md-scheme-on-surface));--label-color: rgb(var(--md-scheme-on-surface-variant));--caret-color: rgb(var(--md-scheme-primary));--outline-color: rgb(var(--md-scheme-on-surface-variant));font-size:1rem;width:100%}:host .md3-text-field-container{display:block;position:relative;border-radius:var(--md-border-radius-xsmall) var(--md-border-radius-xsmall) 0 0;overflow:hidden}:host .md3-text-field-container ::ng-deep [md3-icon-element=leading]{position:absolute;font-size:1.5em!important;height:1em!important;width:1em!important;top:.666667em;inset-inline-start:.5em;pointer-events:none;color:var(--label-color)}:host .md3-text-field-container ::ng-deep [md3-icon-element=trailing]{position:absolute;font-size:1.5em!important;height:1em!important;width:1em!important;top:.666667em;inset-inline-end:.5em;pointer-events:none;color:var(--label-color)}:host .md3-text-field-container ::ng-deep [md3-icon-button]{position:absolute;font-size:1em!important;top:.5em;inset-inline-end:.5em}:host .md3-text-field-container .md3-text-field-outline{position:absolute;inset:0;pointer-events:none;padding:0 .625em;margin:.0625em 0 0;border:none;border-bottom:.0625em solid var(--outline-color);transition:all var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing),border-width none}:host .md3-text-field-container .md3-text-field-outline .md3-label-gap{padding:0;max-width:0em;font-size:.75em;font-weight:400;line-height:1.333332em;letter-spacing:.041667em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;opacity:0;visibility:hidden;transition:all var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing);interpolate-size:allow-keywords}:host .md3-text-field-container .md3-text-field-label{position:absolute;top:1em;inset-inline-start:1em;inset-inline-end:1em;padding:0;font-size:1em;font-weight:400;line-height:1.5em;letter-spacing:.03125em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;pointer-events:none;color:var(--label-color);transform-origin:top left;transform:scale(1);transition:all var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing)}:host .md3-text-field-container .md3-text-field-label:dir(rtl){transform-origin:top right}:host .md3-text-field-container .md3-text-field-state{position:absolute;inset:0;pointer-events:none;border-radius:inherit;background-color:rgba(var(--md-scheme-on-surface));opacity:0;transition:all var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing)}:host .md3-text-field-container ::ng-deep .md3-input-element{display:block;width:100%;border:none;outline:none;padding:1.5em 1em .5em;font-size:1em;font-weight:400;line-height:1.5em;letter-spacing:.03125em;resize:none;background-color:var(--background-color);color:var(--foreground-color);caret-color:var(--caret-color)}:host .md3-text-field-container ::ng-deep .md3-input-element:not(:placeholder-shown)~.md3-text-field-label{top:.4375em;transform:scale(.75)}:host .md3-text-field-container ::ng-deep .md3-input-element:hover~.md3-text-field-state{opacity:.08}:host .md3-text-field-container ::ng-deep .md3-input-element:hover~.md3-text-field-outline{--outline-color: rgb(var(--md-scheme-on-surface))}:host .md3-text-field-container ::ng-deep .md3-input-element:focus~.md3-text-field-outline{--outline-color: rgb(var(--md-scheme-primary));border-bottom-width:.1875em}:host .md3-text-field-container ::ng-deep .md3-input-element:focus~.md3-text-field-label{color:var(--caret-color);top:.4375em;transform:scale(.75)}:host .md3-text-field-container ::ng-deep .md3-input-element:disabled{--background-color: rgba(var(--md-scheme-surface-container), .4);--foreground-color: rgba(var(--md-scheme-on-surface), .38)}:host .md3-text-field-container ::ng-deep .md3-input-element:disabled~.md3-text-field-state{display:none}:host .md3-text-field-container ::ng-deep .md3-input-element:disabled~.md3-text-field-outline{border-bottom:.0625em solid rgba(var(--md-scheme-on-surface),.38)}:host .md3-text-field-container ::ng-deep .md3-input-element:disabled~.md3-text-field-label{color:rgba(var(--md-scheme-on-surface),.38)}:host .md3-text-field-container ::ng-deep .md3-input-element:disabled~[md3-icon-element]{color:rgba(var(--md-scheme-on-surface),.38)}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]{caret-color:rgb(var(--md-scheme-error))}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]~.md3-text-field-outline{--outline-color: rgb(var(--md-scheme-error))}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]~.md3-text-field-label{color:rgb(var(--md-scheme-error))}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]~[md3-icon-element=trailing]{color:rgb(var(--md-scheme-error))}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]~[md3-icon-button].md3-filled{--background-color: rgb(var(--md-scheme-error));--foreground-color: rgb(var(--md-scheme-on-error));--state-color: var(--md-scheme-on-error)}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]~[md3-icon-button].md3-tonal{--background-color: rgb(var(--md-scheme-error-container));--foreground-color: rgb(var(--md-scheme-on-error-container));--state-color: var(--md-scheme-on-error-container)}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]~[md3-icon-button].md3-outlined{--background-color: transparent;--foreground-color: rgb(var(--md-scheme-error));--state-color: var(--md-scheme-error);border-color:var(--md-scheme-error)}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]~[md3-icon-button].md3-standard{--background-color: transparent;--foreground-color: rgb(var(--md-scheme-error));--state-color: var(--md-scheme-error)}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]:hover~.md3-text-field-outline{--outline-color: rgb(var(--md-scheme-on-error-container))}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]:hover~.md3-text-field-label{color:rgb(var(--md-scheme-on-error-container))}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]:hover~[md3-icon-element=trailing]{color:rgb(var(--md-scheme-on-error-container))}:host .md3-text-field-container.md3-has-leading-icon ::ng-deep .md3-input-element{padding-inline-start:3.25em}:host .md3-text-field-container.md3-has-leading-icon .md3-text-field-label{inset-inline-start:3.25em}:host .md3-text-field-container.md3-has-trailing-icon ::ng-deep .md3-input-element{padding-inline-end:3.25em}:host .md3-text-field-container.md3-has-trailing-icon .md3-text-field-label{inset-inline-end:3.25em}:host .md3-text-field-container.md3-text-field-outlined{--background-color: transparent;border-radius:var(--md-border-radius-xsmall);padding-top:.5em}:host .md3-text-field-container.md3-text-field-outlined .md3-text-field-outline{border-radius:inherit;border:.0625em solid var(--outline-color)}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep [md3-icon-element=leading],:host .md3-text-field-container.md3-text-field-outlined ::ng-deep [md3-icon-element=trailing],:host .md3-text-field-container.md3-text-field-outlined ::ng-deep [md3-icon-button]{top:1em}:host .md3-text-field-container.md3-text-field-outlined .md3-text-field-label{top:1.5em}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element{padding-block:1em}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:focus~.md3-text-field-outline{padding:0 .5em;border-width:.1875em}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:focus~.md3-text-field-outline .md3-label-gap,:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:not(:placeholder-shown)~.md3-text-field-outline .md3-label-gap{padding:0 .375em;max-width:fit-content}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:focus~.md3-text-field-label,:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:not(:placeholder-shown)~.md3-text-field-label{top:0;inset-inline:1em}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:disabled{--foreground-color: rgba(var(--md-scheme-on-surface), .38)}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:disabled~.md3-text-field-outline{border:.0625em solid rgba(var(--md-scheme-on-surface),.38)}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:disabled~.md3-text-field-label{color:rgba(var(--md-scheme-on-surface),.38)}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:disabled~[md3-icon-element]{color:rgba(var(--md-scheme-on-surface),.38)}:host .md3-supporting-text-container{display:flex;flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1.333332em;padding:0 1.333332em;font-size:.75em;font-weight:400;line-height:1.333332em;letter-spacing:.033332em;color:var(--label-color)}:host .md3-supporting-text-container.md3-has-error{color:rgb(var(--md-scheme-error))}:host .md3-supporting-text-container .md3-text-input-counter{margin-inline-start:auto}:host .md3-supporting-text-container>::ng-deep *{margin-top:.333332em}\n"] }]
6032
+ ], template: "<label #inputContainer class=\"md3-text-field-container\" [ngClass]=\"{\n 'md3-text-field-filled': fieldType() == 'filled',\n 'md3-text-field-outlined': fieldType() == 'outlined',\n}\">\n <ng-content></ng-content>\n <fieldset aria-hidden=\"true\" class=\"md3-text-field-outline\">\n @if (label() && fieldType() == 'outlined') {\n <legend class=\"md3-label-gap\">{{ label() }}</legend>\n }\n </fieldset>\n @if (label()) {\n <span class=\"md3-text-field-label\">{{ label() }}</span>\n }\n <ng-content select=\"[md3-icon-element=leading]\"></ng-content>\n <ng-content select=\"[md3-icon-button], [md3-icon-element=trailing]\"></ng-content>\n @if (fieldType() != 'outlined') {\n <div class=\"md3-text-field-state\"></div>\n }\n</label>\n<div class=\"md3-supporting-text-container\" [class.md3-has-error]=\"inputError()\">\n <ng-content select=\"[md3-supporting-text]\"></ng-content>\n @if (inputCounter()) {\n <div class=\"md3-text-input-counter\">{{ valueLength() + (maxLength() ? \"/\" + maxLength() : \"\") }}</div>\n }\n</div>\n", styles: [":host{--background-color: rgb(var(--md-scheme-surface-container-highest));--foreground-color: rgb(var(--md-scheme-on-surface));--label-color: rgb(var(--md-scheme-on-surface-variant));--caret-color: rgb(var(--md-scheme-primary));--outline-color: rgb(var(--md-scheme-on-surface-variant));font-size:1rem;width:100%}:host .md3-text-field-container{display:block;position:relative;border-radius:var(--md-border-radius-xsmall) var(--md-border-radius-xsmall) 0 0;overflow:hidden}:host .md3-text-field-container>::ng-deep [md3-icon-element=leading]{position:absolute;font-size:1.5em!important;height:1em!important;width:1em!important;top:.666667em;inset-inline-start:.5em;pointer-events:none;color:var(--label-color)}:host .md3-text-field-container>::ng-deep [md3-icon-element=trailing]{position:absolute;font-size:1.5em!important;height:1em!important;width:1em!important;top:.666667em;inset-inline-end:.5em;pointer-events:none;color:var(--label-color)}:host .md3-text-field-container>::ng-deep [md3-icon-button]{position:absolute;font-size:1em!important;top:.5em;inset-inline-end:.5em}:host .md3-text-field-container .md3-text-field-outline{position:absolute;inset:0;pointer-events:none;padding:0 .625em;margin:.0625em 0 0;border:none;border-bottom:.0625em solid var(--outline-color);transition:all var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing),border-width none}:host .md3-text-field-container .md3-text-field-outline .md3-label-gap{padding:0;max-width:0em;font-size:.75em;font-weight:400;line-height:1.333332em;letter-spacing:.041667em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;opacity:0;visibility:hidden;transition:all var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing);interpolate-size:allow-keywords}:host .md3-text-field-container .md3-text-field-label{position:absolute;top:1em;inset-inline-start:1em;inset-inline-end:1em;padding:0;font-size:1em;font-weight:400;line-height:1.5em;letter-spacing:.03125em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;pointer-events:none;color:var(--label-color);transform-origin:top left;transform:scale(1);transition:all var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing)}:host .md3-text-field-container .md3-text-field-label:dir(rtl){transform-origin:top right}:host .md3-text-field-container .md3-text-field-state{position:absolute;inset:0;pointer-events:none;border-radius:inherit;background-color:rgba(var(--md-scheme-on-surface));opacity:0;transition:all var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing)}:host .md3-text-field-container ::ng-deep .md3-input-element{display:block;width:100%;border:none;outline:none;padding:1.5em 1em .5em;font-size:1em;font-weight:400;line-height:1.5em;letter-spacing:.03125em;resize:none;background-color:var(--background-color);color:var(--foreground-color);caret-color:var(--caret-color)}:host .md3-text-field-container ::ng-deep .md3-input-element:not(:placeholder-shown)~.md3-text-field-label{top:.4375em;transform:scale(.75)}:host .md3-text-field-container ::ng-deep .md3-input-element:hover~.md3-text-field-state{opacity:.08}:host .md3-text-field-container ::ng-deep .md3-input-element:hover~.md3-text-field-outline{--outline-color: rgb(var(--md-scheme-on-surface))}:host .md3-text-field-container ::ng-deep .md3-input-element:focus~.md3-text-field-outline{--outline-color: rgb(var(--md-scheme-primary));border-bottom-width:.1875em}:host .md3-text-field-container ::ng-deep .md3-input-element:focus:not(:read-only)~.md3-text-field-label{color:var(--caret-color);top:.4375em;transform:scale(.75)}:host .md3-text-field-container ::ng-deep .md3-input-element:disabled{--background-color: rgba(var(--md-scheme-surface-container), .4);--foreground-color: rgba(var(--md-scheme-on-surface), .38)}:host .md3-text-field-container ::ng-deep .md3-input-element:disabled~.md3-text-field-state{display:none}:host .md3-text-field-container ::ng-deep .md3-input-element:disabled~.md3-text-field-outline{border-bottom:.0625em solid rgba(var(--md-scheme-on-surface),.38)}:host .md3-text-field-container ::ng-deep .md3-input-element:disabled~.md3-text-field-label{color:rgba(var(--md-scheme-on-surface),.38)}:host .md3-text-field-container ::ng-deep .md3-input-element:disabled~[md3-icon-element]{color:rgba(var(--md-scheme-on-surface),.38)}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]{caret-color:rgb(var(--md-scheme-error))}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]~.md3-text-field-outline{--outline-color: rgb(var(--md-scheme-error))}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]~.md3-text-field-label{color:rgb(var(--md-scheme-error))}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]~[md3-icon-element=trailing]{color:rgb(var(--md-scheme-error))}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]~[md3-icon-button].md3-filled{--background-color: rgb(var(--md-scheme-error));--foreground-color: rgb(var(--md-scheme-on-error));--state-color: var(--md-scheme-on-error)}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]~[md3-icon-button].md3-tonal{--background-color: rgb(var(--md-scheme-error-container));--foreground-color: rgb(var(--md-scheme-on-error-container));--state-color: var(--md-scheme-on-error-container)}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]~[md3-icon-button].md3-outlined{--background-color: transparent;--foreground-color: rgb(var(--md-scheme-error));--state-color: var(--md-scheme-error);border-color:var(--md-scheme-error)}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]~[md3-icon-button].md3-standard{--background-color: transparent;--foreground-color: rgb(var(--md-scheme-error));--state-color: var(--md-scheme-error)}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]:hover~.md3-text-field-outline{--outline-color: rgb(var(--md-scheme-on-error-container))}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]:hover~.md3-text-field-label{color:rgb(var(--md-scheme-on-error-container))}:host .md3-text-field-container ::ng-deep .md3-input-element[aria-invalid=true]:hover~[md3-icon-element=trailing]{color:rgb(var(--md-scheme-on-error-container))}:host .md3-text-field-container.md3-has-leading-icon ::ng-deep .md3-input-element{padding-inline-start:3.25em}:host .md3-text-field-container.md3-has-leading-icon .md3-text-field-label{inset-inline-start:3.25em}:host .md3-text-field-container.md3-has-trailing-icon ::ng-deep .md3-input-element{padding-inline-end:3.25em}:host .md3-text-field-container.md3-has-trailing-icon .md3-text-field-label{inset-inline-end:3.25em}:host .md3-text-field-container.md3-text-field-outlined{--background-color: transparent;border-radius:var(--md-border-radius-xsmall);padding-top:.5em}:host .md3-text-field-container.md3-text-field-outlined .md3-text-field-outline{border-radius:inherit;border:.0625em solid var(--outline-color)}:host .md3-text-field-container.md3-text-field-outlined>::ng-deep [md3-icon-element=leading],:host .md3-text-field-container.md3-text-field-outlined>::ng-deep [md3-icon-element=trailing],:host .md3-text-field-container.md3-text-field-outlined>::ng-deep [md3-icon-button]{top:1em}:host .md3-text-field-container.md3-text-field-outlined .md3-text-field-label{top:1.5em}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element{padding-block:1em}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:focus~.md3-text-field-outline{padding:0 .5em;border-width:.1875em}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:not(:read-only):focus~.md3-text-field-outline .md3-label-gap,:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:not(:placeholder-shown)~.md3-text-field-outline .md3-label-gap{padding:0 .375em;max-width:fit-content}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:not(:read-only):focus~.md3-text-field-label,:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:not(:placeholder-shown)~.md3-text-field-label{top:0;inset-inline:1em}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:disabled{--foreground-color: rgba(var(--md-scheme-on-surface), .38)}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:disabled~.md3-text-field-outline{border:.0625em solid rgba(var(--md-scheme-on-surface),.38)}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:disabled~.md3-text-field-label{color:rgba(var(--md-scheme-on-surface),.38)}:host .md3-text-field-container.md3-text-field-outlined ::ng-deep .md3-input-element:disabled~[md3-icon-element]{color:rgba(var(--md-scheme-on-surface),.38)}:host .md3-supporting-text-container{display:flex;flex-direction:row;align-items:flex-start;justify-content:space-between;gap:1.333332em;padding:0 1.333332em;font-size:.75em;font-weight:400;line-height:1.333332em;letter-spacing:.033332em;color:var(--label-color)}:host .md3-supporting-text-container.md3-has-error{color:rgb(var(--md-scheme-error))}:host .md3-supporting-text-container .md3-text-input-counter{margin-inline-start:auto}:host .md3-supporting-text-container>::ng-deep *{margin-top:.333332em}\n"] }]
6034
6033
  }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], fieldType: [{ type: i0.Input, args: [{ isSignal: true, alias: "field-type", required: false }] }], inputCounter: [{ type: i0.Input, args: [{ isSignal: true, alias: "input-counter", required: false }] }], control: [{ type: i0.Input, args: [{ isSignal: true, alias: "control", required: false }] }], inputContainer: [{ type: i0.ViewChild, args: ['inputContainer', { isSignal: true }] }], input: [{ type: i0.ContentChild, args: [i0.forwardRef(() => InputElement), { isSignal: true }] }], iconElements: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => IconElement), { ...{ descendants: true }, isSignal: true }] }], iconButtons: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => IconButton), { ...{ descendants: true }, isSignal: true }] }], controlName: [{ type: i0.ContentChild, args: [i0.forwardRef(() => FormControlName), { isSignal: true }] }] } });
6035
6034
 
6036
6035
  class SupportingText {
@@ -6047,1207 +6046,1363 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
6047
6046
  }]
6048
6047
  }] });
6049
6048
 
6050
- /**
6051
- * Material 3 dialog shell. It extends the CDK dialog container, so focus
6052
- * trapping, focus restoration and the ARIA attributes are handled by the CDK
6053
- * while this component only owns the MD3 surface and its animation.
6054
- */
6055
- class Dialog extends CdkDialogContainer {
6056
- surface = viewChild('surface', /* @ts-ignore */
6057
- ...(ngDevMode ? [{ debugName: "surface" }] : /* istanbul ignore next */ []));
6058
- isActive = signal(false, /* @ts-ignore */
6059
- ...(ngDevMode ? [{ debugName: "isActive" }] : /* istanbul ignore next */ []));
6060
- config = inject(DIALOG_CONFIG, { optional: true }) ?? {};
6061
- get surfaceElement() {
6062
- return this.surface()?.nativeElement ?? null;
6049
+ class MenuGroup {
6050
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: MenuGroup, deps: [], target: i0.ɵɵFactoryTarget.Component });
6051
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.0", type: MenuGroup, isStandalone: true, selector: "md3-menu-group", host: { attributes: { "role": "group" } }, ngImport: i0, template: "<ng-content></ng-content>\n", styles: [":host{display:flex;flex-direction:column;gap:.125em;padding:.25em;background-color:var(--background-color);color:var(--foreground-color);border-radius:var(--menu-group-radius);box-shadow:var(--menu-group-shadow)}:host:first-child{border-start-start-radius:var(--border-radius);border-start-end-radius:var(--border-radius)}:host:last-child{border-end-start-radius:var(--border-radius);border-end-end-radius:var(--border-radius)}:host ::ng-deep md3-divider{margin:.125em .5em}:host ::ng-deep [md3-menu-item]:first-child .md3-menu-item-container{--border-top-start-r: var(--md-border-radius-medium);--border-top-end-r: var(--md-border-radius-medium);border-start-start-radius:var(--border-top-start-r);border-start-end-radius:var(--border-top-end-r)}:host ::ng-deep [md3-menu-item]:last-child .md3-menu-item-container{--border-bottom-start-r: var(--md-border-radius-medium);--border-bottom-end-r: var(--md-border-radius-medium);border-end-start-radius:var(--border-bottom-start-r);border-end-end-radius:var(--border-bottom-end-r)}.md3-menu-group-label{padding:.5rem var(--menu-item-inline-padding, .75rem) .25rem;color:rgb(var(--md-scheme-on-surface-variant));font-size:.75rem;font-weight:500;line-height:1rem;letter-spacing:.03125rem}\n"] });
6052
+ }
6053
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: MenuGroup, decorators: [{
6054
+ type: Component,
6055
+ args: [{ selector: 'md3-menu-group', imports: [], host: {
6056
+ 'role': 'group',
6057
+ }, template: "<ng-content></ng-content>\n", styles: [":host{display:flex;flex-direction:column;gap:.125em;padding:.25em;background-color:var(--background-color);color:var(--foreground-color);border-radius:var(--menu-group-radius);box-shadow:var(--menu-group-shadow)}:host:first-child{border-start-start-radius:var(--border-radius);border-start-end-radius:var(--border-radius)}:host:last-child{border-end-start-radius:var(--border-radius);border-end-end-radius:var(--border-radius)}:host ::ng-deep md3-divider{margin:.125em .5em}:host ::ng-deep [md3-menu-item]:first-child .md3-menu-item-container{--border-top-start-r: var(--md-border-radius-medium);--border-top-end-r: var(--md-border-radius-medium);border-start-start-radius:var(--border-top-start-r);border-start-end-radius:var(--border-top-end-r)}:host ::ng-deep [md3-menu-item]:last-child .md3-menu-item-container{--border-bottom-start-r: var(--md-border-radius-medium);--border-bottom-end-r: var(--md-border-radius-medium);border-end-start-radius:var(--border-bottom-start-r);border-end-end-radius:var(--border-bottom-end-r)}.md3-menu-group-label{padding:.5rem var(--menu-item-inline-padding, .75rem) .25rem;color:rgb(var(--md-scheme-on-surface-variant));font-size:.75rem;font-weight:500;line-height:1rem;letter-spacing:.03125rem}\n"] }]
6058
+ }] });
6059
+
6060
+ class MenuItem {
6061
+ el;
6062
+ label = input('', { ...(ngDevMode ? { debugName: "label" } : /* istanbul ignore next */ {}), alias: 'label' });
6063
+ supportingText = input(null, { ...(ngDevMode ? { debugName: "supportingText" } : /* istanbul ignore next */ {}), alias: 'supporting-text' });
6064
+ trailingText = input(null, { ...(ngDevMode ? { debugName: "trailingText" } : /* istanbul ignore next */ {}), alias: 'trailing-text' });
6065
+ selected = model(null, { ...(ngDevMode ? { debugName: "selected" } : /* istanbul ignore next */ {}), alias: 'selected' });
6066
+ iconElements = contentChildren(IconElement, /* @ts-ignore */
6067
+ ...(ngDevMode ? [{ debugName: "iconElements" }] : /* istanbul ignore next */ []));
6068
+ get leading() {
6069
+ return this.iconElements().find((icon) => icon.iconType() === 'leading');
6063
6070
  }
6064
- /**
6065
- * Started by DialogService, either right after the dialog is created or
6066
- * once the dialog it replaces has had its head start. The animation runs on
6067
- * the next frame so the surface has a real from/to state.
6068
- */
6069
- startEnterAnimation() {
6070
- requestAnimationFrame(() => {
6071
- this.isActive.set(true);
6072
- });
6071
+ get trailing() {
6072
+ return this.iconElements().find((icon) => icon.iconType() === 'trailing');
6073
6073
  }
6074
- setActive(value) {
6075
- this.isActive.set(value);
6074
+ get showLeadingIcon() {
6075
+ return (!!this.leading && !this.selected()) || this.selected() == true;
6076
6076
  }
6077
- recaptureFocus() {
6078
- this._recaptureFocus();
6077
+ onHostClick() {
6078
+ const selected = this.selected();
6079
+ if (selected === null) {
6080
+ return;
6081
+ }
6082
+ this.selected.set(!selected);
6079
6083
  }
6080
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Dialog, deps: null, target: i0.ɵɵFactoryTarget.Component });
6081
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.0", type: Dialog, isStandalone: true, selector: "md3-dialog", viewQueries: [{ propertyName: "surface", first: true, predicate: ["surface"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<!-- The role and aria attributes live on the host element, applied by the CDK dialog container. -->\n<div\n #surface\n class=\"md3-dialog-container\"\n [class.md3-active]=\"isActive()\"\n [class.md-scheme-dark]=\"config.scheme == 'dark'\"\n [class.md-scheme-light]=\"config.scheme == 'light'\"\n [attr.dir]=\"config.direction ?? null\">\n <ng-template cdkPortalOutlet></ng-template>\n</div>\n", styles: ["::ng-deep .cdk-overlay-container{position:fixed;z-index:1000;inset:0;pointer-events:none;font-size:1rem}::ng-deep .cdk-overlay-container .md3-dialog-scrim{position:absolute;inset:0;pointer-events:auto;background-color:rgb(var(--md-scheme-scrim));transition:all var(--md-motion-expressive-slow-spatial-duration) var(--md-motion-expressive-slow-spatial-easing);opacity:.32}::ng-deep .cdk-overlay-container .md3-dialog-scrim.md3-dialog-opening{opacity:0}::ng-deep .cdk-overlay-container .md3-dialog-scrim.md3-dialog-closing{opacity:0}::ng-deep .cdk-overlay-container .md3-dialog-scrim.md3-dialog-hidden{opacity:0;pointer-events:none}::ng-deep .cdk-overlay-container .md3-dialog-scrim.md3-dialog-scrim-kept{opacity:.32;transition:none}::ng-deep .cdk-overlay-container .md3-dialog-scrim.md3-dialog-scrim-instant{transition:none}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper{position:absolute;display:flex;inset:0;pointer-events:none;padding:3.5rem 1.5rem;overflow:hidden}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-dialog-panel{position:absolute;pointer-events:auto;max-width:35em}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-dialog-panel.md3-dialog-closing,::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-dialog-panel.md3-dialog-hidden{pointer-events:none}:host{display:block;max-width:100%;min-width:17.5em;outline:0}:host .md3-dialog-container{display:block;width:100%;max-height:inherit;overflow:auto;color:rgb(var(--md-scheme-on-surface));background-color:rgb(var(--md-scheme-surface-container-high));border-radius:var(--md-border-radius-xlarge);box-shadow:var(--md-shadow-6dp);transform-origin:center;transition:all var(--md-motion-expressive-default-spatial-duration) var(--md-motion-expressive-default-spatial-easing);opacity:0;transform:scaleY(.8);margin-top:-8rem}:host .md3-dialog-container.md3-active{opacity:1;transform:scaleY(1);margin-top:0}\n"], dependencies: [{ kind: "directive", type: CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }] });
6084
+ constructor(el) {
6085
+ this.el = el;
6086
+ }
6087
+ get element() {
6088
+ return this.el.nativeElement;
6089
+ }
6090
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: MenuItem, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
6091
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: MenuItem, isStandalone: true, selector: "button[md3-menu-item], a[md3-menu-item]", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, supportingText: { classPropertyName: "supportingText", publicName: "supporting-text", isSignal: true, isRequired: false, transformFunction: null }, trailingText: { classPropertyName: "trailingText", publicName: "trailing-text", isSignal: true, isRequired: false, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selected: "selectedChange" }, host: { listeners: { "click": "onHostClick()" } }, queries: [{ propertyName: "iconElements", predicate: IconElement, isSignal: true }], ngImport: i0, template: "@if (selected() !== null) {\n <input type=\"checkbox\" md3-input-element [checked]=\"selected()\">\n}\n\n<div class=\"md3-menu-item-container\" md3-state-component>\n <div class=\"md3-menu-item-leading\" [class.has-icon]=\"showLeadingIcon\">\n <ng-content select=\"[md3-icon-element=leading]\"></ng-content>\n @if (!leading) {\n <md3-icon>check</md3-icon>\n }\n </div>\n\n <div class=\"md3-menu-item-content\">\n <md3-type-label size=\"large\">{{ label() }}</md3-type-label>\n @if (supportingText()?.length) {\n <md3-type-body size=\"small\">{{ supportingText() }}</md3-type-body>\n }\n </div>\n\n <div class=\"md3-menu-item-trailing\">\n <ng-content select=\"md3-badge\"></ng-content>\n @if (trailingText()?.length) {\n <md3-type-title size=\"small\">{{ trailingText() }}</md3-type-title>\n }\n <ng-content select=\"[md3-icon-element=trailing]\"></ng-content>\n </div>\n</div>", styles: [":host{--state-color: var(--menu-item-state-color);--border-radius: var(--menu-item-border-radius);--outline-color: var(--menu-item-outline-color);--outline-width: var(--menu-item-outline-width);--outline-offset: var(--menu-item-outline-offset);position:relative;display:block;padding:0;border:0;background:transparent;color:var(--menu-item-label-color);font:inherit;text-align:start;text-decoration:none;cursor:pointer;outline:none;appearance:none;-webkit-user-select:none;user-select:none;z-index:0}:host:focus{z-index:1}:host:focus:not(:hover) .md3-menu-item-container:after{opacity:1}:host:active{z-index:2}:host:active .md3-menu-item-container:after{opacity:0}:host .md3-menu-item-container{display:flex;align-items:center;min-height:3em;padding:0 .75em;border-radius:var(--border-radius);transition:background-color var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing),color var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing)}:host .md3-menu-item-container .md3-menu-item-leading{display:inline-flex;align-items:center;justify-content:center;font-size:inherit;opacity:0;transform:scale(1.2);width:0;height:1.25em;color:var(--menu-item-supporting-color);transition:all var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing)}:host .md3-menu-item-container .md3-menu-item-leading ::ng-deep [md3-icon-element]{font-size:1.25em!important;height:1em!important;width:1em!important;line-height:1em!important}:host .md3-menu-item-container .md3-menu-item-leading.has-icon{margin-inline-end:.5em;opacity:1;transform:scale(1);height:1.25em;width:1.25em}:host .md3-menu-item-container .md3-menu-item-trailing{flex:0 1 content;display:inline-flex;gap:.5em;align-items:center;justify-content:center;font-size:inherit;margin-inline-start:.5em;color:var(--menu-item-supporting-color)}:host .md3-menu-item-container .md3-menu-item-trailing:empty{margin:0}:host .md3-menu-item-container .md3-menu-item-trailing ::ng-deep md3-badge{background-color:var(--menu-item-badge-background);color:var(--menu-item-badge-foreground)}:host .md3-menu-item-container .md3-menu-item-trailing .md3-text-title.md3-text-small{margin:0;padding:0;white-space:nowrap;letter-spacing:.007143em}:host .md3-menu-item-container .md3-menu-item-trailing ::ng-deep [md3-icon-element]{font-size:1.25em!important;height:1em!important;width:1em!important;line-height:1em!important}:host .md3-menu-item-container .md3-menu-item-content{flex:1 1 auto}:host .md3-menu-item-container .md3-menu-item-content .md3-text-label.md3-text-large{display:block;margin:0;padding:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;letter-spacing:.007143em;color:var(--menu-item-label-color)}:host .md3-menu-item-container .md3-menu-item-content .md3-text-body.md3-text-small{display:block;margin:0;padding:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;letter-spacing:.0333334em;color:var(--menu-item-supporting-color)}:host [md3-input-element]{position:absolute;height:0;width:0;opacity:0;pointer-events:none}:host [md3-input-element]+.md3-menu-item-container .md3-menu-item-leading{opacity:0;transform:scale(1.2);width:0}:host [md3-input-element]:checked+.md3-menu-item-container{--menu-item-label-color: var(--menu-item-selected-foreground);--menu-item-supporting-color: var(--menu-item-selected-foreground);--border-radius: var(--md-border-radius-medium);background-color:var(--menu-item-selected-background)}:host [md3-input-element]:checked+.md3-menu-item-container .md3-menu-item-leading{opacity:1;transform:scale(1);width:1.25em}:host:disabled{cursor:default}:host:disabled .md3-menu-item-container{--menu-item-label-color: var(--menu-item-label-color);--menu-item-supporting-color: var(--menu-item-label-color);--menu-item-selected-background: transparent;--menu-item-selected-foreground: var(--menu-item-label-color);pointer-events:none;opacity:.38}:host:disabled .md3-menu-item-container.md3-state-layer,:host:disabled .md3-menu-item-container:after{display:none}\n"], dependencies: [{ kind: "directive", type: InputElement, selector: "[md3-input-element]" }, { kind: "directive", type: StateComponent, selector: "[md3-state-component]" }, { kind: "component", type: MaterialIcon, selector: "md3-icon", inputs: ["icon-version", "filled", "icon-style", "icon-size", "bi-directional"] }, { kind: "directive", type: TypeLabel, selector: "md3-type-label, [md3-type-label]", inputs: ["size", "color", "emphasized"] }, { kind: "directive", type: TypeBody, selector: "md3-type-body, [md3-type-body]", inputs: ["size", "color", "emphasized"] }, { kind: "directive", type: TypeTitle, selector: "md3-type-title, [md3-type-title]", inputs: ["size", "color", "emphasized"] }] });
6082
6092
  }
6083
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Dialog, decorators: [{
6093
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: MenuItem, decorators: [{
6084
6094
  type: Component,
6085
- args: [{ selector: 'md3-dialog', imports: [
6086
- CdkPortalOutlet,
6087
- ], template: "<!-- The role and aria attributes live on the host element, applied by the CDK dialog container. -->\n<div\n #surface\n class=\"md3-dialog-container\"\n [class.md3-active]=\"isActive()\"\n [class.md-scheme-dark]=\"config.scheme == 'dark'\"\n [class.md-scheme-light]=\"config.scheme == 'light'\"\n [attr.dir]=\"config.direction ?? null\">\n <ng-template cdkPortalOutlet></ng-template>\n</div>\n", styles: ["::ng-deep .cdk-overlay-container{position:fixed;z-index:1000;inset:0;pointer-events:none;font-size:1rem}::ng-deep .cdk-overlay-container .md3-dialog-scrim{position:absolute;inset:0;pointer-events:auto;background-color:rgb(var(--md-scheme-scrim));transition:all var(--md-motion-expressive-slow-spatial-duration) var(--md-motion-expressive-slow-spatial-easing);opacity:.32}::ng-deep .cdk-overlay-container .md3-dialog-scrim.md3-dialog-opening{opacity:0}::ng-deep .cdk-overlay-container .md3-dialog-scrim.md3-dialog-closing{opacity:0}::ng-deep .cdk-overlay-container .md3-dialog-scrim.md3-dialog-hidden{opacity:0;pointer-events:none}::ng-deep .cdk-overlay-container .md3-dialog-scrim.md3-dialog-scrim-kept{opacity:.32;transition:none}::ng-deep .cdk-overlay-container .md3-dialog-scrim.md3-dialog-scrim-instant{transition:none}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper{position:absolute;display:flex;inset:0;pointer-events:none;padding:3.5rem 1.5rem;overflow:hidden}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-dialog-panel{position:absolute;pointer-events:auto;max-width:35em}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-dialog-panel.md3-dialog-closing,::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-dialog-panel.md3-dialog-hidden{pointer-events:none}:host{display:block;max-width:100%;min-width:17.5em;outline:0}:host .md3-dialog-container{display:block;width:100%;max-height:inherit;overflow:auto;color:rgb(var(--md-scheme-on-surface));background-color:rgb(var(--md-scheme-surface-container-high));border-radius:var(--md-border-radius-xlarge);box-shadow:var(--md-shadow-6dp);transform-origin:center;transition:all var(--md-motion-expressive-default-spatial-duration) var(--md-motion-expressive-default-spatial-easing);opacity:0;transform:scaleY(.8);margin-top:-8rem}:host .md3-dialog-container.md3-active{opacity:1;transform:scaleY(1);margin-top:0}\n"] }]
6088
- }], propDecorators: { surface: [{ type: i0.ViewChild, args: ['surface', { isSignal: true }] }] } });
6095
+ args: [{ selector: 'button[md3-menu-item], a[md3-menu-item]', imports: [
6096
+ InputElement,
6097
+ StateComponent,
6098
+ MaterialIcon,
6099
+ TypeLabel,
6100
+ TypeBody,
6101
+ TypeTitle,
6102
+ ], host: {
6103
+ '(click)': 'onHostClick()',
6104
+ }, template: "@if (selected() !== null) {\n <input type=\"checkbox\" md3-input-element [checked]=\"selected()\">\n}\n\n<div class=\"md3-menu-item-container\" md3-state-component>\n <div class=\"md3-menu-item-leading\" [class.has-icon]=\"showLeadingIcon\">\n <ng-content select=\"[md3-icon-element=leading]\"></ng-content>\n @if (!leading) {\n <md3-icon>check</md3-icon>\n }\n </div>\n\n <div class=\"md3-menu-item-content\">\n <md3-type-label size=\"large\">{{ label() }}</md3-type-label>\n @if (supportingText()?.length) {\n <md3-type-body size=\"small\">{{ supportingText() }}</md3-type-body>\n }\n </div>\n\n <div class=\"md3-menu-item-trailing\">\n <ng-content select=\"md3-badge\"></ng-content>\n @if (trailingText()?.length) {\n <md3-type-title size=\"small\">{{ trailingText() }}</md3-type-title>\n }\n <ng-content select=\"[md3-icon-element=trailing]\"></ng-content>\n </div>\n</div>", styles: [":host{--state-color: var(--menu-item-state-color);--border-radius: var(--menu-item-border-radius);--outline-color: var(--menu-item-outline-color);--outline-width: var(--menu-item-outline-width);--outline-offset: var(--menu-item-outline-offset);position:relative;display:block;padding:0;border:0;background:transparent;color:var(--menu-item-label-color);font:inherit;text-align:start;text-decoration:none;cursor:pointer;outline:none;appearance:none;-webkit-user-select:none;user-select:none;z-index:0}:host:focus{z-index:1}:host:focus:not(:hover) .md3-menu-item-container:after{opacity:1}:host:active{z-index:2}:host:active .md3-menu-item-container:after{opacity:0}:host .md3-menu-item-container{display:flex;align-items:center;min-height:3em;padding:0 .75em;border-radius:var(--border-radius);transition:background-color var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing),color var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing)}:host .md3-menu-item-container .md3-menu-item-leading{display:inline-flex;align-items:center;justify-content:center;font-size:inherit;opacity:0;transform:scale(1.2);width:0;height:1.25em;color:var(--menu-item-supporting-color);transition:all var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing)}:host .md3-menu-item-container .md3-menu-item-leading ::ng-deep [md3-icon-element]{font-size:1.25em!important;height:1em!important;width:1em!important;line-height:1em!important}:host .md3-menu-item-container .md3-menu-item-leading.has-icon{margin-inline-end:.5em;opacity:1;transform:scale(1);height:1.25em;width:1.25em}:host .md3-menu-item-container .md3-menu-item-trailing{flex:0 1 content;display:inline-flex;gap:.5em;align-items:center;justify-content:center;font-size:inherit;margin-inline-start:.5em;color:var(--menu-item-supporting-color)}:host .md3-menu-item-container .md3-menu-item-trailing:empty{margin:0}:host .md3-menu-item-container .md3-menu-item-trailing ::ng-deep md3-badge{background-color:var(--menu-item-badge-background);color:var(--menu-item-badge-foreground)}:host .md3-menu-item-container .md3-menu-item-trailing .md3-text-title.md3-text-small{margin:0;padding:0;white-space:nowrap;letter-spacing:.007143em}:host .md3-menu-item-container .md3-menu-item-trailing ::ng-deep [md3-icon-element]{font-size:1.25em!important;height:1em!important;width:1em!important;line-height:1em!important}:host .md3-menu-item-container .md3-menu-item-content{flex:1 1 auto}:host .md3-menu-item-container .md3-menu-item-content .md3-text-label.md3-text-large{display:block;margin:0;padding:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;letter-spacing:.007143em;color:var(--menu-item-label-color)}:host .md3-menu-item-container .md3-menu-item-content .md3-text-body.md3-text-small{display:block;margin:0;padding:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;letter-spacing:.0333334em;color:var(--menu-item-supporting-color)}:host [md3-input-element]{position:absolute;height:0;width:0;opacity:0;pointer-events:none}:host [md3-input-element]+.md3-menu-item-container .md3-menu-item-leading{opacity:0;transform:scale(1.2);width:0}:host [md3-input-element]:checked+.md3-menu-item-container{--menu-item-label-color: var(--menu-item-selected-foreground);--menu-item-supporting-color: var(--menu-item-selected-foreground);--border-radius: var(--md-border-radius-medium);background-color:var(--menu-item-selected-background)}:host [md3-input-element]:checked+.md3-menu-item-container .md3-menu-item-leading{opacity:1;transform:scale(1);width:1.25em}:host:disabled{cursor:default}:host:disabled .md3-menu-item-container{--menu-item-label-color: var(--menu-item-label-color);--menu-item-supporting-color: var(--menu-item-label-color);--menu-item-selected-background: transparent;--menu-item-selected-foreground: var(--menu-item-label-color);pointer-events:none;opacity:.38}:host:disabled .md3-menu-item-container.md3-state-layer,:host:disabled .md3-menu-item-container:after{display:none}\n"] }]
6105
+ }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], supportingText: [{ type: i0.Input, args: [{ isSignal: true, alias: "supporting-text", required: false }] }], trailingText: [{ type: i0.Input, args: [{ isSignal: true, alias: "trailing-text", required: false }] }], selected: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }, { type: i0.Output, args: ["selectedChange"] }], iconElements: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => IconElement), { isSignal: true }] }] } });
6089
6106
 
6090
- /**
6091
- * Material 3 full screen dialog shell. It covers the viewport and holds the content.
6092
- *
6093
- * The content is an `md3-layout`. The layout places the regions — the bar rows for an app bar
6094
- * or a toolbar, the rail columns, and the panes where side sheets open — so the dialog itself
6095
- * places nothing but the content. A content root that is not a layout gets a warning in
6096
- * development mode, and the shell falls back to panes of its own, so a side sheet never opens
6097
- * behind the dialog where nobody can see it.
6098
- */
6099
- class FullScreenDialog extends CdkDialogContainer {
6100
- surface = viewChild('surface', /* @ts-ignore */
6101
- ...(ngDevMode ? [{ debugName: "surface" }] : /* istanbul ignore next */ []));
6102
- mainPane = viewChild('mainPane', /* @ts-ignore */
6103
- ...(ngDevMode ? [{ debugName: "mainPane" }] : /* istanbul ignore next */ []));
6104
- startOutlet = viewChild('startOutlet', { ...(ngDevMode ? { debugName: "startOutlet" } : /* istanbul ignore next */ {}), read: CdkPortalOutlet });
6105
- endOutlet = viewChild('endOutlet', { ...(ngDevMode ? { debugName: "endOutlet" } : /* istanbul ignore next */ {}), read: CdkPortalOutlet });
6106
- sheets = inject(SheetsService);
6107
- /** True while the shell holds the side sheet outlets, which happens without a layout only. */
6108
- ownsSideSheetOutlets = false;
6109
- isActive = signal(false, /* @ts-ignore */
6110
- ...(ngDevMode ? [{ debugName: "isActive" }] : /* istanbul ignore next */ []));
6111
- config = inject(DIALOG_CONFIG, { optional: true }) ?? {};
6112
- get surfaceElement() {
6113
- return this.surface()?.nativeElement ?? null;
6114
- }
6115
- ngAfterViewInit() {
6116
- // The content is attached by now, so the layout it brings has already taken the
6117
- // outlets. Its panes sit under the bar regions, which is where a side sheet belongs.
6118
- const layout = this.findContentLayout();
6119
- if (typeof ngDevMode === 'undefined' || ngDevMode) {
6120
- this.checkContentLayout(layout);
6121
- }
6122
- if (layout) {
6107
+ class SelectOptions {
6108
+ options = input.required(/* @ts-ignore */
6109
+ ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
6110
+ multiple = input(false, /* @ts-ignore */
6111
+ ...(ngDevMode ? [{ debugName: "multiple" }] : /* istanbul ignore next */ []));
6112
+ /** Emits the values selected by the user. The field owns the selection state. */
6113
+ selectionChange = output();
6114
+ optionClick(index) {
6115
+ const options = this.options();
6116
+ const option = options.at(index);
6117
+ if (!option) {
6123
6118
  return;
6124
6119
  }
6125
- // No layout, so the shell keeps the dialog self-sufficient. Without this the next side
6126
- // sheet would open in the outlet underneath — the page scaffold, behind the dialog.
6127
- const startOutlet = this.startOutlet();
6128
- const endOutlet = this.endOutlet();
6129
- if (startOutlet) {
6130
- this.sheets.registerSideSheetOutlet('start', startOutlet);
6131
- }
6132
- if (endOutlet) {
6133
- this.sheets.registerSideSheetOutlet('end', endOutlet);
6120
+ if (this.multiple()) {
6121
+ option.selected = !(option.selected ?? false);
6134
6122
  }
6135
- this.ownsSideSheetOutlets = !!startOutlet || !!endOutlet;
6136
- }
6137
- ngOnDestroy() {
6138
- if (this.ownsSideSheetOutlets) {
6139
- const startOutlet = this.startOutlet();
6140
- const endOutlet = this.endOutlet();
6141
- if (startOutlet) {
6142
- this.sheets.unregisterSideSheetOutlet('start', startOutlet);
6143
- }
6144
- if (endOutlet) {
6145
- this.sheets.unregisterSideSheetOutlet('end', endOutlet);
6146
- }
6147
- this.ownsSideSheetOutlets = false;
6123
+ else {
6124
+ options.forEach((item, i) => {
6125
+ item.selected = i == index;
6126
+ });
6148
6127
  }
6149
- super.ngOnDestroy();
6128
+ this.selectionChange.emit(options.filter((item) => item.selected).map((item) => item.value));
6150
6129
  }
6130
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: SelectOptions, deps: [], target: i0.ɵɵFactoryTarget.Component });
6131
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: SelectOptions, isStandalone: true, selector: "md3-select-options", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectionChange: "selectionChange" }, ngImport: i0, template: "<md3-menu-group class=\"md3-scrollable\">\n @for (item of options(); track `${$index}-${item.value}`) {\n <button\n md3-menu-item\n [selected]=\"item.selected ?? false\"\n [label]=\"item.label\"\n [supporting-text]=\"item.supportingText ?? null\"\n [trailing-text]=\"item.trailingText ?? null\"\n (click)=\"optionClick($index)\"\n >\n </button>\n }\n</md3-menu-group>", styles: [":host{max-height:inherit}:host md3-menu-group.md3-scrollable{padding:.5em .375em;max-height:inherit;overflow-y:auto}\n"], dependencies: [{ kind: "component", type: MenuGroup, selector: "md3-menu-group" }, { kind: "component", type: MenuItem, selector: "button[md3-menu-item], a[md3-menu-item]", inputs: ["label", "supporting-text", "trailing-text", "selected"], outputs: ["selectedChange"] }] });
6132
+ }
6133
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: SelectOptions, decorators: [{
6134
+ type: Component,
6135
+ args: [{ selector: 'md3-select-options', imports: [
6136
+ MenuGroup,
6137
+ MenuItem,
6138
+ ], template: "<md3-menu-group class=\"md3-scrollable\">\n @for (item of options(); track `${$index}-${item.value}`) {\n <button\n md3-menu-item\n [selected]=\"item.selected ?? false\"\n [label]=\"item.label\"\n [supporting-text]=\"item.supportingText ?? null\"\n [trailing-text]=\"item.trailingText ?? null\"\n (click)=\"optionClick($index)\"\n >\n </button>\n }\n</md3-menu-group>", styles: [":host{max-height:inherit}:host md3-menu-group.md3-scrollable{padding:.5em .375em;max-height:inherit;overflow-y:auto}\n"] }]
6139
+ }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }] } });
6140
+
6141
+ const MENU_DATA = new InjectionToken('MD3_MENU_DATA');
6142
+ const MENU_CONFIG = new InjectionToken('MD3_MENU_CONFIG');
6143
+ const MENU_COMPONENT = new InjectionToken('MD3_MENU_COMPONENT');
6144
+ const MENU_EXIT_ANIMATION_FALLBACK_MS = 300;
6145
+ class MenuRef {
6146
+ overlayRef;
6147
+ previouslyFocusedElement;
6148
+ closed = new Subject();
6149
+ isClosed = false;
6150
+ menuInstance;
6151
6151
  /**
6152
- * The layout at the root of the content, if the content brought one.
6153
- *
6154
- * The root is either the layout itself, or the component that holds it — a dialog is
6155
- * normally opened with a component whose own template starts with `md3-layout`.
6152
+ * Filled by MenuService after the user component is attached. Keeping the
6153
+ * instance here lets callers imperatively update inputs when that is useful.
6156
6154
  */
6157
- findContentLayout() {
6158
- const content = this.mainPane()?.nativeElement.firstElementChild;
6159
- if (!content) {
6160
- return null;
6161
- }
6162
- if (content.tagName === 'MD3-LAYOUT') {
6163
- return content;
6164
- }
6165
- const root = content.firstElementChild;
6166
- return root?.tagName === 'MD3-LAYOUT' ? root : null;
6155
+ componentInstance;
6156
+ constructor(overlayRef, previouslyFocusedElement) {
6157
+ this.overlayRef = overlayRef;
6158
+ this.previouslyFocusedElement = previouslyFocusedElement;
6167
6159
  }
6168
- checkContentLayout(layout) {
6169
- if (layout) {
6160
+ close(result) {
6161
+ if (this.isClosed) {
6170
6162
  return;
6171
6163
  }
6172
- console.warn('[md3-fullscreen-dialog] the content of a full screen dialog should start with '
6173
- + 'md3-layout. Without it the dialog has no bar and no rail region, so an app bar or '
6174
- + 'a toolbar has nowhere to go, and the content does not scroll on its own.');
6175
- }
6176
- startEnterAnimation() {
6177
- requestAnimationFrame(() => {
6178
- this.isActive.set(true);
6164
+ this.isClosed = true;
6165
+ this.startCloseAnimation().then(() => {
6166
+ // Disposing the overlay removes the wrapper, the backdrop, and the
6167
+ // dynamically attached content component in one operation.
6168
+ this.overlayRef.dispose();
6169
+ // this.previouslyFocusedElement?.focus();
6170
+ this.closed.next(result);
6171
+ this.closed.complete();
6179
6172
  });
6180
6173
  }
6181
- setActive(value) {
6182
- this.isActive.set(value);
6174
+ startCloseAnimation() {
6175
+ let panel = this.overlayRef.overlayElement;
6176
+ let animatedElement = panel.querySelector('.md3-menu-container') ?? panel;
6177
+ panel.classList.add('md3-menu-closing');
6178
+ this.menuInstance?.showMenu(false);
6179
+ return new Promise((resolve) => {
6180
+ let isResolved = false;
6181
+ let timeoutId = setTimeout(done, MENU_EXIT_ANIMATION_FALLBACK_MS);
6182
+ function done() {
6183
+ if (isResolved) {
6184
+ return;
6185
+ }
6186
+ isResolved = true;
6187
+ clearTimeout(timeoutId);
6188
+ animatedElement.removeEventListener('transitionend', onTransitionEnd);
6189
+ resolve();
6190
+ }
6191
+ function onTransitionEnd(event) {
6192
+ if (event.target === animatedElement) {
6193
+ done();
6194
+ }
6195
+ }
6196
+ animatedElement.addEventListener('transitionend', onTransitionEnd);
6197
+ });
6183
6198
  }
6184
- recaptureFocus() {
6185
- this._recaptureFocus();
6199
+ afterClosed() {
6200
+ return this.closed.asObservable();
6186
6201
  }
6187
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: FullScreenDialog, deps: null, target: i0.ɵɵFactoryTarget.Component });
6188
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.0", type: FullScreenDialog, isStandalone: true, selector: "md3-fullscreen-dialog", viewQueries: [{ propertyName: "surface", first: true, predicate: ["surface"], descendants: true, isSignal: true }, { propertyName: "mainPane", first: true, predicate: ["mainPane"], descendants: true, isSignal: true }, { propertyName: "startOutlet", first: true, predicate: ["startOutlet"], descendants: true, read: CdkPortalOutlet, isSignal: true }, { propertyName: "endOutlet", first: true, predicate: ["endOutlet"], descendants: true, read: CdkPortalOutlet, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<!-- The role and aria attributes live on the host element, applied by the CDK dialog container. -->\n<div\n #surface\n class=\"md3-fullscreen-dialog-container\"\n [class.md3-active]=\"isActive()\"\n [class.md-scheme-dark]=\"config.scheme == 'dark'\"\n [class.md-scheme-light]=\"config.scheme == 'light'\"\n [attr.dir]=\"config.direction ?? null\">\n <div class=\"md3-panes-container md3-scrollable\">\n <!--\n The CDK attaches the dialog content to the first portal outlet of\n this template, so the main pane has to come first. The panes are\n placed by grid area, which makes the DOM order irrelevant visually.\n -->\n <div class=\"md3-pane md3-pane-main\" #mainPane>\n <ng-template cdkPortalOutlet></ng-template>\n </div>\n <div class=\"md3-pane md3-pane-start\">\n <ng-template #startOutlet cdkPortalOutlet></ng-template>\n </div>\n <div class=\"md3-pane md3-pane-end\">\n <ng-template #endOutlet cdkPortalOutlet></ng-template>\n </div>\n </div>\n</div>\n", styles: ["::ng-deep .cdk-overlay-container{position:fixed;z-index:1000;inset:0;pointer-events:none;font-size:1rem}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper{position:absolute;display:flex;inset:0;pointer-events:none;overflow:hidden}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper:has(>.md3-fullscreen-dialog-panel){padding:0}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-fullscreen-dialog-panel{width:100%;height:100%;max-width:none;pointer-events:auto}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-fullscreen-dialog-panel.md3-dialog-closing,::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-fullscreen-dialog-panel.md3-dialog-hidden{pointer-events:none}:host{display:block;height:100%;width:100%;outline:0}:host .md3-fullscreen-dialog-container{display:grid;grid-template-rows:minmax(0,1fr);height:100%;min-height:0;overflow:hidden;color:rgb(var(--md-scheme-on-surface));background-color:rgb(var(--md-scheme-surface));transition:all var(--md-motion-expressive-default-spatial-duration) var(--md-motion-expressive-default-spatial-easing);opacity:0;transform:translateY(1.5em)}:host .md3-fullscreen-dialog-container.md3-active{opacity:1;transform:none}:host .md3-panes-container{display:grid;grid-template-areas:\"start main end\";grid-template-columns:auto minmax(0,1fr) auto;grid-template-rows:minmax(0,1fr);min-width:0;min-height:0;overflow:hidden}:host .md3-panes-container .md3-pane{justify-self:stretch;align-self:stretch;min-width:0;min-height:0;height:100%;overflow:hidden}:host .md3-panes-container .md3-pane.md3-pane-start{grid-area:start}:host .md3-panes-container .md3-pane.md3-pane-main{position:relative;grid-area:main;z-index:1;display:grid;grid-template-rows:minmax(0,1fr);grid-template-columns:minmax(0,1fr)}:host .md3-panes-container .md3-pane.md3-pane-main>::ng-deep *{min-width:0;min-height:0;height:100%;max-height:100dvh;max-width:100dvw}:host .md3-panes-container .md3-pane.md3-pane-end{grid-area:end}\n"], dependencies: [{ kind: "directive", type: CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }] });
6189
6202
  }
6190
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: FullScreenDialog, decorators: [{
6191
- type: Component,
6192
- args: [{ selector: 'md3-fullscreen-dialog', imports: [
6193
- CdkPortalOutlet,
6194
- ], template: "<!-- The role and aria attributes live on the host element, applied by the CDK dialog container. -->\n<div\n #surface\n class=\"md3-fullscreen-dialog-container\"\n [class.md3-active]=\"isActive()\"\n [class.md-scheme-dark]=\"config.scheme == 'dark'\"\n [class.md-scheme-light]=\"config.scheme == 'light'\"\n [attr.dir]=\"config.direction ?? null\">\n <div class=\"md3-panes-container md3-scrollable\">\n <!--\n The CDK attaches the dialog content to the first portal outlet of\n this template, so the main pane has to come first. The panes are\n placed by grid area, which makes the DOM order irrelevant visually.\n -->\n <div class=\"md3-pane md3-pane-main\" #mainPane>\n <ng-template cdkPortalOutlet></ng-template>\n </div>\n <div class=\"md3-pane md3-pane-start\">\n <ng-template #startOutlet cdkPortalOutlet></ng-template>\n </div>\n <div class=\"md3-pane md3-pane-end\">\n <ng-template #endOutlet cdkPortalOutlet></ng-template>\n </div>\n </div>\n</div>\n", styles: ["::ng-deep .cdk-overlay-container{position:fixed;z-index:1000;inset:0;pointer-events:none;font-size:1rem}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper{position:absolute;display:flex;inset:0;pointer-events:none;overflow:hidden}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper:has(>.md3-fullscreen-dialog-panel){padding:0}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-fullscreen-dialog-panel{width:100%;height:100%;max-width:none;pointer-events:auto}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-fullscreen-dialog-panel.md3-dialog-closing,::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-fullscreen-dialog-panel.md3-dialog-hidden{pointer-events:none}:host{display:block;height:100%;width:100%;outline:0}:host .md3-fullscreen-dialog-container{display:grid;grid-template-rows:minmax(0,1fr);height:100%;min-height:0;overflow:hidden;color:rgb(var(--md-scheme-on-surface));background-color:rgb(var(--md-scheme-surface));transition:all var(--md-motion-expressive-default-spatial-duration) var(--md-motion-expressive-default-spatial-easing);opacity:0;transform:translateY(1.5em)}:host .md3-fullscreen-dialog-container.md3-active{opacity:1;transform:none}:host .md3-panes-container{display:grid;grid-template-areas:\"start main end\";grid-template-columns:auto minmax(0,1fr) auto;grid-template-rows:minmax(0,1fr);min-width:0;min-height:0;overflow:hidden}:host .md3-panes-container .md3-pane{justify-self:stretch;align-self:stretch;min-width:0;min-height:0;height:100%;overflow:hidden}:host .md3-panes-container .md3-pane.md3-pane-start{grid-area:start}:host .md3-panes-container .md3-pane.md3-pane-main{position:relative;grid-area:main;z-index:1;display:grid;grid-template-rows:minmax(0,1fr);grid-template-columns:minmax(0,1fr)}:host .md3-panes-container .md3-pane.md3-pane-main>::ng-deep *{min-width:0;min-height:0;height:100%;max-height:100dvh;max-width:100dvw}:host .md3-panes-container .md3-pane.md3-pane-end{grid-area:end}\n"] }]
6195
- }], propDecorators: { surface: [{ type: i0.ViewChild, args: ['surface', { isSignal: true }] }], mainPane: [{ type: i0.ViewChild, args: ['mainPane', { isSignal: true }] }], startOutlet: [{ type: i0.ViewChild, args: ['startOutlet', { ...{ read: CdkPortalOutlet }, isSignal: true }] }], endOutlet: [{ type: i0.ViewChild, args: ['endOutlet', { ...{ read: CdkPortalOutlet }, isSignal: true }] }] } });
6196
6203
 
6197
- const PANEL_CLASS = 'md3-dialog-panel';
6198
- const FULL_SCREEN_PANEL_CLASS = 'md3-fullscreen-dialog-panel';
6199
- const OPENING_CLASS = 'md3-dialog-opening';
6200
- const SCRIM_KEPT_CLASS = 'md3-dialog-scrim-kept';
6201
- const SCRIM_INSTANT_CLASS = 'md3-dialog-scrim-instant';
6202
- /**
6203
- * Head start given to a dialog that leaves the screen before the dialog taking
6204
- * its place animates in. Long enough to read as one dialog replacing another,
6205
- * short enough to still feel like a single gesture.
6206
- */
6207
- const DIALOG_HANDOVER_DELAY_MS = 160;
6208
- class DialogService {
6209
- cdkDialog = inject(Dialog$1);
6210
- overlay = inject(Overlay);
6211
- overlayContainer = inject(OverlayContainer);
6212
- injector = inject(Injector);
6213
- document = inject(DOCUMENT);
6214
- /** Open dialogs, from the first one opened to the one currently on top. */
6215
- refs = [];
6216
- /**
6217
- * Elements the page was made of that the CDK hid from assistive technology,
6218
- * kept here while the dialogs are hidden and the page is given back.
6219
- */
6220
- pageAriaHidden = new Map();
6221
- /**
6222
- * Page scrolling is blocked here instead of per overlay, so stacked dialogs
6223
- * cannot unblock the page while another dialog is still open.
6224
- */
6225
- scrollBlock = this.overlay.scrollStrategies.block();
6226
- /** Element that was focused before the first dialog of the stack opened. */
6227
- rootTrigger = null;
6228
- pageStateQueued = false;
6229
- get openDialogs() {
6230
- return this.refs;
6231
- }
6232
- /** Open dialogs that are currently on screen, from the bottom one up. */
6233
- get visibleDialogs() {
6234
- return this.refs.filter((ref) => !ref.isHidden);
6235
- }
6236
- /** The full screen dialog that is open, if there is one. */
6237
- get fullScreenDialog() {
6238
- return this.refs.find((ref) => ref.isFullScreen && !ref.isClosing);
6204
+ class Menu {
6205
+ el;
6206
+ portalOutlet = viewChild(CdkPortalOutlet, /* @ts-ignore */
6207
+ ...(ngDevMode ? [{ debugName: "portalOutlet" }] : /* istanbul ignore next */ []));
6208
+ config = inject(MENU_CONFIG, { optional: true }) ?? {};
6209
+ menuColors = signal('standard', /* @ts-ignore */
6210
+ ...(ngDevMode ? [{ debugName: "menuColors" }] : /* istanbul ignore next */ []));
6211
+ isVisible = signal(false, /* @ts-ignore */
6212
+ ...(ngDevMode ? [{ debugName: "isVisible" }] : /* istanbul ignore next */ []));
6213
+ scheme = signal('inherit', /* @ts-ignore */
6214
+ ...(ngDevMode ? [{ debugName: "scheme" }] : /* istanbul ignore next */ []));
6215
+ direction = signal(null, /* @ts-ignore */
6216
+ ...(ngDevMode ? [{ debugName: "direction" }] : /* istanbul ignore next */ []));
6217
+ isActive = signal(true, /* @ts-ignore */
6218
+ ...(ngDevMode ? [{ debugName: "isActive" }] : /* istanbul ignore next */ []));
6219
+ get element() {
6220
+ return this.el.nativeElement;
6239
6221
  }
6240
- open(component, config = {}) {
6241
- const dialogConfig = this.mergeConfig(config);
6242
- if (this.refs.length === 0) {
6243
- this.rootTrigger = this.getFocusedElement();
6244
- }
6245
- // The dialog that is on screen starts leaving first, so the two dialogs
6246
- // never animate on top of each other. A full screen dialog is not part
6247
- // of this: regular dialogs open on top of it and leave it alone.
6248
- const previousRef = this.topRegularRef();
6249
- if (previousRef) {
6250
- this.dismissPrevious(previousRef, dialogConfig.previousDialog);
6251
- }
6252
- let dialogRef;
6253
- const cdkRef = this.cdkDialog.open(component, {
6254
- // The MD3 shell replaces the default CDK container, so the overlay,
6255
- // focus trap and focus restoration come from the CDK while the shell
6256
- // only renders the surface.
6257
- container: {
6258
- type: Dialog,
6259
- providers: () => [
6260
- { provide: DIALOG_CONFIG, useValue: dialogConfig },
6261
- ],
6262
- },
6263
- // These tokens make the dialog controllable from the dynamic
6264
- // component without coupling that component to the service.
6265
- providers: (cdkDialogRef) => {
6266
- dialogRef = new DialogRef(cdkDialogRef, dialogConfig.disableCloseEvents);
6267
- return [
6268
- { provide: DialogRef, useValue: dialogRef },
6269
- { provide: DIALOG_DATA, useValue: dialogConfig.data },
6270
- { provide: DIALOG_CONFIG, useValue: dialogConfig },
6271
- { provide: DIALOG_COMPONENT, useValue: component },
6272
- ];
6273
- },
6274
- data: dialogConfig.data,
6275
- role: dialogConfig.role,
6276
- ariaModal: true,
6277
- ariaLabel: dialogConfig.ariaLabel || null,
6278
- ariaLabelledBy: dialogConfig.ariaLabelledBy || null,
6279
- ariaDescribedBy: dialogConfig.ariaDescribedBy || null,
6280
- direction: dialogConfig.direction ?? undefined,
6281
- viewContainerRef: dialogConfig.viewContainerRef,
6282
- injector: dialogConfig.injector,
6283
- hasBackdrop: true,
6284
- backdropClass: ['md3-dialog-scrim', OPENING_CLASS],
6285
- panelClass: [PANEL_CLASS, OPENING_CLASS],
6286
- positionStrategy: this.overlay.position()
6287
- .global()
6288
- .centerHorizontally()
6289
- .centerVertically(),
6290
- // Scrolling is blocked by the service for the whole stack.
6291
- scrollStrategy: this.overlay.scrollStrategies.noop(),
6292
- // Focus goes back to whatever opened the stack, even when the dialog
6293
- // that was open underneath has been closed in the meantime.
6294
- restoreFocus: this.rootTrigger ?? true,
6295
- // Scrim and Escape handling lives in DialogRef so the dialog can
6296
- // play its exit animation before the overlay is disposed.
6297
- disableClose: true,
6222
+ constructor(el) {
6223
+ this.el = el;
6224
+ effect((onCleanup) => {
6225
+ const color = 'md3-' + this.menuColors();
6226
+ this.element.classList.add(color);
6227
+ onCleanup(() => {
6228
+ this.element.classList.remove(color);
6229
+ });
6298
6230
  });
6299
- this.registerDialog(dialogRef, cdkRef, dialogConfig, previousRef);
6300
- return dialogRef;
6231
+ this.menuColors.set(this.config.menuColors ?? 'standard');
6232
+ this.scheme.set(this.config.scheme ?? 'inherit');
6233
+ this.direction.set(this.config.direction ?? null);
6301
6234
  }
6302
- /**
6303
- * Opens a dialog that covers the whole screen. Only one can be open at a
6304
- * time, so it replaces whatever is open, and while it is up the side sheet
6305
- * outlets belong to it: side sheets open inside the dialog, not behind it.
6306
- * `previousDialog` decides what happens to the dialogs it replaces, the
6307
- * same way it does for a regular dialog.
6308
- */
6309
- openFullScreen(component, config = {}) {
6310
- const dialogConfig = this.mergeConfig(config);
6311
- if (this.refs.length === 0) {
6312
- this.rootTrigger = this.getFocusedElement();
6313
- }
6314
- // A full screen dialog is a context of its own: the dialog it replaces
6315
- // and anything stacked on that dialog goes with it.
6316
- const previousRef = this.topRef();
6317
- if (previousRef) {
6318
- // Everything open belongs to the context being replaced, including
6319
- // a full screen dialog that is already up.
6320
- this.dismissPreviousContext(dialogConfig.previousDialog);
6321
- }
6322
- let dialogRef;
6323
- const cdkRef = this.cdkDialog.open(component, {
6324
- container: {
6325
- type: FullScreenDialog,
6326
- providers: () => [
6327
- { provide: DIALOG_CONFIG, useValue: dialogConfig },
6328
- ],
6329
- },
6330
- providers: (cdkDialogRef) => {
6331
- dialogRef = new DialogRef(cdkDialogRef, dialogConfig.disableCloseEvents, true);
6332
- return [
6333
- { provide: DialogRef, useValue: dialogRef },
6334
- { provide: DIALOG_DATA, useValue: dialogConfig.data },
6335
- { provide: DIALOG_CONFIG, useValue: dialogConfig },
6336
- { provide: DIALOG_COMPONENT, useValue: component },
6337
- ];
6338
- },
6339
- data: dialogConfig.data,
6340
- role: dialogConfig.role,
6341
- ariaModal: true,
6342
- ariaLabel: dialogConfig.ariaLabel || null,
6343
- ariaLabelledBy: dialogConfig.ariaLabelledBy || null,
6344
- ariaDescribedBy: dialogConfig.ariaDescribedBy || null,
6345
- direction: dialogConfig.direction ?? undefined,
6346
- viewContainerRef: dialogConfig.viewContainerRef,
6347
- injector: dialogConfig.injector,
6348
- // The dialog covers the page on its own, so there is nothing left
6349
- // for a scrim to dim.
6350
- hasBackdrop: false,
6351
- panelClass: [FULL_SCREEN_PANEL_CLASS, OPENING_CLASS],
6352
- // The CDK writes the size on the panel and the global strategy then
6353
- // drops its centering margins, which is how the panel ends up
6354
- // covering the whole wrapper.
6355
- width: '100%',
6356
- height: '100%',
6357
- positionStrategy: this.overlay.position().global(),
6358
- scrollStrategy: this.overlay.scrollStrategies.noop(),
6359
- restoreFocus: this.rootTrigger ?? true,
6360
- disableClose: true,
6235
+ ngAfterContentInit() {
6236
+ requestAnimationFrame(() => {
6237
+ this.showMenu(true);
6361
6238
  });
6362
- this.registerDialog(dialogRef, cdkRef, dialogConfig, previousRef);
6363
- return dialogRef;
6364
- }
6365
- /** Wires a freshly created dialog into the stack and starts its animation. */
6366
- registerDialog(dialogRef, cdkRef, dialogConfig, previousRef) {
6367
- dialogRef.dialogInstance = cdkRef.containerInstance;
6368
- dialogRef.componentInstance = cdkRef.componentInstance ?? undefined;
6369
- if (cdkRef.componentRef) {
6370
- this.bindDataToInputs(cdkRef.componentRef, dialogConfig);
6371
- }
6372
- this.refs.push(dialogRef);
6373
- this.syncPageState();
6374
- if (previousRef) {
6375
- setTimeout(() => this.startReplacingAnimation(dialogRef, previousRef), DIALOG_HANDOVER_DELAY_MS);
6376
- }
6377
- else {
6378
- this.startOpenAnimation(dialogRef);
6379
- }
6380
- // Restoring starts with the exit animation, so the dialog underneath is
6381
- // already coming back while this one is fading out.
6382
- dialogRef.beforeClosed().subscribe(() => this.restorePreviousDialog(dialogRef));
6383
- dialogRef.afterClosed().subscribe(() => this.removeRef(dialogRef));
6384
- // Hiding and showing can also come straight from the reference, so the
6385
- // page state is driven by what the dialogs report rather than by the
6386
- // calls that went through this service.
6387
- dialogRef.hiddenChanged().subscribe(() => this.syncPageState());
6388
6239
  }
6389
- /**
6390
- * Hides every dialog that is on screen, the full screen one included, with
6391
- * the closing animation. They stay alive with their content and state, the
6392
- * page underneath becomes usable, and showAll() puts them back. The promise
6393
- * resolves once they are all out of sight.
6394
- */
6395
- hideAll() {
6396
- const visible = this.refs.filter((ref) => !ref.isHidden && !ref.isClosing);
6397
- return Promise.all(visible.map((ref) => ref.hide())).then(() => undefined);
6240
+ showMenu(value) {
6241
+ this.isVisible.set(value);
6398
6242
  }
6399
6243
  /**
6400
- * Brings the dialogs back on screen, whether they were hidden one by one or
6401
- * all at once: the dialog that was on top returns, along with the full
6402
- * screen dialog it sits in. Dialogs hidden behind another dialog stay
6403
- * hidden, since that is where they belong once the stack is back.
6244
+ * The service creates this wrapper first, then calls this method to mount
6245
+ * the user supplied component into the dialog container.
6404
6246
  */
6405
- showAll() {
6406
- const topRef = this.topRef();
6407
- if (!topRef) {
6408
- return;
6409
- }
6410
- const fullScreenRef = this.fullScreenDialog;
6411
- // The full screen dialog is the context the top one sits in, so it
6412
- // comes back first and the dialog on top ends up on top, with the focus.
6413
- if (fullScreenRef && fullScreenRef !== topRef && fullScreenRef.isHidden) {
6414
- fullScreenRef.show();
6415
- }
6416
- if (topRef.isHidden) {
6417
- topRef.show();
6418
- }
6247
+ attachContent(component, injector) {
6248
+ const portal = new ComponentPortal(component, null, injector);
6249
+ return this.portalOutlet().attachComponentPortal(portal);
6419
6250
  }
6420
- /** Closes every open dialog, including the hidden and the full screen ones. */
6421
- closeAll() {
6422
- // Closed from the bottom up so a closing dialog never brings back a
6423
- // hidden dialog that is about to close as well.
6424
- return Promise.all([...this.refs].map((ref) => ref.close())).then(() => undefined);
6251
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Menu, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
6252
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.0", type: Menu, isStandalone: true, selector: "md3-menu", host: { attributes: { "role": "menu" }, properties: { "class.md3-visible": "isVisible()", "class.md3-inactive": "!isActive()", "class.md-scheme-dark": "scheme() == \"dark\"", "class.md-scheme-light": "scheme() == \"light\"", "dir": "direction()" }, classAttribute: "md3-menu-container" }, viewQueries: [{ propertyName: "portalOutlet", first: true, predicate: CdkPortalOutlet, descendants: true, isSignal: true }], ngImport: i0, template: "<ng-template cdkPortalOutlet></ng-template>", styles: ["::ng-deep .cdk-overlay-container{position:fixed;z-index:1000;inset:0;pointer-events:none;font-size:1rem}::ng-deep .cdk-overlay-container .md3-menu-scrim{position:absolute;inset:0;pointer-events:auto}::ng-deep .cdk-overlay-container .cdk-overlay-connected-position-bounding-box{pointer-events:none}::ng-deep .cdk-overlay-container .md3-menu-panel{pointer-events:auto}::ng-deep .cdk-overlay-container .md3-menu-panel.md3-select-menu>.md3-menu-container{--menu-min-width: 100%;--menu-max-width: 100%}::ng-deep .cdk-overlay-container .md3-menu-panel.md3-menu-closing{pointer-events:none}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper{position:absolute;display:flex;inset:0;pointer-events:none;padding:3.5rem 0;overflow:hidden}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-menu-panel{position:absolute;pointer-events:auto}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-menu-panel.md3-menu-closing{pointer-events:none}:host{--background-color: rgb(var(--md-scheme-surface-container-low));--foreground-color: rgb(var(--md-scheme-on-surface));--border-radius: var(--md-border-radius-large);--menu-min-width: 7em;--menu-max-width: 17.5em;--menu-group-radius: var(--md-border-radius-small);--menu-group-shadow: var(--md-shadow-3dp);--menu-group-label-color: rgb(var(--md-scheme-on-surface-variant));--menu-item-label-color: rgb(var(--md-scheme-on-surface));--menu-item-supporting-color: rgb(var(--md-scheme-on-surface-variant));--menu-item-selected-background: rgb(var(--md-scheme-tertiary-container));--menu-item-selected-foreground: rgb(var(--md-scheme-on-tertiary-container));--menu-item-state-color: var(--md-scheme-on-surface);--menu-item-border-radius: var(--md-border-radius-xsmall);--menu-item-outline-color: rgb(var(--md-scheme-secondary));--menu-item-outline-width: .1875em;--menu-item-outline-offset: -.1875em;--menu-item-badge-background: rgb(var(--md-scheme-primary-container));--menu-item-badge-foreground: rgb(var(--md-scheme-on-primary-container));position:relative;display:block;font-size:1rem;min-width:var(--menu-min-width);max-width:min(var(--menu-max-width),100vw - 2rem);max-height:min(32rem,100vh - 2rem);outline:none;transition:all var(--md-motion-expressive-default-spatial-duration) var(--md-motion-expressive-default-spatial-easing);opacity:0;transform:scaleY(.8)}:host.md3-visible{opacity:1;transform:scaleY(1)}:host.md3-vibrant{--background-color: rgb(var(--md-scheme-tertiary-container));--foreground-color: rgb(var(--md-scheme-on-tertiary-container));--menu-group-label-color: rgb(var(--md-scheme-on-tertiary-container));--menu-item-label-color: rgb(var(--md-scheme-on-tertiary-container));--menu-item-supporting-color: rgb(var(--md-scheme-on-tertiary-container));--menu-item-selected-background: rgb(var(--md-scheme-tertiary));--menu-item-selected-foreground: rgb(var(--md-scheme-on-tertiary));--menu-item-state-color: var(--md-scheme-on-tertiary-container)}:host.md3-inactive{--border-radius: var(--md-border-radius-small)}:host ::ng-deep md3-menu-group{margin-top:.125em}:host ::ng-deep md3-menu-group:first-child{margin-top:0}\n"], dependencies: [{ kind: "directive", type: CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }] });
6253
+ }
6254
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Menu, decorators: [{
6255
+ type: Component,
6256
+ args: [{ selector: 'md3-menu', imports: [
6257
+ CdkPortalOutlet,
6258
+ ], host: {
6259
+ 'class': 'md3-menu-container',
6260
+ 'role': 'menu',
6261
+ '[class.md3-visible]': 'isVisible()',
6262
+ '[class.md3-inactive]': '!isActive()',
6263
+ '[class.md-scheme-dark]': 'scheme() == "dark"',
6264
+ '[class.md-scheme-light]': 'scheme() == "light"',
6265
+ '[dir]': 'direction()',
6266
+ }, template: "<ng-template cdkPortalOutlet></ng-template>", styles: ["::ng-deep .cdk-overlay-container{position:fixed;z-index:1000;inset:0;pointer-events:none;font-size:1rem}::ng-deep .cdk-overlay-container .md3-menu-scrim{position:absolute;inset:0;pointer-events:auto}::ng-deep .cdk-overlay-container .cdk-overlay-connected-position-bounding-box{pointer-events:none}::ng-deep .cdk-overlay-container .md3-menu-panel{pointer-events:auto}::ng-deep .cdk-overlay-container .md3-menu-panel.md3-select-menu>.md3-menu-container{--menu-min-width: 100%;--menu-max-width: 100%}::ng-deep .cdk-overlay-container .md3-menu-panel.md3-menu-closing{pointer-events:none}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper{position:absolute;display:flex;inset:0;pointer-events:none;padding:3.5rem 0;overflow:hidden}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-menu-panel{position:absolute;pointer-events:auto}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-menu-panel.md3-menu-closing{pointer-events:none}:host{--background-color: rgb(var(--md-scheme-surface-container-low));--foreground-color: rgb(var(--md-scheme-on-surface));--border-radius: var(--md-border-radius-large);--menu-min-width: 7em;--menu-max-width: 17.5em;--menu-group-radius: var(--md-border-radius-small);--menu-group-shadow: var(--md-shadow-3dp);--menu-group-label-color: rgb(var(--md-scheme-on-surface-variant));--menu-item-label-color: rgb(var(--md-scheme-on-surface));--menu-item-supporting-color: rgb(var(--md-scheme-on-surface-variant));--menu-item-selected-background: rgb(var(--md-scheme-tertiary-container));--menu-item-selected-foreground: rgb(var(--md-scheme-on-tertiary-container));--menu-item-state-color: var(--md-scheme-on-surface);--menu-item-border-radius: var(--md-border-radius-xsmall);--menu-item-outline-color: rgb(var(--md-scheme-secondary));--menu-item-outline-width: .1875em;--menu-item-outline-offset: -.1875em;--menu-item-badge-background: rgb(var(--md-scheme-primary-container));--menu-item-badge-foreground: rgb(var(--md-scheme-on-primary-container));position:relative;display:block;font-size:1rem;min-width:var(--menu-min-width);max-width:min(var(--menu-max-width),100vw - 2rem);max-height:min(32rem,100vh - 2rem);outline:none;transition:all var(--md-motion-expressive-default-spatial-duration) var(--md-motion-expressive-default-spatial-easing);opacity:0;transform:scaleY(.8)}:host.md3-visible{opacity:1;transform:scaleY(1)}:host.md3-vibrant{--background-color: rgb(var(--md-scheme-tertiary-container));--foreground-color: rgb(var(--md-scheme-on-tertiary-container));--menu-group-label-color: rgb(var(--md-scheme-on-tertiary-container));--menu-item-label-color: rgb(var(--md-scheme-on-tertiary-container));--menu-item-supporting-color: rgb(var(--md-scheme-on-tertiary-container));--menu-item-selected-background: rgb(var(--md-scheme-tertiary));--menu-item-selected-foreground: rgb(var(--md-scheme-on-tertiary));--menu-item-state-color: var(--md-scheme-on-tertiary-container)}:host.md3-inactive{--border-radius: var(--md-border-radius-small)}:host ::ng-deep md3-menu-group{margin-top:.125em}:host ::ng-deep md3-menu-group:first-child{margin-top:0}\n"] }]
6267
+ }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { portalOutlet: [{ type: i0.ViewChild, args: [i0.forwardRef(() => CdkPortalOutlet), { isSignal: true }] }] } });
6268
+
6269
+ class MenuService {
6270
+ overlay = inject(Overlay);
6271
+ injector = inject(Injector);
6272
+ document = inject(DOCUMENT);
6273
+ open(component, config = {}) {
6274
+ const menuConfig = this.mergeConfig(config);
6275
+ const previouslyFocusedElement = this.getFocusedElement();
6276
+ const triggerElement = this.resolveTriggerElement(menuConfig.origin, previouslyFocusedElement);
6277
+ const menuOverlay = this.createOverlay(menuConfig, previouslyFocusedElement);
6278
+ const overlayRef = menuOverlay.overlayRef;
6279
+ const menuRef = new MenuRef(overlayRef, previouslyFocusedElement);
6280
+ const injector = this.createInjector(component, menuConfig, menuRef);
6281
+ // The overlay hosts the MD3 menu shell. The shell is responsible for
6282
+ // the surface markup and receives the dynamic content through a portal.
6283
+ const menuPortal = new ComponentPortal(Menu, menuConfig.viewContainerRef ?? null, injector);
6284
+ const menuComponentRef = overlayRef.attach(menuPortal);
6285
+ const contentComponentRef = menuComponentRef.instance.attachContent(component, injector);
6286
+ this.bindDataToInputs(contentComponentRef, menuConfig);
6287
+ this.flipPositionIfPushBreaksAnchor(menuOverlay, menuConfig);
6288
+ menuRef.componentInstance = contentComponentRef.instance;
6289
+ menuRef.menuInstance = menuComponentRef.instance;
6290
+ this.bindTriggerActiveState(triggerElement, overlayRef, menuRef);
6291
+ this.startOpenAnimation(overlayRef);
6292
+ this.connectCloseEvents(overlayRef, menuRef, menuOverlay.origin);
6293
+ return menuRef;
6425
6294
  }
6426
- /** Closes the regular dialogs and leaves a full screen dialog alone. */
6427
- closeRegularDialogs() {
6428
- const closing = this.refs
6429
- .filter((ref) => !ref.isFullScreen)
6430
- .map((ref) => ref.close());
6431
- return Promise.all(closing).then(() => undefined);
6295
+ openSubMenu(component, config = {}) {
6296
+ const xPosition = this.getSubMenuXPosition(config.xPosition);
6297
+ const yPosition = config.yPosition ?? 'below';
6298
+ const offsetX = config.offsetX ?? 0;
6299
+ const offsetY = config.offsetY ?? 0;
6300
+ const subMenuConfig = {
6301
+ ...config,
6302
+ isSubMenu: true,
6303
+ xPosition,
6304
+ yPosition,
6305
+ overlapTrigger: config.overlapTrigger ?? true,
6306
+ offsetX,
6307
+ offsetY,
6308
+ scheme: config.scheme ?? 'inherit',
6309
+ direction: config.direction ?? null,
6310
+ };
6311
+ return this.open(component, subMenuConfig);
6432
6312
  }
6433
6313
  mergeConfig(config) {
6314
+ const viewportMargin = this.getViewportMargin(config.viewportMargin);
6434
6315
  return {
6435
6316
  data: config.data,
6436
6317
  bindDataToInputs: config.bindDataToInputs ?? false,
6437
- disableCloseEvents: config.disableCloseEvents ?? false,
6438
- previousDialog: config.previousDialog ?? 'close',
6439
- role: config.role ?? 'dialog',
6440
- ariaLabel: config.ariaLabel ?? '',
6441
- ariaLabelledBy: config.ariaLabelledBy ?? '',
6442
- ariaDescribedBy: config.ariaDescribedBy ?? '',
6318
+ isSubMenu: config.isSubMenu ?? false,
6319
+ menuColors: config.menuColors ?? 'standard',
6320
+ origin: config.origin,
6321
+ xPosition: config.xPosition ?? 'start',
6322
+ yPosition: config.yPosition ?? 'below',
6323
+ overlapTrigger: config.overlapTrigger ?? false,
6324
+ offsetX: config.offsetX ?? 0,
6325
+ offsetY: config.offsetY ?? 4,
6326
+ viewportMargin,
6327
+ positions: config.positions,
6328
+ scrollStrategy: config.scrollStrategy ?? 'reposition',
6443
6329
  scheme: config.scheme ?? 'inherit',
6444
6330
  direction: config.direction ?? null,
6445
6331
  viewContainerRef: config.viewContainerRef,
6446
6332
  injector: config.injector ?? this.injector,
6447
6333
  };
6448
6334
  }
6449
- /**
6450
- * Hides or closes the dialog that is on screen. Its scrim stays up until
6451
- * the dialog taking over is ready for it, so the page behind never
6452
- * brightens between the two dialogs.
6453
- */
6454
- dismissPrevious(previousRef, behavior) {
6455
- previousRef.overlayRef.backdropElement?.classList.add(SCRIM_KEPT_CLASS);
6456
- return behavior === 'hide' ? previousRef.hide() : this.closeRegularDialogs();
6457
- }
6458
- /**
6459
- * The same choice for a full screen dialog, applied to everything that is
6460
- * on screen: what it takes over from is a whole context, a full screen
6461
- * dialog of its own included. `hide` keeps that context alive, and it comes
6462
- * back as it was once this dialog closes.
6463
- */
6464
- dismissPreviousContext(behavior) {
6465
- return behavior === 'hide' ? this.hideAll() : this.closeAll();
6335
+ isSelectInput(origin) {
6336
+ return origin?.tagName.toLowerCase() == 'input' && origin?.classList.contains('md3-select-input');
6466
6337
  }
6467
- startOpenAnimation(dialogRef) {
6468
- const overlayRef = dialogRef.overlayRef;
6469
- const panel = overlayRef.overlayElement;
6470
- const backdrop = overlayRef.backdropElement;
6471
- // The overlay is inserted already rendered. Keep an initial class for
6472
- // one frame, force style calculation, then remove it so CSS transitions
6473
- // have a real from/to state.
6474
- requestAnimationFrame(() => {
6475
- setTimeout(() => {
6476
- panel.getBoundingClientRect();
6477
- panel.classList.remove(OPENING_CLASS);
6478
- backdrop?.classList.remove(OPENING_CLASS);
6479
- }, 100);
6338
+ createOverlay(config, fallbackOrigin) {
6339
+ const origin = this.resolveOrigin(config.origin, fallbackOrigin);
6340
+ const positionStrategy = this.overlay.position()
6341
+ .flexibleConnectedTo(origin)
6342
+ .withFlexibleDimensions(false)
6343
+ .withGrowAfterOpen(true)
6344
+ .withPush(true)
6345
+ .withViewportMargin(config.viewportMargin)
6346
+ .withPositions(config.positions ?? this.getInitialConnectedPositions(config))
6347
+ .withTransformOriginOn('.md3-menu-container');
6348
+ const isSelectInput = this.isSelectInput(origin);
6349
+ const originRect = this.getOriginClientRect(origin);
6350
+ const minWidth = !isSelectInput ? undefined : originRect.width;
6351
+ const overlayConfig = new OverlayConfig({
6352
+ hasBackdrop: !isSelectInput,
6353
+ backdropClass: ['md3-menu-scrim', 'md3-menu-opening'],
6354
+ panelClass: ['md3-menu-panel', 'md3-menu-opening', ...(isSelectInput ? ['md3-select-menu'] : [])],
6355
+ positionStrategy,
6356
+ scrollStrategy: this.getScrollStrategy(config.scrollStrategy),
6357
+ minWidth: minWidth,
6480
6358
  });
6481
- dialogRef.dialogInstance?.startEnterAnimation();
6359
+ return {
6360
+ overlayRef: this.overlay.create(overlayConfig),
6361
+ positionStrategy: config.positions ? undefined : positionStrategy,
6362
+ origin,
6363
+ };
6482
6364
  }
6483
- /** Opening animation for a dialog that took the place of another one. */
6484
- startReplacingAnimation(dialogRef, previousRef) {
6485
- if (dialogRef.isClosing) {
6365
+ bindTriggerActiveState(triggerElement, overlayRef, menuRef) {
6366
+ if (!triggerElement) {
6486
6367
  return;
6487
6368
  }
6488
- const panel = dialogRef.overlayRef.overlayElement;
6489
- const backdrop = dialogRef.overlayRef.backdropElement;
6490
- this.beginScrimHandover(previousRef, dialogRef);
6491
- panel.getBoundingClientRect();
6492
- panel.classList.remove(OPENING_CLASS);
6493
- backdrop?.classList.remove(OPENING_CLASS);
6494
- this.endScrimHandover(previousRef, dialogRef);
6495
- dialogRef.dialogInstance?.startEnterAnimation();
6369
+ const removeActiveClass = () => triggerElement.classList.remove('md3-menu-active');
6370
+ triggerElement.classList.add('md3-menu-active');
6371
+ menuRef.afterClosed().pipe(take(1)).subscribe(removeActiveClass);
6372
+ overlayRef.detachments().pipe(take(1)).subscribe(removeActiveClass);
6496
6373
  }
6497
- /** Opening animation for a dialog coming back from behind a closing one. */
6498
- startRestoringAnimation(closingRef, previousRef) {
6499
- if (previousRef.isClosing) {
6374
+ resolveOrigin(origin, fallbackOrigin) {
6375
+ const resolvedOrigin = origin ?? fallbackOrigin;
6376
+ if (resolvedOrigin) {
6377
+ if (this.isMouseEvent(resolvedOrigin)) {
6378
+ if (resolvedOrigin.currentTarget instanceof HTMLElement) {
6379
+ return resolvedOrigin.currentTarget;
6380
+ }
6381
+ return {
6382
+ x: resolvedOrigin.clientX,
6383
+ y: resolvedOrigin.clientY,
6384
+ };
6385
+ }
6386
+ if (this.isElementRef(resolvedOrigin)) {
6387
+ return resolvedOrigin.nativeElement;
6388
+ }
6389
+ return resolvedOrigin;
6390
+ }
6391
+ const viewport = this.document.defaultView;
6392
+ return {
6393
+ x: (viewport?.innerWidth ?? 0) / 2,
6394
+ y: (viewport?.innerHeight ?? 0) / 2,
6395
+ };
6396
+ }
6397
+ resolveTriggerElement(origin, fallbackOrigin) {
6398
+ const resolvedOrigin = origin ?? fallbackOrigin;
6399
+ if (!resolvedOrigin) {
6400
+ return null;
6401
+ }
6402
+ if (this.isMouseEvent(resolvedOrigin)) {
6403
+ return resolvedOrigin.currentTarget instanceof HTMLElement
6404
+ ? resolvedOrigin.currentTarget
6405
+ : null;
6406
+ }
6407
+ if (this.isElementRef(resolvedOrigin)) {
6408
+ return resolvedOrigin.nativeElement;
6409
+ }
6410
+ return resolvedOrigin instanceof HTMLElement ? resolvedOrigin : null;
6411
+ }
6412
+ flipPositionIfPushBreaksAnchor(menuOverlay, config) {
6413
+ if (!menuOverlay.positionStrategy) {
6414
+ return;
6415
+ }
6416
+ menuOverlay.overlayRef.updatePosition();
6417
+ const fallbackPosition = this.getFallbackPositionAfterPush(config, this.getOriginClientRect(menuOverlay.origin), menuOverlay.overlayRef.overlayElement.getBoundingClientRect());
6418
+ if (!fallbackPosition) {
6500
6419
  return;
6501
6420
  }
6502
- this.beginScrimHandover(closingRef, previousRef);
6503
- previousRef.show();
6504
- this.endScrimHandover(closingRef, previousRef);
6421
+ menuOverlay.positionStrategy.withPositions([fallbackPosition]);
6422
+ menuOverlay.overlayRef.updatePosition();
6423
+ }
6424
+ getInitialConnectedPositions(config) {
6425
+ return [this.getPreferredConnectedPosition(config)];
6426
+ }
6427
+ getPreferredConnectedPosition(config) {
6428
+ if (config.isSubMenu) {
6429
+ return this.createSubMenuConnectedPosition(this.getSubMenuXPosition(config.xPosition), this.getSubMenuVerticalAlignment(config.yPosition), config.offsetX, config.offsetY);
6430
+ }
6431
+ return this.createConnectedPosition(config.xPosition, config.yPosition, config);
6432
+ }
6433
+ getFallbackPositionAfterPush(config, originRect, overlayRect) {
6434
+ const xPosition = this.shouldFlipXPosition(config, originRect, overlayRect)
6435
+ ? this.getFallbackXPosition(config.xPosition)
6436
+ : config.xPosition;
6437
+ const yPosition = this.shouldFlipYPosition(config, originRect, overlayRect)
6438
+ ? this.getFallbackYPosition(config.yPosition)
6439
+ : config.yPosition;
6440
+ if (xPosition === config.xPosition && yPosition === config.yPosition) {
6441
+ return null;
6442
+ }
6443
+ return this.getPreferredConnectedPosition({
6444
+ ...config,
6445
+ xPosition,
6446
+ yPosition,
6447
+ });
6448
+ }
6449
+ shouldFlipXPosition(config, originRect, overlayRect) {
6450
+ const tolerance = config.viewportMargin;
6451
+ switch (config.xPosition) {
6452
+ case 'before':
6453
+ return overlayRect.right > originRect.left + tolerance;
6454
+ case 'after':
6455
+ return overlayRect.left < originRect.right - tolerance;
6456
+ case 'start':
6457
+ return originRect.left < overlayRect.left - tolerance
6458
+ || originRect.left > overlayRect.right + tolerance;
6459
+ case 'end':
6460
+ return originRect.right < overlayRect.left - tolerance
6461
+ || originRect.right > overlayRect.right + tolerance;
6462
+ case 'center':
6463
+ default:
6464
+ return false;
6465
+ }
6466
+ }
6467
+ shouldFlipYPosition(config, originRect, overlayRect) {
6468
+ const tolerance = config.viewportMargin;
6469
+ if (config.isSubMenu) {
6470
+ const anchorY = config.yPosition === 'above' ? originRect.bottom : originRect.top;
6471
+ return anchorY < overlayRect.top - tolerance
6472
+ || anchorY > overlayRect.bottom + tolerance;
6473
+ }
6474
+ if (config.overlapTrigger) {
6475
+ const anchorY = config.yPosition === 'above' ? originRect.bottom : originRect.top;
6476
+ return anchorY < overlayRect.top - tolerance
6477
+ || anchorY > overlayRect.bottom + tolerance;
6478
+ }
6479
+ switch (config.yPosition) {
6480
+ case 'above':
6481
+ return overlayRect.bottom > originRect.top + tolerance;
6482
+ case 'below':
6483
+ return overlayRect.top < originRect.bottom - tolerance;
6484
+ case 'center':
6485
+ default:
6486
+ return false;
6487
+ }
6488
+ }
6489
+ createConnectedPosition(xPosition, yPosition, config) {
6490
+ const horizontal = this.getHorizontalPosition(xPosition);
6491
+ const vertical = this.getVerticalPosition(yPosition, config.overlapTrigger);
6492
+ return {
6493
+ ...horizontal,
6494
+ ...vertical,
6495
+ offsetX: config.offsetX,
6496
+ offsetY: this.getOffsetY(yPosition, config),
6497
+ panelClass: [
6498
+ 'md3-menu-position-' + xPosition,
6499
+ 'md3-menu-position-' + yPosition,
6500
+ ],
6501
+ };
6502
+ }
6503
+ createSubMenuConnectedPosition(xPosition, yAlignment, offsetX, offsetY) {
6504
+ const horizontal = this.getHorizontalPosition(xPosition);
6505
+ const yPosition = yAlignment === 'top' ? 'below' : 'above';
6506
+ return {
6507
+ ...horizontal,
6508
+ originY: yAlignment,
6509
+ overlayY: yAlignment,
6510
+ offsetX: this.getSubMenuOffsetX(xPosition, offsetX),
6511
+ offsetY: this.getSubMenuOffsetY(yAlignment, offsetY),
6512
+ panelClass: [
6513
+ 'md3-menu-position-' + xPosition,
6514
+ 'md3-menu-position-' + yPosition,
6515
+ 'md3-menu-position-submenu',
6516
+ ],
6517
+ };
6518
+ }
6519
+ getHorizontalPosition(position) {
6520
+ switch (position) {
6521
+ case 'end':
6522
+ return {
6523
+ originX: 'end',
6524
+ overlayX: 'end',
6525
+ };
6526
+ case 'before':
6527
+ return {
6528
+ originX: 'start',
6529
+ overlayX: 'end',
6530
+ };
6531
+ case 'after':
6532
+ return {
6533
+ originX: 'end',
6534
+ overlayX: 'start',
6535
+ };
6536
+ case 'center':
6537
+ return {
6538
+ originX: 'center',
6539
+ overlayX: 'center',
6540
+ };
6541
+ case 'start':
6542
+ default:
6543
+ return {
6544
+ originX: 'start',
6545
+ overlayX: 'start',
6546
+ };
6547
+ }
6505
6548
  }
6506
- /**
6507
- * Moves both scrims to their new value in a single frame, without a
6508
- * transition on either side, so the dim behind the dialogs never changes.
6509
- */
6510
- beginScrimHandover(fromRef, toRef) {
6511
- const from = fromRef.overlayRef.backdropElement;
6512
- const to = toRef.overlayRef.backdropElement;
6513
- // Nothing to hand the scrim over to, e.g. a full screen dialog: the
6514
- // outgoing scrim fades out on its own instead.
6515
- if (!to) {
6516
- return;
6549
+ getVerticalPosition(position, overlapTrigger) {
6550
+ switch (position) {
6551
+ case 'above':
6552
+ return {
6553
+ originY: overlapTrigger ? 'bottom' : 'top',
6554
+ overlayY: 'bottom',
6555
+ };
6556
+ case 'center':
6557
+ return {
6558
+ originY: 'center',
6559
+ overlayY: 'center',
6560
+ };
6561
+ case 'below':
6562
+ default:
6563
+ return {
6564
+ originY: overlapTrigger ? 'top' : 'bottom',
6565
+ overlayY: 'top',
6566
+ };
6517
6567
  }
6518
- // Nothing to hand over either: the dialog leaving carries no scrim, so
6519
- // the one coming in fades its own in rather than switching it on in a
6520
- // single frame. This is a full screen dialog closing over a dialog that
6521
- // was hidden underneath it.
6522
- if (!from) {
6523
- return;
6568
+ }
6569
+ getFallbackXPosition(position) {
6570
+ switch (position) {
6571
+ case 'start':
6572
+ return 'end';
6573
+ case 'end':
6574
+ return 'start';
6575
+ case 'before':
6576
+ return 'after';
6577
+ case 'after':
6578
+ return 'before';
6579
+ case 'center':
6580
+ default:
6581
+ return 'center';
6524
6582
  }
6525
- from?.classList.add(SCRIM_INSTANT_CLASS);
6526
- from?.classList.remove(SCRIM_KEPT_CLASS);
6527
- to?.classList.add(SCRIM_INSTANT_CLASS);
6528
6583
  }
6529
- /** Gives the scrims their transitions back once the handover is painted. */
6530
- endScrimHandover(fromRef, toRef) {
6531
- const from = fromRef.overlayRef.backdropElement;
6532
- const to = toRef.overlayRef.backdropElement;
6533
- requestAnimationFrame(() => {
6534
- from?.classList.remove(SCRIM_INSTANT_CLASS);
6535
- to?.classList.remove(SCRIM_INSTANT_CLASS);
6536
- });
6584
+ getSubMenuXPosition(position) {
6585
+ return position === 'before' ? 'before' : 'after';
6537
6586
  }
6538
- bindDataToInputs(componentRef, config) {
6539
- if (!config.bindDataToInputs || !this.canBindDataToInputs(config.data)) {
6540
- return;
6587
+ getFallbackYPosition(position) {
6588
+ switch (position) {
6589
+ case 'above':
6590
+ return 'below';
6591
+ case 'below':
6592
+ return 'above';
6593
+ case 'center':
6594
+ default:
6595
+ return 'center';
6541
6596
  }
6542
- // setInput runs Angular's normal input pipeline, so input setters,
6543
- // transforms, signal inputs, and change detection are handled for us.
6544
- Object.entries(config.data).forEach(([inputName, inputValue]) => {
6545
- componentRef.setInput(inputName, inputValue);
6546
- });
6547
6597
  }
6548
- canBindDataToInputs(data) {
6549
- return typeof data === 'object' && data !== null && !Array.isArray(data);
6598
+ getSubMenuVerticalAlignment(position) {
6599
+ return position === 'above' ? 'bottom' : 'top';
6550
6600
  }
6551
- /** Last dialog of the stack that is not closing yet. */
6552
- topRef() {
6553
- for (let index = this.refs.length - 1; index >= 0; index--) {
6554
- if (!this.refs[index].isClosing) {
6555
- return this.refs[index];
6556
- }
6601
+ getOriginClientRect(origin) {
6602
+ if ('getBoundingClientRect' in origin) {
6603
+ return origin.getBoundingClientRect();
6557
6604
  }
6558
- return undefined;
6605
+ return {
6606
+ x: origin.x,
6607
+ y: origin.y,
6608
+ top: origin.y,
6609
+ right: origin.x,
6610
+ bottom: origin.y,
6611
+ left: origin.x,
6612
+ width: 0,
6613
+ height: 0,
6614
+ };
6559
6615
  }
6560
- /**
6561
- * Same, without the full screen dialog: it hosts the dialogs opened on top
6562
- * of it, so it is never the one that gets hidden or closed to make room.
6563
- */
6564
- topRegularRef() {
6565
- const ref = this.topRef();
6566
- return ref?.isFullScreen ? undefined : ref;
6616
+ getViewportMargin(configuredMargin) {
6617
+ const minimumMargin = this.getHalfEmInPixels();
6618
+ return Math.max(configuredMargin ?? minimumMargin, minimumMargin);
6567
6619
  }
6568
- restorePreviousDialog(ref) {
6569
- const index = this.refs.indexOf(ref);
6570
- if (index < 1) {
6571
- return;
6572
- }
6573
- for (let previousIndex = index - 1; previousIndex >= 0; previousIndex--) {
6574
- const previousRef = this.refs[previousIndex];
6575
- if (previousRef.isClosing) {
6576
- continue;
6577
- }
6578
- // The closing dialog gets the same head start it was given when it
6579
- // took the screen, and keeps the scrim until then. Nothing to keep
6580
- // when the dialog underneath has no scrim of its own.
6581
- if (previousRef.overlayRef.backdropElement) {
6582
- ref.overlayRef.backdropElement?.classList.add(SCRIM_KEPT_CLASS);
6583
- }
6584
- setTimeout(() => {
6585
- this.showHostFullScreenDialog(previousRef);
6586
- this.startRestoringAnimation(ref, previousRef);
6587
- }, DIALOG_HANDOVER_DELAY_MS);
6588
- return;
6589
- }
6620
+ getHalfEmInPixels() {
6621
+ const viewport = this.document.defaultView;
6622
+ const fontSize = viewport?.getComputedStyle(this.document.documentElement).fontSize;
6623
+ const pixels = fontSize ? Number.parseFloat(fontSize) * 0.5 : 8;
6624
+ return Number.isFinite(pixels) ? pixels : 8;
6590
6625
  }
6591
- /**
6592
- * Brings back the full screen dialog a restored dialog sits in. The two are
6593
- * hidden together when a full screen dialog takes over with
6594
- * `previousDialog: 'hide'`, so the context has to be on screen again before
6595
- * the dialog that sits on top of it, exactly as showAll() puts them back.
6596
- */
6597
- showHostFullScreenDialog(ref) {
6598
- if (ref.isFullScreen) {
6599
- return;
6600
- }
6601
- const hostRef = this.fullScreenDialog;
6602
- if (hostRef?.isHidden && this.refs.indexOf(hostRef) < this.refs.indexOf(ref)) {
6603
- hostRef.show();
6626
+ getOffsetY(position, config) {
6627
+ if (config.overlapTrigger || position === 'center') {
6628
+ return 0;
6604
6629
  }
6630
+ return position === 'above'
6631
+ ? config.offsetY * -1
6632
+ : config.offsetY;
6605
6633
  }
6606
- removeRef(ref) {
6607
- const index = this.refs.indexOf(ref);
6608
- if (index === -1) {
6609
- return;
6610
- }
6611
- this.refs.splice(index, 1);
6612
- if (this.refs.length === 0) {
6613
- this.rootTrigger = null;
6614
- }
6615
- this.syncPageState();
6634
+ getSubMenuOffsetX(position, offsetX) {
6635
+ return position === 'before' ? offsetX * -1 : offsetX;
6616
6636
  }
6617
- /**
6618
- * State that belongs to the dialogs as a whole rather than to one of them:
6619
- * page scrolling, the page being hidden from assistive technology, and
6620
- * where focus sits. Applied in a task of its own, so a dialog handing the
6621
- * screen over to another one does not give the page back and take it again
6622
- * within the same frame.
6623
- */
6624
- syncPageState() {
6625
- if (this.pageStateQueued) {
6626
- return;
6627
- }
6628
- this.pageStateQueued = true;
6629
- setTimeout(() => {
6630
- this.pageStateQueued = false;
6631
- this.applyPageState();
6632
- });
6637
+ getSubMenuOffsetY(alignment, offsetY) {
6638
+ return alignment === 'bottom' ? offsetY * -1 : offsetY;
6633
6639
  }
6634
- applyPageState() {
6635
- const hasVisibleDialog = this.refs.some((ref) => !ref.isHidden);
6636
- if (hasVisibleDialog) {
6637
- this.scrollBlock.enable();
6638
- }
6639
- else {
6640
- this.scrollBlock.disable();
6641
- }
6642
- if (this.refs.length === 0) {
6643
- // Nothing is open anymore: the CDK gives the page back to assistive
6644
- // technology on its own, so there is nothing left to restore here.
6645
- this.pageAriaHidden.clear();
6646
- return;
6647
- }
6648
- if (hasVisibleDialog) {
6649
- this.hidePageFromAssistiveTechnology();
6650
- return;
6640
+ getScrollStrategy(scrollStrategy) {
6641
+ switch (scrollStrategy) {
6642
+ case 'block':
6643
+ return this.overlay.scrollStrategies.block();
6644
+ case 'close':
6645
+ return this.overlay.scrollStrategies.close();
6646
+ case 'noop':
6647
+ return this.overlay.scrollStrategies.noop();
6648
+ case 'reposition':
6649
+ default:
6650
+ return this.overlay.scrollStrategies.reposition();
6651
6651
  }
6652
- this.exposePageToAssistiveTechnology();
6653
- this.releaseFocus();
6654
6652
  }
6655
- /**
6656
- * Gives the page back to assistive technology while every dialog is hidden.
6657
- * The CDK hides it for as long as a dialog is open, which would otherwise
6658
- * leave nothing to read at all once the dialogs are out of sight.
6659
- */
6660
- exposePageToAssistiveTechnology() {
6661
- if (this.pageAriaHidden.size > 0) {
6662
- return;
6663
- }
6664
- const container = this.overlayContainer.getContainerElement();
6665
- Array.from(container.parentElement?.children ?? []).forEach((sibling) => {
6666
- const value = sibling.getAttribute('aria-hidden');
6667
- if (sibling === container || value === null) {
6668
- return;
6669
- }
6670
- this.pageAriaHidden.set(sibling, value);
6671
- sibling.removeAttribute('aria-hidden');
6653
+ isMouseEvent(origin) {
6654
+ return typeof MouseEvent !== 'undefined' && origin instanceof MouseEvent;
6655
+ }
6656
+ isElementRef(origin) {
6657
+ return typeof origin === 'object'
6658
+ && origin !== null
6659
+ && 'nativeElement' in origin
6660
+ && origin.nativeElement instanceof HTMLElement;
6661
+ }
6662
+ startOpenAnimation(overlayRef) {
6663
+ const panel = overlayRef.overlayElement;
6664
+ const backdrop = overlayRef.backdropElement;
6665
+ // The overlay is inserted already rendered. Keep an initial class for
6666
+ // one frame, force style calculation, then remove it so CSS transitions
6667
+ // have a real from/to state.
6668
+ requestAnimationFrame(() => {
6669
+ setTimeout(() => {
6670
+ panel.getBoundingClientRect();
6671
+ panel.classList.remove('md3-menu-opening');
6672
+ backdrop?.classList.remove('md3-menu-opening');
6673
+ }, 100);
6674
+ });
6675
+ }
6676
+ createInjector(component, config, menuRef) {
6677
+ return Injector.create({
6678
+ parent: config.injector,
6679
+ providers: [
6680
+ // These tokens make the menu controllable from the dynamic
6681
+ // component without coupling that component to the service.
6682
+ { provide: MenuRef, useValue: menuRef },
6683
+ { provide: MENU_DATA, useValue: config.data },
6684
+ { provide: MENU_CONFIG, useValue: config },
6685
+ { provide: MENU_COMPONENT, useValue: component },
6686
+ ],
6672
6687
  });
6673
6688
  }
6674
- /** Hides the page again as soon as a dialog is back on screen. */
6675
- hidePageFromAssistiveTechnology() {
6676
- this.pageAriaHidden.forEach((value, element) => {
6677
- element.setAttribute('aria-hidden', value);
6689
+ bindDataToInputs(componentRef, config) {
6690
+ if (!config.bindDataToInputs || !this.canBindDataToInputs(config.data)) {
6691
+ return;
6692
+ }
6693
+ // setInput runs Angular's normal input pipeline, so input setters,
6694
+ // transforms, signal inputs, and change detection are handled for us.
6695
+ Object.entries(config.data).forEach(([inputName, inputValue]) => {
6696
+ componentRef.setInput(inputName, inputValue);
6678
6697
  });
6679
- this.pageAriaHidden.clear();
6680
6698
  }
6681
- /**
6682
- * Focus follows the dialogs off the screen: it goes back to whatever opened
6683
- * the stack, so the page stays usable with the keyboard while the dialogs
6684
- * wait behind it. Focus that already moved somewhere else is left alone.
6685
- */
6686
- releaseFocus() {
6687
- const activeElement = this.document.activeElement;
6688
- if (this.rootTrigger && (!activeElement || activeElement === this.document.body)) {
6689
- this.rootTrigger.focus();
6699
+ connectCloseEvents(overlayRef, menuRef, origin) {
6700
+ overlayRef.backdropClick().pipe(take(1)).subscribe(() => menuRef.close());
6701
+ overlayRef.keydownEvents().pipe(filter((event) => event.key === 'Escape'), take(1)).subscribe(() => menuRef.close());
6702
+ if (this.isSelectInput(origin)) {
6703
+ const originRect = this.getOriginClientRect(origin);
6704
+ overlayRef.outsidePointerEvents().pipe(filter((event) => {
6705
+ const clickedInsideOrigin = event.clientX >= originRect.left &&
6706
+ event.clientX <= originRect.right &&
6707
+ event.clientY >= originRect.top &&
6708
+ event.clientY <= originRect.bottom;
6709
+ return !clickedInsideOrigin;
6710
+ })).subscribe(() => menuRef.close());
6690
6711
  }
6691
6712
  }
6692
6713
  getFocusedElement() {
6693
6714
  const activeElement = this.document.activeElement;
6694
6715
  return activeElement instanceof HTMLElement ? activeElement : null;
6695
6716
  }
6696
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DialogService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
6697
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DialogService, providedIn: 'root' });
6717
+ canBindDataToInputs(data) {
6718
+ return typeof data === 'object' && data !== null && !Array.isArray(data);
6719
+ }
6720
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: MenuService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
6721
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: MenuService, providedIn: 'root' });
6698
6722
  }
6699
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DialogService, decorators: [{
6723
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: MenuService, decorators: [{
6700
6724
  type: Injectable,
6701
6725
  args: [{
6702
6726
  providedIn: 'root',
6703
6727
  }]
6704
6728
  }] });
6705
6729
 
6706
- class DialogHeader {
6707
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DialogHeader, deps: [], target: i0.ɵɵFactoryTarget.Component });
6708
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.0", type: DialogHeader, isStandalone: true, selector: "md3-dialog-header", ngImport: i0, template: "<ng-content select=\"[md3-icon-element]\"></ng-content>\n<div class=\"md3-dialog-title\">\n <md3-type-headline size=\"small\">\n <ng-content></ng-content>\n </md3-type-headline>\n</div>", styles: [":host{display:block;margin:1.5em 1.5em 1em;text-align:center}:host .md3-dialog-title{text-align:start;color:rgb(var(--md-scheme-on-surface))}:host ::ng-deep [md3-icon-element]{color:rgb(var(--md-scheme-secondary))}:host ::ng-deep [md3-icon-element]+.md3-dialog-title{margin-top:1em;text-align:center}\n"], dependencies: [{ kind: "directive", type: TypeHeadline, selector: "md3-type-headline, [md3-type-headline]", inputs: ["size", "color", "emphasized"] }] });
6709
- }
6710
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DialogHeader, decorators: [{
6711
- type: Component,
6712
- args: [{ selector: 'md3-dialog-header', imports: [
6713
- TypeHeadline
6714
- ], template: "<ng-content select=\"[md3-icon-element]\"></ng-content>\n<div class=\"md3-dialog-title\">\n <md3-type-headline size=\"small\">\n <ng-content></ng-content>\n </md3-type-headline>\n</div>", styles: [":host{display:block;margin:1.5em 1.5em 1em;text-align:center}:host .md3-dialog-title{text-align:start;color:rgb(var(--md-scheme-on-surface))}:host ::ng-deep [md3-icon-element]{color:rgb(var(--md-scheme-secondary))}:host ::ng-deep [md3-icon-element]+.md3-dialog-title{margin-top:1em;text-align:center}\n"] }]
6715
- }] });
6716
-
6717
- class DialogBody {
6718
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DialogBody, deps: [], target: i0.ɵɵFactoryTarget.Component });
6719
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.0", type: DialogBody, isStandalone: true, selector: "md3-dialog-body", host: { classAttribute: "md3-scrollable" }, ngImport: i0, template: "<ng-content></ng-content>", styles: [":host{display:block;margin:1em 1.5em 1.5em;color:rgb(var(--md-scheme-on-surface-variant))}:host .md3-text-body.md3-text-medium{letter-spacing:.017857em}\n"] });
6720
- }
6721
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DialogBody, decorators: [{
6722
- type: Component,
6723
- args: [{ selector: 'md3-dialog-body', imports: [], host: {
6724
- 'class': 'md3-scrollable',
6725
- }, template: "<ng-content></ng-content>", styles: [":host{display:block;margin:1em 1.5em 1.5em;color:rgb(var(--md-scheme-on-surface-variant))}:host .md3-text-body.md3-text-medium{letter-spacing:.017857em}\n"] }]
6726
- }] });
6727
-
6728
- class DialogActions {
6729
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DialogActions, deps: [], target: i0.ɵɵFactoryTarget.Component });
6730
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.0", type: DialogActions, isStandalone: true, selector: "md3-dialog-actions", ngImport: i0, template: "<div class=\"md3-actions\">\n <ng-content></ng-content>\n</div>", styles: [":host{display:block;margin:1.5em}:host .md3-actions{display:flex;gap:1em;flex-direction:row-reverse}\n"] });
6731
- }
6732
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DialogActions, decorators: [{
6733
- type: Component,
6734
- args: [{ selector: 'md3-dialog-actions', imports: [], template: "<div class=\"md3-actions\">\n <ng-content></ng-content>\n</div>", styles: [":host{display:block;margin:1.5em}:host .md3-actions{display:flex;gap:1em;flex-direction:row-reverse}\n"] }]
6735
- }] });
6736
-
6737
- const MENU_DATA = new InjectionToken('MD3_MENU_DATA');
6738
- const MENU_CONFIG = new InjectionToken('MD3_MENU_CONFIG');
6739
- const MENU_COMPONENT = new InjectionToken('MD3_MENU_COMPONENT');
6740
- const MENU_EXIT_ANIMATION_FALLBACK_MS = 300;
6741
- class MenuRef {
6742
- overlayRef;
6743
- previouslyFocusedElement;
6744
- closed = new Subject();
6745
- isClosed = false;
6746
- menuInstance;
6730
+ class SelectField {
6731
+ menuService;
6732
+ viewContainerRef;
6747
6733
  /**
6748
- * Filled by MenuService after the user component is attached. Keeping the
6749
- * instance here lets callers imperatively update inputs when that is useful.
6734
+ * Form directive placed on the host element: [formControl], formControlName or [(ngModel)].
6735
+ * The accessor is registered from the constructor instead of through an NG_VALUE_ACCESSOR
6736
+ * provider, which keeps the directive injectable here and gives access to the control
6737
+ * itself for the error state forwarded to md3-text-field.
6750
6738
  */
6751
- componentInstance;
6752
- constructor(overlayRef, previouslyFocusedElement) {
6753
- this.overlayRef = overlayRef;
6754
- this.previouslyFocusedElement = previouslyFocusedElement;
6755
- }
6756
- close(result) {
6757
- if (this.isClosed) {
6758
- return;
6739
+ ngControl = inject(NgControl, { self: true, optional: true });
6740
+ options = input.required(/* @ts-ignore */
6741
+ ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
6742
+ multiple = input(false, { ...(ngDevMode ? { debugName: "multiple" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
6743
+ label = input(null, { ...(ngDevMode ? { debugName: "label" } : /* istanbul ignore next */ {}), alias: 'label' });
6744
+ /**
6745
+ * Formats the text shown in the field. The default joins the labels of the selected
6746
+ * options with a comma, so the field shows as many labels as its width allows.
6747
+ * The formatter is not called for an empty selection: the field stays empty, which keeps
6748
+ * the floating label in its place.
6749
+ */
6750
+ displayWith = input(null, { ...(ngDevMode ? { debugName: "displayWith" } : /* istanbul ignore next */ {}), alias: 'display-with' });
6751
+ /**
6752
+ * Disables the field outside of a form. With reactive forms prefer control.disable().
6753
+ */
6754
+ disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
6755
+ menuColors = input('standard', { ...(ngDevMode ? { debugName: "menuColors" } : /* istanbul ignore next */ {}), alias: 'menu-colors' });
6756
+ fieldType = input('filled', { ...(ngDevMode ? { debugName: "fieldType" } : /* istanbul ignore next */ {}), alias: 'field-type' });
6757
+ /**
6758
+ * Control passed explicitly, mirroring md3-text-field. Takes precedence over a form
6759
+ * directive on the host element.
6760
+ */
6761
+ control = input(undefined, { ...(ngDevMode ? { debugName: "control" } : /* istanbul ignore next */ {}), alias: 'control' });
6762
+ iconElements = contentChildren(IconElement, { ...(ngDevMode ? { debugName: "iconElements" } : /* istanbul ignore next */ {}), descendants: true });
6763
+ hasLeadingIcon = computed(() => this.iconElements().some(i => i.iconType() === 'leading') ?? false, /* @ts-ignore */
6764
+ ...(ngDevMode ? [{ debugName: "hasLeadingIcon" }] : /* istanbul ignore next */ []));
6765
+ hasArrowIcon = computed(() => this.iconElements().some(i => i.iconType() === 'arrow') ?? false, /* @ts-ignore */
6766
+ ...(ngDevMode ? [{ debugName: "hasArrowIcon" }] : /* istanbul ignore next */ []));
6767
+ isMenuOpen = signal(false, /* @ts-ignore */
6768
+ ...(ngDevMode ? [{ debugName: "isMenuOpen" }] : /* istanbul ignore next */ []));
6769
+ menuRef = null;
6770
+ inputElement = viewChild(InputElement, { ...(ngDevMode ? { debugName: "inputElement" } : /* istanbul ignore next */ {}), read: ElementRef });
6771
+ /** Control resolved from the host form directive, captured when the accessor is wired up. */
6772
+ hostControl = signal(undefined, /* @ts-ignore */
6773
+ ...(ngDevMode ? [{ debugName: "hostControl" }] : /* istanbul ignore next */ []));
6774
+ /** Disabled state pushed by the control, either through setDisabledState or control.disabled. */
6775
+ controlDisabled = signal(false, /* @ts-ignore */
6776
+ ...(ngDevMode ? [{ debugName: "controlDisabled" }] : /* istanbul ignore next */ []));
6777
+ /**
6778
+ * Set as soon as a control writes a value. From then on the control owns the selection and
6779
+ * SelectOption.selected no longer seeds it.
6780
+ */
6781
+ controlOwnsValue = false;
6782
+ onChange = () => { };
6783
+ onTouched = () => { };
6784
+ /** Selection is kept by value so it survives options arriving or being replaced later. */
6785
+ selectedValues = signal([], /* @ts-ignore */
6786
+ ...(ngDevMode ? [{ debugName: "selectedValues" }] : /* istanbul ignore next */ []));
6787
+ formControl = computed(() => this.control() ?? this.hostControl(), /* @ts-ignore */
6788
+ ...(ngDevMode ? [{ debugName: "formControl" }] : /* istanbul ignore next */ []));
6789
+ isDisabled = computed(() => this.disabled() || this.controlDisabled(), /* @ts-ignore */
6790
+ ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
6791
+ selectedOptions = computed(() => {
6792
+ const values = this.selectedValues();
6793
+ return this.options().filter((option) => values.includes(option.value));
6794
+ }, /* @ts-ignore */
6795
+ ...(ngDevMode ? [{ debugName: "selectedOptions" }] : /* istanbul ignore next */ []));
6796
+ selectedText = computed(() => {
6797
+ const selected = this.selectedOptions();
6798
+ if (selected.length == 0) {
6799
+ return null;
6759
6800
  }
6760
- this.isClosed = true;
6761
- this.startCloseAnimation().then(() => {
6762
- // Disposing the overlay removes the wrapper, the backdrop, and the
6763
- // dynamically attached content component in one operation.
6764
- this.overlayRef.dispose();
6765
- this.previouslyFocusedElement?.focus();
6766
- this.closed.next(result);
6767
- this.closed.complete();
6768
- });
6769
- }
6770
- startCloseAnimation() {
6771
- let panel = this.overlayRef.overlayElement;
6772
- let animatedElement = panel.querySelector('.md3-menu-container') ?? panel;
6773
- panel.classList.add('md3-menu-closing');
6774
- this.menuInstance?.showMenu(false);
6775
- return new Promise((resolve) => {
6776
- let isResolved = false;
6777
- let timeoutId = setTimeout(done, MENU_EXIT_ANIMATION_FALLBACK_MS);
6778
- function done() {
6779
- if (isResolved) {
6801
+ const format = this.displayWith();
6802
+ if (format) {
6803
+ return format(selected);
6804
+ }
6805
+ return selected.map((option) => option.label).join(', ');
6806
+ }, /* @ts-ignore */
6807
+ ...(ngDevMode ? [{ debugName: "selectedText" }] : /* istanbul ignore next */ []));
6808
+ constructor(menuService, viewContainerRef) {
6809
+ this.menuService = menuService;
6810
+ this.viewContainerRef = viewContainerRef;
6811
+ if (this.ngControl) {
6812
+ this.ngControl.valueAccessor = this;
6813
+ }
6814
+ // Seeds the selection from SelectOption.selected while no control owns the value.
6815
+ effect(() => {
6816
+ const options = this.options();
6817
+ untracked(() => {
6818
+ if (this.controlOwnsValue) {
6780
6819
  return;
6781
6820
  }
6782
- isResolved = true;
6783
- clearTimeout(timeoutId);
6784
- animatedElement.removeEventListener('transitionend', onTransitionEnd);
6785
- resolve();
6786
- }
6787
- function onTransitionEnd(event) {
6788
- if (event.target === animatedElement) {
6789
- done();
6821
+ let selected = options.filter((option) => option.selected);
6822
+ if (!this.multiple() && selected.length > 1) {
6823
+ selected = [selected[0]];
6790
6824
  }
6825
+ this.setSelectedValues(selected.map((option) => option.value));
6826
+ });
6827
+ });
6828
+ // Mirrors the selection back onto SelectOption.selected so the menu renders it.
6829
+ effect(() => {
6830
+ const values = this.selectedValues();
6831
+ for (const option of this.options()) {
6832
+ option.selected = values.includes(option.value);
6791
6833
  }
6792
- animatedElement.addEventListener('transitionend', onTransitionEnd);
6834
+ });
6835
+ // [control] is bound manually; [formControl]/formControlName go through the accessor API.
6836
+ effect((onCleanup) => {
6837
+ const control = this.control();
6838
+ if (!control) {
6839
+ return;
6840
+ }
6841
+ this.syncFromControl(control);
6842
+ const controlEvents = control.events.subscribe(() => this.syncFromControl(control));
6843
+ onCleanup(() => controlEvents.unsubscribe());
6844
+ });
6845
+ effect(() => {
6846
+ const isOpen = this.isMenuOpen();
6847
+ untracked(() => {
6848
+ if (isOpen) {
6849
+ this.openSelectOptions();
6850
+ return;
6851
+ }
6852
+ this.menuRef?.close();
6853
+ this.menuRef = null;
6854
+ });
6793
6855
  });
6794
6856
  }
6795
- afterClosed() {
6796
- return this.closed.asObservable();
6857
+ // ControlValueAccessor
6858
+ writeValue(value) {
6859
+ this.captureHostControl();
6860
+ this.controlOwnsValue = true;
6861
+ this.setSelectedValues(this.normalizeValues(value));
6862
+ }
6863
+ registerOnChange(fn) {
6864
+ this.captureHostControl();
6865
+ this.onChange = fn;
6866
+ }
6867
+ registerOnTouched(fn) {
6868
+ this.onTouched = fn;
6869
+ }
6870
+ setDisabledState(isDisabled) {
6871
+ this.controlDisabled.set(isDisabled);
6872
+ }
6873
+ // Menu
6874
+ openMenu() {
6875
+ if (this.isDisabled()) {
6876
+ return;
6877
+ }
6878
+ this.isMenuOpen.set(true);
6879
+ }
6880
+ closeMenu() {
6881
+ this.isMenuOpen.set(false);
6882
+ }
6883
+ toggleMenu() {
6884
+ if (this.isDisabled()) {
6885
+ return;
6886
+ }
6887
+ this.isMenuOpen.update(current => !current);
6888
+ }
6889
+ openSelectOptions() {
6890
+ this.menuRef = this.menuService.open(SelectOptions, {
6891
+ data: {
6892
+ options: this.options(),
6893
+ multiple: this.multiple(),
6894
+ },
6895
+ bindDataToInputs: true,
6896
+ menuColors: this.menuColors(),
6897
+ origin: this.inputElement(),
6898
+ xPosition: 'start',
6899
+ yPosition: 'below',
6900
+ overlapTrigger: false,
6901
+ // scrollStrategy: 'close',
6902
+ viewContainerRef: this.viewContainerRef,
6903
+ });
6904
+ this.menuRef.componentInstance?.selectionChange.subscribe((values) => this.selectValues(values));
6905
+ this.menuRef.afterClosed().subscribe(() => {
6906
+ this.isMenuOpen.set(false);
6907
+ this.markAsTouched();
6908
+ });
6909
+ }
6910
+ // Selection
6911
+ selectValues(values) {
6912
+ this.setSelectedValues(this.normalizeValues(values));
6913
+ this.commitValue();
6914
+ }
6915
+ setSelectedValues(values) {
6916
+ const current = untracked(this.selectedValues);
6917
+ if (current.length === values.length && current.every((value, index) => value === values[index])) {
6918
+ return;
6919
+ }
6920
+ this.selectedValues.set(values);
6921
+ }
6922
+ commitValue() {
6923
+ const value = this.currentValue();
6924
+ // No-op unless a form directive registered an accessor callback.
6925
+ this.onChange(value);
6926
+ const control = this.control();
6927
+ if (control && !this.valuesAreEqual(control.value, value)) {
6928
+ control.markAsDirty();
6929
+ control.setValue(value);
6930
+ }
6931
+ }
6932
+ currentValue() {
6933
+ const values = untracked(this.selectedValues);
6934
+ if (this.multiple()) {
6935
+ return [...values];
6936
+ }
6937
+ return values.length > 0 ? values[0] : null;
6938
+ }
6939
+ markAsTouched() {
6940
+ this.onTouched();
6941
+ const control = this.control();
6942
+ if (control && !control.touched) {
6943
+ control.markAsTouched();
6944
+ }
6945
+ }
6946
+ syncFromControl(control) {
6947
+ this.controlOwnsValue = true;
6948
+ this.setSelectedValues(this.normalizeValues(control.value));
6949
+ this.controlDisabled.set(control.disabled);
6950
+ }
6951
+ captureHostControl() {
6952
+ const control = this.ngControl?.control ?? undefined;
6953
+ if (control && untracked(this.hostControl) !== control) {
6954
+ this.hostControl.set(control);
6955
+ }
6956
+ }
6957
+ /**
6958
+ * Accepts whatever a control holds and reduces it to the option values this field can show:
6959
+ * a single value for single selection, a de-duplicated list for multiple selection.
6960
+ */
6961
+ normalizeValues(value) {
6962
+ const values = (Array.isArray(value) ? value : [value])
6963
+ .filter((item) => typeof item === 'string' || typeof item === 'number');
6964
+ const unique = [...new Set(values)];
6965
+ return this.multiple() ? unique : unique.slice(0, 1);
6966
+ }
6967
+ valuesAreEqual(current, next) {
6968
+ if (Array.isArray(current) && Array.isArray(next)) {
6969
+ return current.length === next.length && current.every((value, index) => value === next[index]);
6970
+ }
6971
+ return current === next;
6797
6972
  }
6973
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: SelectField, deps: [{ token: MenuService }, { token: i0.ViewContainerRef }], target: i0.ɵɵFactoryTarget.Component });
6974
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: SelectField, isStandalone: true, selector: "md3-select-field", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, multiple: { classPropertyName: "multiple", publicName: "multiple", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, displayWith: { classPropertyName: "displayWith", publicName: "display-with", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, menuColors: { classPropertyName: "menuColors", publicName: "menu-colors", isSignal: true, isRequired: false, transformFunction: null }, fieldType: { classPropertyName: "fieldType", publicName: "field-type", isSignal: true, isRequired: false, transformFunction: null }, control: { classPropertyName: "control", publicName: "control", isSignal: true, isRequired: false, transformFunction: null } }, queries: [{ propertyName: "iconElements", predicate: IconElement, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "inputElement", first: true, predicate: InputElement, descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: "<md3-text-field [label]=\"label()\" [field-type]=\"fieldType()\" [control]=\"formControl()\">\n <!-- <md3-icon md3-icon-element=\"leading\">search</md3-icon> -->\n @if (hasLeadingIcon()) {\n <div md3-icon-element=\"leading\">\n <ng-content select=\"[md3-icon-element=leading]\"></ng-content>\n </div>\n }\n\n <input md3-input-element type=\"text\" [readonly]=\"true\" [disabled]=\"isDisabled()\" (click)=\"openMenu()\" class=\"md3-select-input\" [class.md3-menu-active]=\"isMenuOpen()\" [value]=\"selectedText()\">\n\n <button md3-icon-button button-type=\"standard\" [disabled]=\"isDisabled()\" (click)=\"toggleMenu()\" class=\"md3-select-arrow\">\n @if (hasArrowIcon()) {\n <ng-content select=\"[md3-icon-element=arrow]\"></ng-content>\n } @else {\n <md3-icon md3-icon-element>arrow_drop_down</md3-icon>\n }\n </button>\n\n <ng-content select=\"[md3-supporting-text]\" ngProjectAs=\"[md3-supporting-text]\"></ng-content>\n</md3-text-field>\n", styles: [".md3-select-input{text-overflow:ellipsis}.md3-select-input:read-only{cursor:default;-webkit-user-select:none;user-select:none}.md3-select-input~.md3-select-arrow{transition:transform var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing)}.md3-select-input.md3-menu-active:dir(ltr)~.md3-select-arrow{transform:rotate(180deg)}.md3-select-input.md3-menu-active:dir(rtl)~.md3-select-arrow{transform:rotate(-180deg)}\n"], dependencies: [{ kind: "component", type: TextField, selector: "md3-text-field", inputs: ["label", "field-type", "input-counter", "control"] }, { kind: "directive", type: IconElement, selector: "[md3-icon-element]", inputs: ["md3-icon-element"] }, { kind: "component", type: IconButton, selector: "button[md3-icon-button], a[md3-icon-button]", inputs: ["button-size", "button-type", "button-width", "button-squared", "selected"], outputs: ["selectedChange"] }, { kind: "directive", type: InputElement, selector: "[md3-input-element]" }, { kind: "component", type: MaterialIcon, selector: "md3-icon", inputs: ["icon-version", "filled", "icon-style", "icon-size", "bi-directional"] }] });
6798
6975
  }
6976
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: SelectField, decorators: [{
6977
+ type: Component,
6978
+ args: [{ selector: 'md3-select-field', imports: [
6979
+ TextField,
6980
+ IconElement,
6981
+ IconButton,
6982
+ InputElement,
6983
+ MaterialIcon,
6984
+ ], template: "<md3-text-field [label]=\"label()\" [field-type]=\"fieldType()\" [control]=\"formControl()\">\n <!-- <md3-icon md3-icon-element=\"leading\">search</md3-icon> -->\n @if (hasLeadingIcon()) {\n <div md3-icon-element=\"leading\">\n <ng-content select=\"[md3-icon-element=leading]\"></ng-content>\n </div>\n }\n\n <input md3-input-element type=\"text\" [readonly]=\"true\" [disabled]=\"isDisabled()\" (click)=\"openMenu()\" class=\"md3-select-input\" [class.md3-menu-active]=\"isMenuOpen()\" [value]=\"selectedText()\">\n\n <button md3-icon-button button-type=\"standard\" [disabled]=\"isDisabled()\" (click)=\"toggleMenu()\" class=\"md3-select-arrow\">\n @if (hasArrowIcon()) {\n <ng-content select=\"[md3-icon-element=arrow]\"></ng-content>\n } @else {\n <md3-icon md3-icon-element>arrow_drop_down</md3-icon>\n }\n </button>\n\n <ng-content select=\"[md3-supporting-text]\" ngProjectAs=\"[md3-supporting-text]\"></ng-content>\n</md3-text-field>\n", styles: [".md3-select-input{text-overflow:ellipsis}.md3-select-input:read-only{cursor:default;-webkit-user-select:none;user-select:none}.md3-select-input~.md3-select-arrow{transition:transform var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing)}.md3-select-input.md3-menu-active:dir(ltr)~.md3-select-arrow{transform:rotate(180deg)}.md3-select-input.md3-menu-active:dir(rtl)~.md3-select-arrow{transform:rotate(-180deg)}\n"] }]
6985
+ }], ctorParameters: () => [{ type: MenuService }, { type: i0.ViewContainerRef }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], displayWith: [{ type: i0.Input, args: [{ isSignal: true, alias: "display-with", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], menuColors: [{ type: i0.Input, args: [{ isSignal: true, alias: "menu-colors", required: false }] }], fieldType: [{ type: i0.Input, args: [{ isSignal: true, alias: "field-type", required: false }] }], control: [{ type: i0.Input, args: [{ isSignal: true, alias: "control", required: false }] }], iconElements: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => IconElement), { ...{ descendants: true }, isSignal: true }] }], inputElement: [{ type: i0.ViewChild, args: [i0.forwardRef(() => InputElement), { ...{ read: ElementRef }, isSignal: true }] }] } });
6799
6986
 
6800
- class Menu {
6801
- el;
6802
- portalOutlet = viewChild(CdkPortalOutlet, /* @ts-ignore */
6803
- ...(ngDevMode ? [{ debugName: "portalOutlet" }] : /* istanbul ignore next */ []));
6804
- config = inject(MENU_CONFIG, { optional: true }) ?? {};
6805
- menuColors = signal('standard', /* @ts-ignore */
6806
- ...(ngDevMode ? [{ debugName: "menuColors" }] : /* istanbul ignore next */ []));
6807
- isVisible = signal(false, /* @ts-ignore */
6808
- ...(ngDevMode ? [{ debugName: "isVisible" }] : /* istanbul ignore next */ []));
6809
- scheme = signal('inherit', /* @ts-ignore */
6810
- ...(ngDevMode ? [{ debugName: "scheme" }] : /* istanbul ignore next */ []));
6811
- direction = signal(null, /* @ts-ignore */
6812
- ...(ngDevMode ? [{ debugName: "direction" }] : /* istanbul ignore next */ []));
6813
- isActive = signal(true, /* @ts-ignore */
6987
+ /**
6988
+ * Material 3 dialog shell. It extends the CDK dialog container, so focus
6989
+ * trapping, focus restoration and the ARIA attributes are handled by the CDK
6990
+ * while this component only owns the MD3 surface and its animation.
6991
+ */
6992
+ class Dialog extends CdkDialogContainer {
6993
+ surface = viewChild('surface', /* @ts-ignore */
6994
+ ...(ngDevMode ? [{ debugName: "surface" }] : /* istanbul ignore next */ []));
6995
+ isActive = signal(false, /* @ts-ignore */
6814
6996
  ...(ngDevMode ? [{ debugName: "isActive" }] : /* istanbul ignore next */ []));
6815
- get element() {
6816
- return this.el.nativeElement;
6817
- }
6818
- constructor(el) {
6819
- this.el = el;
6820
- effect((onCleanup) => {
6821
- const color = 'md3-' + this.menuColors();
6822
- this.element.classList.add(color);
6823
- onCleanup(() => {
6824
- this.element.classList.remove(color);
6825
- });
6826
- });
6827
- this.menuColors.set(this.config.menuColors ?? 'standard');
6828
- this.scheme.set(this.config.scheme ?? 'inherit');
6829
- this.direction.set(this.config.direction ?? null);
6997
+ config = inject(DIALOG_CONFIG, { optional: true }) ?? {};
6998
+ get surfaceElement() {
6999
+ return this.surface()?.nativeElement ?? null;
6830
7000
  }
6831
- ngAfterContentInit() {
7001
+ /**
7002
+ * Started by DialogService, either right after the dialog is created or
7003
+ * once the dialog it replaces has had its head start. The animation runs on
7004
+ * the next frame so the surface has a real from/to state.
7005
+ */
7006
+ startEnterAnimation() {
6832
7007
  requestAnimationFrame(() => {
6833
- this.showMenu(true);
7008
+ this.isActive.set(true);
6834
7009
  });
6835
7010
  }
6836
- showMenu(value) {
6837
- this.isVisible.set(value);
7011
+ setActive(value) {
7012
+ this.isActive.set(value);
6838
7013
  }
6839
- /**
6840
- * The service creates this wrapper first, then calls this method to mount
6841
- * the user supplied component into the dialog container.
6842
- */
6843
- attachContent(component, injector) {
6844
- const portal = new ComponentPortal(component, null, injector);
6845
- return this.portalOutlet().attachComponentPortal(portal);
7014
+ recaptureFocus() {
7015
+ this._recaptureFocus();
6846
7016
  }
6847
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Menu, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
6848
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.0", type: Menu, isStandalone: true, selector: "md3-menu", host: { attributes: { "role": "menu" }, properties: { "class.md3-visible": "isVisible()", "class.md3-inactive": "!isActive()", "class.md-scheme-dark": "scheme() == \"dark\"", "class.md-scheme-light": "scheme() == \"light\"", "dir": "direction()" }, classAttribute: "md3-menu-container" }, viewQueries: [{ propertyName: "portalOutlet", first: true, predicate: CdkPortalOutlet, descendants: true, isSignal: true }], ngImport: i0, template: "<ng-template cdkPortalOutlet></ng-template>", styles: ["::ng-deep .cdk-overlay-container{position:fixed;z-index:1000;inset:0;pointer-events:none;font-size:1rem}::ng-deep .cdk-overlay-container .md3-menu-scrim{position:absolute;inset:0;pointer-events:auto}::ng-deep .cdk-overlay-container .cdk-overlay-connected-position-bounding-box{pointer-events:none}::ng-deep .cdk-overlay-container .md3-menu-panel{pointer-events:auto}::ng-deep .cdk-overlay-container .md3-menu-panel.md3-menu-closing{pointer-events:none}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper{position:absolute;display:flex;inset:0;pointer-events:none;padding:3.5rem 0;overflow:hidden}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-menu-panel{position:absolute;pointer-events:auto}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-menu-panel.md3-menu-closing{pointer-events:none}:host{--background-color: rgb(var(--md-scheme-surface-container-low));--foreground-color: rgb(var(--md-scheme-on-surface));--border-radius: var(--md-border-radius-large);--menu-min-width: 7em;--menu-max-width: 17.5em;--menu-group-radius: var(--md-border-radius-small);--menu-group-shadow: var(--md-shadow-3dp);--menu-group-label-color: rgb(var(--md-scheme-on-surface-variant));--menu-item-label-color: rgb(var(--md-scheme-on-surface));--menu-item-supporting-color: rgb(var(--md-scheme-on-surface-variant));--menu-item-selected-background: rgb(var(--md-scheme-tertiary-container));--menu-item-selected-foreground: rgb(var(--md-scheme-on-tertiary-container));--menu-item-state-color: var(--md-scheme-on-surface);--menu-item-border-radius: var(--md-border-radius-xsmall);--menu-item-outline-color: rgb(var(--md-scheme-secondary));--menu-item-outline-width: .1875em;--menu-item-outline-offset: -.1875em;--menu-item-badge-background: rgb(var(--md-scheme-primary-container));--menu-item-badge-foreground: rgb(var(--md-scheme-on-primary-container));position:relative;display:block;font-size:1rem;min-width:var(--menu-min-width);max-width:min(var(--menu-max-width),100vw - 2rem);max-height:min(32rem,100vh - 2rem);outline:none;transition:all var(--md-motion-expressive-default-spatial-duration) var(--md-motion-expressive-default-spatial-easing);opacity:0;transform:scaleY(.8)}:host.md3-visible{opacity:1;transform:scaleY(1)}:host.md3-vibrant{--background-color: rgb(var(--md-scheme-tertiary-container));--foreground-color: rgb(var(--md-scheme-on-tertiary-container));--menu-group-label-color: rgb(var(--md-scheme-on-tertiary-container));--menu-item-label-color: rgb(var(--md-scheme-on-tertiary-container));--menu-item-supporting-color: rgb(var(--md-scheme-on-tertiary-container));--menu-item-selected-background: rgb(var(--md-scheme-tertiary));--menu-item-selected-foreground: rgb(var(--md-scheme-on-tertiary));--menu-item-state-color: var(--md-scheme-on-tertiary-container)}:host.md3-inactive{--border-radius: var(--md-border-radius-small)}:host ::ng-deep md3-menu-group{margin-top:.125em}:host ::ng-deep md3-menu-group:first-child{margin-top:0}\n"], dependencies: [{ kind: "directive", type: CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }] });
7017
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Dialog, deps: null, target: i0.ɵɵFactoryTarget.Component });
7018
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.0", type: Dialog, isStandalone: true, selector: "md3-dialog", viewQueries: [{ propertyName: "surface", first: true, predicate: ["surface"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<!-- The role and aria attributes live on the host element, applied by the CDK dialog container. -->\n<div\n #surface\n class=\"md3-dialog-container\"\n [class.md3-active]=\"isActive()\"\n [class.md-scheme-dark]=\"config.scheme == 'dark'\"\n [class.md-scheme-light]=\"config.scheme == 'light'\"\n [attr.dir]=\"config.direction ?? null\">\n <ng-template cdkPortalOutlet></ng-template>\n</div>\n", styles: ["::ng-deep .cdk-overlay-container{position:fixed;z-index:1000;inset:0;pointer-events:none;font-size:1rem}::ng-deep .cdk-overlay-container .md3-dialog-scrim{position:absolute;inset:0;pointer-events:auto;background-color:rgb(var(--md-scheme-scrim));transition:all var(--md-motion-expressive-slow-spatial-duration) var(--md-motion-expressive-slow-spatial-easing);opacity:.32}::ng-deep .cdk-overlay-container .md3-dialog-scrim.md3-dialog-opening{opacity:0}::ng-deep .cdk-overlay-container .md3-dialog-scrim.md3-dialog-closing{opacity:0}::ng-deep .cdk-overlay-container .md3-dialog-scrim.md3-dialog-hidden{opacity:0;pointer-events:none}::ng-deep .cdk-overlay-container .md3-dialog-scrim.md3-dialog-scrim-kept{opacity:.32;transition:none}::ng-deep .cdk-overlay-container .md3-dialog-scrim.md3-dialog-scrim-instant{transition:none}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper{position:absolute;display:flex;inset:0;pointer-events:none;padding:3.5rem 1.5rem;overflow:hidden}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-dialog-panel{position:absolute;pointer-events:auto;max-width:35em}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-dialog-panel.md3-dialog-closing,::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-dialog-panel.md3-dialog-hidden{pointer-events:none}:host{display:block;max-width:100%;min-width:17.5em;outline:0}:host .md3-dialog-container{display:block;width:100%;max-height:inherit;overflow:auto;color:rgb(var(--md-scheme-on-surface));background-color:rgb(var(--md-scheme-surface-container-high));border-radius:var(--md-border-radius-xlarge);box-shadow:var(--md-shadow-6dp);transform-origin:center;transition:all var(--md-motion-expressive-default-spatial-duration) var(--md-motion-expressive-default-spatial-easing);opacity:0;transform:scaleY(.8);margin-top:-8rem}:host .md3-dialog-container.md3-active{opacity:1;transform:scaleY(1);margin-top:0}\n"], dependencies: [{ kind: "directive", type: CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }] });
6849
7019
  }
6850
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Menu, decorators: [{
7020
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Dialog, decorators: [{
6851
7021
  type: Component,
6852
- args: [{ selector: 'md3-menu', imports: [
7022
+ args: [{ selector: 'md3-dialog', imports: [
6853
7023
  CdkPortalOutlet,
6854
- ], host: {
6855
- 'class': 'md3-menu-container',
6856
- 'role': 'menu',
6857
- '[class.md3-visible]': 'isVisible()',
6858
- '[class.md3-inactive]': '!isActive()',
6859
- '[class.md-scheme-dark]': 'scheme() == "dark"',
6860
- '[class.md-scheme-light]': 'scheme() == "light"',
6861
- '[dir]': 'direction()',
6862
- }, template: "<ng-template cdkPortalOutlet></ng-template>", styles: ["::ng-deep .cdk-overlay-container{position:fixed;z-index:1000;inset:0;pointer-events:none;font-size:1rem}::ng-deep .cdk-overlay-container .md3-menu-scrim{position:absolute;inset:0;pointer-events:auto}::ng-deep .cdk-overlay-container .cdk-overlay-connected-position-bounding-box{pointer-events:none}::ng-deep .cdk-overlay-container .md3-menu-panel{pointer-events:auto}::ng-deep .cdk-overlay-container .md3-menu-panel.md3-menu-closing{pointer-events:none}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper{position:absolute;display:flex;inset:0;pointer-events:none;padding:3.5rem 0;overflow:hidden}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-menu-panel{position:absolute;pointer-events:auto}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-menu-panel.md3-menu-closing{pointer-events:none}:host{--background-color: rgb(var(--md-scheme-surface-container-low));--foreground-color: rgb(var(--md-scheme-on-surface));--border-radius: var(--md-border-radius-large);--menu-min-width: 7em;--menu-max-width: 17.5em;--menu-group-radius: var(--md-border-radius-small);--menu-group-shadow: var(--md-shadow-3dp);--menu-group-label-color: rgb(var(--md-scheme-on-surface-variant));--menu-item-label-color: rgb(var(--md-scheme-on-surface));--menu-item-supporting-color: rgb(var(--md-scheme-on-surface-variant));--menu-item-selected-background: rgb(var(--md-scheme-tertiary-container));--menu-item-selected-foreground: rgb(var(--md-scheme-on-tertiary-container));--menu-item-state-color: var(--md-scheme-on-surface);--menu-item-border-radius: var(--md-border-radius-xsmall);--menu-item-outline-color: rgb(var(--md-scheme-secondary));--menu-item-outline-width: .1875em;--menu-item-outline-offset: -.1875em;--menu-item-badge-background: rgb(var(--md-scheme-primary-container));--menu-item-badge-foreground: rgb(var(--md-scheme-on-primary-container));position:relative;display:block;font-size:1rem;min-width:var(--menu-min-width);max-width:min(var(--menu-max-width),100vw - 2rem);max-height:min(32rem,100vh - 2rem);outline:none;transition:all var(--md-motion-expressive-default-spatial-duration) var(--md-motion-expressive-default-spatial-easing);opacity:0;transform:scaleY(.8)}:host.md3-visible{opacity:1;transform:scaleY(1)}:host.md3-vibrant{--background-color: rgb(var(--md-scheme-tertiary-container));--foreground-color: rgb(var(--md-scheme-on-tertiary-container));--menu-group-label-color: rgb(var(--md-scheme-on-tertiary-container));--menu-item-label-color: rgb(var(--md-scheme-on-tertiary-container));--menu-item-supporting-color: rgb(var(--md-scheme-on-tertiary-container));--menu-item-selected-background: rgb(var(--md-scheme-tertiary));--menu-item-selected-foreground: rgb(var(--md-scheme-on-tertiary));--menu-item-state-color: var(--md-scheme-on-tertiary-container)}:host.md3-inactive{--border-radius: var(--md-border-radius-small)}:host ::ng-deep md3-menu-group{margin-top:.125em}:host ::ng-deep md3-menu-group:first-child{margin-top:0}\n"] }]
6863
- }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { portalOutlet: [{ type: i0.ViewChild, args: [i0.forwardRef(() => CdkPortalOutlet), { isSignal: true }] }] } });
7024
+ ], template: "<!-- The role and aria attributes live on the host element, applied by the CDK dialog container. -->\n<div\n #surface\n class=\"md3-dialog-container\"\n [class.md3-active]=\"isActive()\"\n [class.md-scheme-dark]=\"config.scheme == 'dark'\"\n [class.md-scheme-light]=\"config.scheme == 'light'\"\n [attr.dir]=\"config.direction ?? null\">\n <ng-template cdkPortalOutlet></ng-template>\n</div>\n", styles: ["::ng-deep .cdk-overlay-container{position:fixed;z-index:1000;inset:0;pointer-events:none;font-size:1rem}::ng-deep .cdk-overlay-container .md3-dialog-scrim{position:absolute;inset:0;pointer-events:auto;background-color:rgb(var(--md-scheme-scrim));transition:all var(--md-motion-expressive-slow-spatial-duration) var(--md-motion-expressive-slow-spatial-easing);opacity:.32}::ng-deep .cdk-overlay-container .md3-dialog-scrim.md3-dialog-opening{opacity:0}::ng-deep .cdk-overlay-container .md3-dialog-scrim.md3-dialog-closing{opacity:0}::ng-deep .cdk-overlay-container .md3-dialog-scrim.md3-dialog-hidden{opacity:0;pointer-events:none}::ng-deep .cdk-overlay-container .md3-dialog-scrim.md3-dialog-scrim-kept{opacity:.32;transition:none}::ng-deep .cdk-overlay-container .md3-dialog-scrim.md3-dialog-scrim-instant{transition:none}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper{position:absolute;display:flex;inset:0;pointer-events:none;padding:3.5rem 1.5rem;overflow:hidden}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-dialog-panel{position:absolute;pointer-events:auto;max-width:35em}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-dialog-panel.md3-dialog-closing,::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-dialog-panel.md3-dialog-hidden{pointer-events:none}:host{display:block;max-width:100%;min-width:17.5em;outline:0}:host .md3-dialog-container{display:block;width:100%;max-height:inherit;overflow:auto;color:rgb(var(--md-scheme-on-surface));background-color:rgb(var(--md-scheme-surface-container-high));border-radius:var(--md-border-radius-xlarge);box-shadow:var(--md-shadow-6dp);transform-origin:center;transition:all var(--md-motion-expressive-default-spatial-duration) var(--md-motion-expressive-default-spatial-easing);opacity:0;transform:scaleY(.8);margin-top:-8rem}:host .md3-dialog-container.md3-active{opacity:1;transform:scaleY(1);margin-top:0}\n"] }]
7025
+ }], propDecorators: { surface: [{ type: i0.ViewChild, args: ['surface', { isSignal: true }] }] } });
6864
7026
 
6865
- class MenuService {
6866
- overlay = inject(Overlay);
6867
- injector = inject(Injector);
6868
- document = inject(DOCUMENT);
6869
- open(component, config = {}) {
6870
- const menuConfig = this.mergeConfig(config);
6871
- const previouslyFocusedElement = this.getFocusedElement();
6872
- const triggerElement = this.resolveTriggerElement(menuConfig.origin, previouslyFocusedElement);
6873
- const menuOverlay = this.createOverlay(menuConfig, previouslyFocusedElement);
6874
- const overlayRef = menuOverlay.overlayRef;
6875
- const menuRef = new MenuRef(overlayRef, previouslyFocusedElement);
6876
- const injector = this.createInjector(component, menuConfig, menuRef);
6877
- // The overlay hosts the MD3 menu shell. The shell is responsible for
6878
- // the surface markup and receives the dynamic content through a portal.
6879
- const menuPortal = new ComponentPortal(Menu, menuConfig.viewContainerRef ?? null, injector);
6880
- const menuComponentRef = overlayRef.attach(menuPortal);
6881
- const contentComponentRef = menuComponentRef.instance.attachContent(component, injector);
6882
- this.bindDataToInputs(contentComponentRef, menuConfig);
6883
- this.flipPositionIfPushBreaksAnchor(menuOverlay, menuConfig);
6884
- menuRef.componentInstance = contentComponentRef.instance;
6885
- menuRef.menuInstance = menuComponentRef.instance;
6886
- this.bindTriggerActiveState(triggerElement, overlayRef, menuRef);
6887
- this.startOpenAnimation(overlayRef);
6888
- this.connectCloseEvents(overlayRef, menuRef);
6889
- return menuRef;
6890
- }
6891
- openSubMenu(component, config = {}) {
6892
- const xPosition = this.getSubMenuXPosition(config.xPosition);
6893
- const yPosition = config.yPosition ?? 'below';
6894
- const offsetX = config.offsetX ?? 0;
6895
- const offsetY = config.offsetY ?? 0;
6896
- const subMenuConfig = {
6897
- ...config,
6898
- isSubMenu: true,
6899
- xPosition,
6900
- yPosition,
6901
- overlapTrigger: config.overlapTrigger ?? true,
6902
- offsetX,
6903
- offsetY,
6904
- scheme: config.scheme ?? 'inherit',
6905
- direction: config.direction ?? null,
6906
- };
6907
- return this.open(component, subMenuConfig);
6908
- }
6909
- mergeConfig(config) {
6910
- const viewportMargin = this.getViewportMargin(config.viewportMargin);
6911
- return {
6912
- data: config.data,
6913
- bindDataToInputs: config.bindDataToInputs ?? false,
6914
- isSubMenu: config.isSubMenu ?? false,
6915
- menuColors: config.menuColors ?? 'standard',
6916
- xPosition: config.xPosition ?? 'start',
6917
- yPosition: config.yPosition ?? 'below',
6918
- overlapTrigger: config.overlapTrigger ?? false,
6919
- offsetX: config.offsetX ?? 0,
6920
- offsetY: config.offsetY ?? 4,
6921
- viewportMargin,
6922
- positions: config.positions,
6923
- scrollStrategy: config.scrollStrategy ?? 'reposition',
6924
- scheme: config.scheme ?? 'inherit',
6925
- direction: config.direction ?? null,
6926
- viewContainerRef: config.viewContainerRef,
6927
- injector: config.injector ?? this.injector,
6928
- };
6929
- }
6930
- createOverlay(config, fallbackOrigin) {
6931
- const origin = this.resolveOrigin(config.origin, fallbackOrigin);
6932
- const positionStrategy = this.overlay.position()
6933
- .flexibleConnectedTo(origin)
6934
- .withFlexibleDimensions(false)
6935
- .withGrowAfterOpen(true)
6936
- .withPush(true)
6937
- .withViewportMargin(config.viewportMargin)
6938
- .withPositions(config.positions ?? this.getInitialConnectedPositions(config))
6939
- .withTransformOriginOn('.md3-menu-container');
6940
- const overlayConfig = new OverlayConfig({
6941
- hasBackdrop: true,
6942
- backdropClass: ['md3-menu-scrim', 'md3-menu-opening'],
6943
- panelClass: ['md3-menu-panel', 'md3-menu-opening'],
6944
- positionStrategy,
6945
- scrollStrategy: this.getScrollStrategy(config.scrollStrategy),
6946
- });
6947
- return {
6948
- overlayRef: this.overlay.create(overlayConfig),
6949
- positionStrategy: config.positions ? undefined : positionStrategy,
6950
- origin,
6951
- };
7027
+ /**
7028
+ * Material 3 full screen dialog shell. It covers the viewport and holds the content.
7029
+ *
7030
+ * The content is an `md3-layout`. The layout places the regions — the bar rows for an app bar
7031
+ * or a toolbar, the rail columns, and the panes where side sheets open — so the dialog itself
7032
+ * places nothing but the content. A content root that is not a layout gets a warning in
7033
+ * development mode, and the shell falls back to panes of its own, so a side sheet never opens
7034
+ * behind the dialog where nobody can see it.
7035
+ */
7036
+ class FullScreenDialog extends CdkDialogContainer {
7037
+ surface = viewChild('surface', /* @ts-ignore */
7038
+ ...(ngDevMode ? [{ debugName: "surface" }] : /* istanbul ignore next */ []));
7039
+ mainPane = viewChild('mainPane', /* @ts-ignore */
7040
+ ...(ngDevMode ? [{ debugName: "mainPane" }] : /* istanbul ignore next */ []));
7041
+ startOutlet = viewChild('startOutlet', { ...(ngDevMode ? { debugName: "startOutlet" } : /* istanbul ignore next */ {}), read: CdkPortalOutlet });
7042
+ endOutlet = viewChild('endOutlet', { ...(ngDevMode ? { debugName: "endOutlet" } : /* istanbul ignore next */ {}), read: CdkPortalOutlet });
7043
+ sheets = inject(SheetsService);
7044
+ /** True while the shell holds the side sheet outlets, which happens without a layout only. */
7045
+ ownsSideSheetOutlets = false;
7046
+ isActive = signal(false, /* @ts-ignore */
7047
+ ...(ngDevMode ? [{ debugName: "isActive" }] : /* istanbul ignore next */ []));
7048
+ config = inject(DIALOG_CONFIG, { optional: true }) ?? {};
7049
+ get surfaceElement() {
7050
+ return this.surface()?.nativeElement ?? null;
6952
7051
  }
6953
- bindTriggerActiveState(triggerElement, overlayRef, menuRef) {
6954
- if (!triggerElement) {
7052
+ ngAfterViewInit() {
7053
+ // The content is attached by now, so the layout it brings has already taken the
7054
+ // outlets. Its panes sit under the bar regions, which is where a side sheet belongs.
7055
+ const layout = this.findContentLayout();
7056
+ if (typeof ngDevMode === 'undefined' || ngDevMode) {
7057
+ this.checkContentLayout(layout);
7058
+ }
7059
+ if (layout) {
6955
7060
  return;
6956
7061
  }
6957
- const removeActiveClass = () => triggerElement.classList.remove('md3-menu-active');
6958
- triggerElement.classList.add('md3-menu-active');
6959
- menuRef.afterClosed().pipe(take(1)).subscribe(removeActiveClass);
6960
- overlayRef.detachments().pipe(take(1)).subscribe(removeActiveClass);
7062
+ // No layout, so the shell keeps the dialog self-sufficient. Without this the next side
7063
+ // sheet would open in the outlet underneath — the page scaffold, behind the dialog.
7064
+ const startOutlet = this.startOutlet();
7065
+ const endOutlet = this.endOutlet();
7066
+ if (startOutlet) {
7067
+ this.sheets.registerSideSheetOutlet('start', startOutlet);
7068
+ }
7069
+ if (endOutlet) {
7070
+ this.sheets.registerSideSheetOutlet('end', endOutlet);
7071
+ }
7072
+ this.ownsSideSheetOutlets = !!startOutlet || !!endOutlet;
6961
7073
  }
6962
- resolveOrigin(origin, fallbackOrigin) {
6963
- const resolvedOrigin = origin ?? fallbackOrigin;
6964
- if (resolvedOrigin) {
6965
- if (this.isMouseEvent(resolvedOrigin)) {
6966
- if (resolvedOrigin.currentTarget instanceof HTMLElement) {
6967
- return resolvedOrigin.currentTarget;
6968
- }
6969
- return {
6970
- x: resolvedOrigin.clientX,
6971
- y: resolvedOrigin.clientY,
6972
- };
7074
+ ngOnDestroy() {
7075
+ if (this.ownsSideSheetOutlets) {
7076
+ const startOutlet = this.startOutlet();
7077
+ const endOutlet = this.endOutlet();
7078
+ if (startOutlet) {
7079
+ this.sheets.unregisterSideSheetOutlet('start', startOutlet);
6973
7080
  }
6974
- if (this.isElementRef(resolvedOrigin)) {
6975
- return resolvedOrigin.nativeElement;
7081
+ if (endOutlet) {
7082
+ this.sheets.unregisterSideSheetOutlet('end', endOutlet);
6976
7083
  }
6977
- return resolvedOrigin;
7084
+ this.ownsSideSheetOutlets = false;
6978
7085
  }
6979
- const viewport = this.document.defaultView;
6980
- return {
6981
- x: (viewport?.innerWidth ?? 0) / 2,
6982
- y: (viewport?.innerHeight ?? 0) / 2,
6983
- };
7086
+ super.ngOnDestroy();
6984
7087
  }
6985
- resolveTriggerElement(origin, fallbackOrigin) {
6986
- const resolvedOrigin = origin ?? fallbackOrigin;
6987
- if (!resolvedOrigin) {
7088
+ /**
7089
+ * The layout at the root of the content, if the content brought one.
7090
+ *
7091
+ * The root is either the layout itself, or the component that holds it — a dialog is
7092
+ * normally opened with a component whose own template starts with `md3-layout`.
7093
+ */
7094
+ findContentLayout() {
7095
+ const content = this.mainPane()?.nativeElement.firstElementChild;
7096
+ if (!content) {
6988
7097
  return null;
6989
7098
  }
6990
- if (this.isMouseEvent(resolvedOrigin)) {
6991
- return resolvedOrigin.currentTarget instanceof HTMLElement
6992
- ? resolvedOrigin.currentTarget
6993
- : null;
7099
+ if (content.tagName === 'MD3-LAYOUT') {
7100
+ return content;
6994
7101
  }
6995
- if (this.isElementRef(resolvedOrigin)) {
6996
- return resolvedOrigin.nativeElement;
7102
+ const root = content.firstElementChild;
7103
+ return root?.tagName === 'MD3-LAYOUT' ? root : null;
7104
+ }
7105
+ checkContentLayout(layout) {
7106
+ if (layout) {
7107
+ return;
6997
7108
  }
6998
- return resolvedOrigin instanceof HTMLElement ? resolvedOrigin : null;
7109
+ console.warn('[md3-fullscreen-dialog] the content of a full screen dialog should start with '
7110
+ + 'md3-layout. Without it the dialog has no bar and no rail region, so an app bar or '
7111
+ + 'a toolbar has nowhere to go, and the content does not scroll on its own.');
7112
+ }
7113
+ startEnterAnimation() {
7114
+ requestAnimationFrame(() => {
7115
+ this.isActive.set(true);
7116
+ });
7117
+ }
7118
+ setActive(value) {
7119
+ this.isActive.set(value);
7120
+ }
7121
+ recaptureFocus() {
7122
+ this._recaptureFocus();
7123
+ }
7124
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: FullScreenDialog, deps: null, target: i0.ɵɵFactoryTarget.Component });
7125
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.0", type: FullScreenDialog, isStandalone: true, selector: "md3-fullscreen-dialog", viewQueries: [{ propertyName: "surface", first: true, predicate: ["surface"], descendants: true, isSignal: true }, { propertyName: "mainPane", first: true, predicate: ["mainPane"], descendants: true, isSignal: true }, { propertyName: "startOutlet", first: true, predicate: ["startOutlet"], descendants: true, read: CdkPortalOutlet, isSignal: true }, { propertyName: "endOutlet", first: true, predicate: ["endOutlet"], descendants: true, read: CdkPortalOutlet, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<!-- The role and aria attributes live on the host element, applied by the CDK dialog container. -->\n<div\n #surface\n class=\"md3-fullscreen-dialog-container\"\n [class.md3-active]=\"isActive()\"\n [class.md-scheme-dark]=\"config.scheme == 'dark'\"\n [class.md-scheme-light]=\"config.scheme == 'light'\"\n [attr.dir]=\"config.direction ?? null\">\n <div class=\"md3-panes-container md3-scrollable\">\n <!--\n The CDK attaches the dialog content to the first portal outlet of\n this template, so the main pane has to come first. The panes are\n placed by grid area, which makes the DOM order irrelevant visually.\n -->\n <div class=\"md3-pane md3-pane-main\" #mainPane>\n <ng-template cdkPortalOutlet></ng-template>\n </div>\n <div class=\"md3-pane md3-pane-start\">\n <ng-template #startOutlet cdkPortalOutlet></ng-template>\n </div>\n <div class=\"md3-pane md3-pane-end\">\n <ng-template #endOutlet cdkPortalOutlet></ng-template>\n </div>\n </div>\n</div>\n", styles: ["::ng-deep .cdk-overlay-container{position:fixed;z-index:1000;inset:0;pointer-events:none;font-size:1rem}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper{position:absolute;display:flex;inset:0;pointer-events:none;overflow:hidden}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper:has(>.md3-fullscreen-dialog-panel){padding:0}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-fullscreen-dialog-panel{width:100%;height:100%;max-width:none;pointer-events:auto}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-fullscreen-dialog-panel.md3-dialog-closing,::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-fullscreen-dialog-panel.md3-dialog-hidden{pointer-events:none}:host{display:block;height:100%;width:100%;outline:0}:host .md3-fullscreen-dialog-container{display:grid;grid-template-rows:minmax(0,1fr);height:100%;min-height:0;overflow:hidden;color:rgb(var(--md-scheme-on-surface));background-color:rgb(var(--md-scheme-surface));transition:all var(--md-motion-expressive-default-spatial-duration) var(--md-motion-expressive-default-spatial-easing);opacity:0;transform:translateY(1.5em)}:host .md3-fullscreen-dialog-container.md3-active{opacity:1;transform:none}:host .md3-panes-container{display:grid;grid-template-areas:\"start main end\";grid-template-columns:auto minmax(0,1fr) auto;grid-template-rows:minmax(0,1fr);min-width:0;min-height:0;overflow:hidden}:host .md3-panes-container .md3-pane{justify-self:stretch;align-self:stretch;min-width:0;min-height:0;height:100%;overflow:hidden}:host .md3-panes-container .md3-pane.md3-pane-start{grid-area:start}:host .md3-panes-container .md3-pane.md3-pane-main{position:relative;grid-area:main;z-index:1;display:grid;grid-template-rows:minmax(0,1fr);grid-template-columns:minmax(0,1fr)}:host .md3-panes-container .md3-pane.md3-pane-main>::ng-deep *{min-width:0;min-height:0;height:100%;max-height:100dvh;max-width:100dvw}:host .md3-panes-container .md3-pane.md3-pane-end{grid-area:end}\n"], dependencies: [{ kind: "directive", type: CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }] });
7126
+ }
7127
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: FullScreenDialog, decorators: [{
7128
+ type: Component,
7129
+ args: [{ selector: 'md3-fullscreen-dialog', imports: [
7130
+ CdkPortalOutlet,
7131
+ ], template: "<!-- The role and aria attributes live on the host element, applied by the CDK dialog container. -->\n<div\n #surface\n class=\"md3-fullscreen-dialog-container\"\n [class.md3-active]=\"isActive()\"\n [class.md-scheme-dark]=\"config.scheme == 'dark'\"\n [class.md-scheme-light]=\"config.scheme == 'light'\"\n [attr.dir]=\"config.direction ?? null\">\n <div class=\"md3-panes-container md3-scrollable\">\n <!--\n The CDK attaches the dialog content to the first portal outlet of\n this template, so the main pane has to come first. The panes are\n placed by grid area, which makes the DOM order irrelevant visually.\n -->\n <div class=\"md3-pane md3-pane-main\" #mainPane>\n <ng-template cdkPortalOutlet></ng-template>\n </div>\n <div class=\"md3-pane md3-pane-start\">\n <ng-template #startOutlet cdkPortalOutlet></ng-template>\n </div>\n <div class=\"md3-pane md3-pane-end\">\n <ng-template #endOutlet cdkPortalOutlet></ng-template>\n </div>\n </div>\n</div>\n", styles: ["::ng-deep .cdk-overlay-container{position:fixed;z-index:1000;inset:0;pointer-events:none;font-size:1rem}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper{position:absolute;display:flex;inset:0;pointer-events:none;overflow:hidden}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper:has(>.md3-fullscreen-dialog-panel){padding:0}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-fullscreen-dialog-panel{width:100%;height:100%;max-width:none;pointer-events:auto}::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-fullscreen-dialog-panel.md3-dialog-closing,::ng-deep .cdk-overlay-container .cdk-global-overlay-wrapper .md3-fullscreen-dialog-panel.md3-dialog-hidden{pointer-events:none}:host{display:block;height:100%;width:100%;outline:0}:host .md3-fullscreen-dialog-container{display:grid;grid-template-rows:minmax(0,1fr);height:100%;min-height:0;overflow:hidden;color:rgb(var(--md-scheme-on-surface));background-color:rgb(var(--md-scheme-surface));transition:all var(--md-motion-expressive-default-spatial-duration) var(--md-motion-expressive-default-spatial-easing);opacity:0;transform:translateY(1.5em)}:host .md3-fullscreen-dialog-container.md3-active{opacity:1;transform:none}:host .md3-panes-container{display:grid;grid-template-areas:\"start main end\";grid-template-columns:auto minmax(0,1fr) auto;grid-template-rows:minmax(0,1fr);min-width:0;min-height:0;overflow:hidden}:host .md3-panes-container .md3-pane{justify-self:stretch;align-self:stretch;min-width:0;min-height:0;height:100%;overflow:hidden}:host .md3-panes-container .md3-pane.md3-pane-start{grid-area:start}:host .md3-panes-container .md3-pane.md3-pane-main{position:relative;grid-area:main;z-index:1;display:grid;grid-template-rows:minmax(0,1fr);grid-template-columns:minmax(0,1fr)}:host .md3-panes-container .md3-pane.md3-pane-main>::ng-deep *{min-width:0;min-height:0;height:100%;max-height:100dvh;max-width:100dvw}:host .md3-panes-container .md3-pane.md3-pane-end{grid-area:end}\n"] }]
7132
+ }], propDecorators: { surface: [{ type: i0.ViewChild, args: ['surface', { isSignal: true }] }], mainPane: [{ type: i0.ViewChild, args: ['mainPane', { isSignal: true }] }], startOutlet: [{ type: i0.ViewChild, args: ['startOutlet', { ...{ read: CdkPortalOutlet }, isSignal: true }] }], endOutlet: [{ type: i0.ViewChild, args: ['endOutlet', { ...{ read: CdkPortalOutlet }, isSignal: true }] }] } });
7133
+
7134
+ const PANEL_CLASS = 'md3-dialog-panel';
7135
+ const FULL_SCREEN_PANEL_CLASS = 'md3-fullscreen-dialog-panel';
7136
+ const OPENING_CLASS = 'md3-dialog-opening';
7137
+ const SCRIM_KEPT_CLASS = 'md3-dialog-scrim-kept';
7138
+ const SCRIM_INSTANT_CLASS = 'md3-dialog-scrim-instant';
7139
+ /**
7140
+ * Head start given to a dialog that leaves the screen before the dialog taking
7141
+ * its place animates in. Long enough to read as one dialog replacing another,
7142
+ * short enough to still feel like a single gesture.
7143
+ */
7144
+ const DIALOG_HANDOVER_DELAY_MS = 160;
7145
+ class DialogService {
7146
+ cdkDialog = inject(Dialog$1);
7147
+ overlay = inject(Overlay);
7148
+ overlayContainer = inject(OverlayContainer);
7149
+ injector = inject(Injector);
7150
+ document = inject(DOCUMENT);
7151
+ /** Open dialogs, from the first one opened to the one currently on top. */
7152
+ refs = [];
7153
+ /**
7154
+ * Elements the page was made of that the CDK hid from assistive technology,
7155
+ * kept here while the dialogs are hidden and the page is given back.
7156
+ */
7157
+ pageAriaHidden = new Map();
7158
+ /**
7159
+ * Page scrolling is blocked here instead of per overlay, so stacked dialogs
7160
+ * cannot unblock the page while another dialog is still open.
7161
+ */
7162
+ scrollBlock = this.overlay.scrollStrategies.block();
7163
+ /** Element that was focused before the first dialog of the stack opened. */
7164
+ rootTrigger = null;
7165
+ pageStateQueued = false;
7166
+ get openDialogs() {
7167
+ return this.refs;
6999
7168
  }
7000
- flipPositionIfPushBreaksAnchor(menuOverlay, config) {
7001
- if (!menuOverlay.positionStrategy) {
7002
- return;
7003
- }
7004
- menuOverlay.overlayRef.updatePosition();
7005
- const fallbackPosition = this.getFallbackPositionAfterPush(config, this.getOriginClientRect(menuOverlay.origin), menuOverlay.overlayRef.overlayElement.getBoundingClientRect());
7006
- if (!fallbackPosition) {
7007
- return;
7008
- }
7009
- menuOverlay.positionStrategy.withPositions([fallbackPosition]);
7010
- menuOverlay.overlayRef.updatePosition();
7169
+ /** Open dialogs that are currently on screen, from the bottom one up. */
7170
+ get visibleDialogs() {
7171
+ return this.refs.filter((ref) => !ref.isHidden);
7011
7172
  }
7012
- getInitialConnectedPositions(config) {
7013
- return [this.getPreferredConnectedPosition(config)];
7173
+ /** The full screen dialog that is open, if there is one. */
7174
+ get fullScreenDialog() {
7175
+ return this.refs.find((ref) => ref.isFullScreen && !ref.isClosing);
7014
7176
  }
7015
- getPreferredConnectedPosition(config) {
7016
- if (config.isSubMenu) {
7017
- return this.createSubMenuConnectedPosition(this.getSubMenuXPosition(config.xPosition), this.getSubMenuVerticalAlignment(config.yPosition), config.offsetX, config.offsetY);
7177
+ open(component, config = {}) {
7178
+ const dialogConfig = this.mergeConfig(config);
7179
+ if (this.refs.length === 0) {
7180
+ this.rootTrigger = this.getFocusedElement();
7018
7181
  }
7019
- return this.createConnectedPosition(config.xPosition, config.yPosition, config);
7020
- }
7021
- getFallbackPositionAfterPush(config, originRect, overlayRect) {
7022
- const xPosition = this.shouldFlipXPosition(config, originRect, overlayRect)
7023
- ? this.getFallbackXPosition(config.xPosition)
7024
- : config.xPosition;
7025
- const yPosition = this.shouldFlipYPosition(config, originRect, overlayRect)
7026
- ? this.getFallbackYPosition(config.yPosition)
7027
- : config.yPosition;
7028
- if (xPosition === config.xPosition && yPosition === config.yPosition) {
7029
- return null;
7182
+ // The dialog that is on screen starts leaving first, so the two dialogs
7183
+ // never animate on top of each other. A full screen dialog is not part
7184
+ // of this: regular dialogs open on top of it and leave it alone.
7185
+ const previousRef = this.topRegularRef();
7186
+ if (previousRef) {
7187
+ this.dismissPrevious(previousRef, dialogConfig.previousDialog);
7030
7188
  }
7031
- return this.getPreferredConnectedPosition({
7032
- ...config,
7033
- xPosition,
7034
- yPosition,
7189
+ let dialogRef;
7190
+ const cdkRef = this.cdkDialog.open(component, {
7191
+ // The MD3 shell replaces the default CDK container, so the overlay,
7192
+ // focus trap and focus restoration come from the CDK while the shell
7193
+ // only renders the surface.
7194
+ container: {
7195
+ type: Dialog,
7196
+ providers: () => [
7197
+ { provide: DIALOG_CONFIG, useValue: dialogConfig },
7198
+ ],
7199
+ },
7200
+ // These tokens make the dialog controllable from the dynamic
7201
+ // component without coupling that component to the service.
7202
+ providers: (cdkDialogRef) => {
7203
+ dialogRef = new DialogRef(cdkDialogRef, dialogConfig.disableCloseEvents);
7204
+ return [
7205
+ { provide: DialogRef, useValue: dialogRef },
7206
+ { provide: DIALOG_DATA, useValue: dialogConfig.data },
7207
+ { provide: DIALOG_CONFIG, useValue: dialogConfig },
7208
+ { provide: DIALOG_COMPONENT, useValue: component },
7209
+ ];
7210
+ },
7211
+ data: dialogConfig.data,
7212
+ role: dialogConfig.role,
7213
+ ariaModal: true,
7214
+ ariaLabel: dialogConfig.ariaLabel || null,
7215
+ ariaLabelledBy: dialogConfig.ariaLabelledBy || null,
7216
+ ariaDescribedBy: dialogConfig.ariaDescribedBy || null,
7217
+ direction: dialogConfig.direction ?? undefined,
7218
+ viewContainerRef: dialogConfig.viewContainerRef,
7219
+ injector: dialogConfig.injector,
7220
+ hasBackdrop: true,
7221
+ backdropClass: ['md3-dialog-scrim', OPENING_CLASS],
7222
+ panelClass: [PANEL_CLASS, OPENING_CLASS],
7223
+ positionStrategy: this.overlay.position()
7224
+ .global()
7225
+ .centerHorizontally()
7226
+ .centerVertically(),
7227
+ // Scrolling is blocked by the service for the whole stack.
7228
+ scrollStrategy: this.overlay.scrollStrategies.noop(),
7229
+ // Focus goes back to whatever opened the stack, even when the dialog
7230
+ // that was open underneath has been closed in the meantime.
7231
+ restoreFocus: this.rootTrigger ?? true,
7232
+ // Scrim and Escape handling lives in DialogRef so the dialog can
7233
+ // play its exit animation before the overlay is disposed.
7234
+ disableClose: true,
7035
7235
  });
7236
+ this.registerDialog(dialogRef, cdkRef, dialogConfig, previousRef);
7237
+ return dialogRef;
7036
7238
  }
7037
- shouldFlipXPosition(config, originRect, overlayRect) {
7038
- const tolerance = config.viewportMargin;
7039
- switch (config.xPosition) {
7040
- case 'before':
7041
- return overlayRect.right > originRect.left + tolerance;
7042
- case 'after':
7043
- return overlayRect.left < originRect.right - tolerance;
7044
- case 'start':
7045
- return originRect.left < overlayRect.left - tolerance
7046
- || originRect.left > overlayRect.right + tolerance;
7047
- case 'end':
7048
- return originRect.right < overlayRect.left - tolerance
7049
- || originRect.right > overlayRect.right + tolerance;
7050
- case 'center':
7051
- default:
7052
- return false;
7053
- }
7054
- }
7055
- shouldFlipYPosition(config, originRect, overlayRect) {
7056
- const tolerance = config.viewportMargin;
7057
- if (config.isSubMenu) {
7058
- const anchorY = config.yPosition === 'above' ? originRect.bottom : originRect.top;
7059
- return anchorY < overlayRect.top - tolerance
7060
- || anchorY > overlayRect.bottom + tolerance;
7061
- }
7062
- if (config.overlapTrigger) {
7063
- const anchorY = config.yPosition === 'above' ? originRect.bottom : originRect.top;
7064
- return anchorY < overlayRect.top - tolerance
7065
- || anchorY > overlayRect.bottom + tolerance;
7066
- }
7067
- switch (config.yPosition) {
7068
- case 'above':
7069
- return overlayRect.bottom > originRect.top + tolerance;
7070
- case 'below':
7071
- return overlayRect.top < originRect.bottom - tolerance;
7072
- case 'center':
7073
- default:
7074
- return false;
7239
+ /**
7240
+ * Opens a dialog that covers the whole screen. Only one can be open at a
7241
+ * time, so it replaces whatever is open, and while it is up the side sheet
7242
+ * outlets belong to it: side sheets open inside the dialog, not behind it.
7243
+ * `previousDialog` decides what happens to the dialogs it replaces, the
7244
+ * same way it does for a regular dialog.
7245
+ */
7246
+ openFullScreen(component, config = {}) {
7247
+ const dialogConfig = this.mergeConfig(config);
7248
+ if (this.refs.length === 0) {
7249
+ this.rootTrigger = this.getFocusedElement();
7075
7250
  }
7076
- }
7077
- createConnectedPosition(xPosition, yPosition, config) {
7078
- const horizontal = this.getHorizontalPosition(xPosition);
7079
- const vertical = this.getVerticalPosition(yPosition, config.overlapTrigger);
7080
- return {
7081
- ...horizontal,
7082
- ...vertical,
7083
- offsetX: config.offsetX,
7084
- offsetY: this.getOffsetY(yPosition, config),
7085
- panelClass: [
7086
- 'md3-menu-position-' + xPosition,
7087
- 'md3-menu-position-' + yPosition,
7088
- ],
7089
- };
7090
- }
7091
- createSubMenuConnectedPosition(xPosition, yAlignment, offsetX, offsetY) {
7092
- const horizontal = this.getHorizontalPosition(xPosition);
7093
- const yPosition = yAlignment === 'top' ? 'below' : 'above';
7094
- return {
7095
- ...horizontal,
7096
- originY: yAlignment,
7097
- overlayY: yAlignment,
7098
- offsetX: this.getSubMenuOffsetX(xPosition, offsetX),
7099
- offsetY: this.getSubMenuOffsetY(yAlignment, offsetY),
7100
- panelClass: [
7101
- 'md3-menu-position-' + xPosition,
7102
- 'md3-menu-position-' + yPosition,
7103
- 'md3-menu-position-submenu',
7104
- ],
7105
- };
7106
- }
7107
- getHorizontalPosition(position) {
7108
- switch (position) {
7109
- case 'end':
7110
- return {
7111
- originX: 'end',
7112
- overlayX: 'end',
7113
- };
7114
- case 'before':
7115
- return {
7116
- originX: 'start',
7117
- overlayX: 'end',
7118
- };
7119
- case 'after':
7120
- return {
7121
- originX: 'end',
7122
- overlayX: 'start',
7123
- };
7124
- case 'center':
7125
- return {
7126
- originX: 'center',
7127
- overlayX: 'center',
7128
- };
7129
- case 'start':
7130
- default:
7131
- return {
7132
- originX: 'start',
7133
- overlayX: 'start',
7134
- };
7251
+ // A full screen dialog is a context of its own: the dialog it replaces
7252
+ // and anything stacked on that dialog goes with it.
7253
+ const previousRef = this.topRef();
7254
+ if (previousRef) {
7255
+ // Everything open belongs to the context being replaced, including
7256
+ // a full screen dialog that is already up.
7257
+ this.dismissPreviousContext(dialogConfig.previousDialog);
7135
7258
  }
7259
+ let dialogRef;
7260
+ const cdkRef = this.cdkDialog.open(component, {
7261
+ container: {
7262
+ type: FullScreenDialog,
7263
+ providers: () => [
7264
+ { provide: DIALOG_CONFIG, useValue: dialogConfig },
7265
+ ],
7266
+ },
7267
+ providers: (cdkDialogRef) => {
7268
+ dialogRef = new DialogRef(cdkDialogRef, dialogConfig.disableCloseEvents, true);
7269
+ return [
7270
+ { provide: DialogRef, useValue: dialogRef },
7271
+ { provide: DIALOG_DATA, useValue: dialogConfig.data },
7272
+ { provide: DIALOG_CONFIG, useValue: dialogConfig },
7273
+ { provide: DIALOG_COMPONENT, useValue: component },
7274
+ ];
7275
+ },
7276
+ data: dialogConfig.data,
7277
+ role: dialogConfig.role,
7278
+ ariaModal: true,
7279
+ ariaLabel: dialogConfig.ariaLabel || null,
7280
+ ariaLabelledBy: dialogConfig.ariaLabelledBy || null,
7281
+ ariaDescribedBy: dialogConfig.ariaDescribedBy || null,
7282
+ direction: dialogConfig.direction ?? undefined,
7283
+ viewContainerRef: dialogConfig.viewContainerRef,
7284
+ injector: dialogConfig.injector,
7285
+ // The dialog covers the page on its own, so there is nothing left
7286
+ // for a scrim to dim.
7287
+ hasBackdrop: false,
7288
+ panelClass: [FULL_SCREEN_PANEL_CLASS, OPENING_CLASS],
7289
+ // The CDK writes the size on the panel and the global strategy then
7290
+ // drops its centering margins, which is how the panel ends up
7291
+ // covering the whole wrapper.
7292
+ width: '100%',
7293
+ height: '100%',
7294
+ positionStrategy: this.overlay.position().global(),
7295
+ scrollStrategy: this.overlay.scrollStrategies.noop(),
7296
+ restoreFocus: this.rootTrigger ?? true,
7297
+ disableClose: true,
7298
+ });
7299
+ this.registerDialog(dialogRef, cdkRef, dialogConfig, previousRef);
7300
+ return dialogRef;
7136
7301
  }
7137
- getVerticalPosition(position, overlapTrigger) {
7138
- switch (position) {
7139
- case 'above':
7140
- return {
7141
- originY: overlapTrigger ? 'bottom' : 'top',
7142
- overlayY: 'bottom',
7143
- };
7144
- case 'center':
7145
- return {
7146
- originY: 'center',
7147
- overlayY: 'center',
7148
- };
7149
- case 'below':
7150
- default:
7151
- return {
7152
- originY: overlapTrigger ? 'top' : 'bottom',
7153
- overlayY: 'top',
7154
- };
7302
+ /** Wires a freshly created dialog into the stack and starts its animation. */
7303
+ registerDialog(dialogRef, cdkRef, dialogConfig, previousRef) {
7304
+ dialogRef.dialogInstance = cdkRef.containerInstance;
7305
+ dialogRef.componentInstance = cdkRef.componentInstance ?? undefined;
7306
+ if (cdkRef.componentRef) {
7307
+ this.bindDataToInputs(cdkRef.componentRef, dialogConfig);
7155
7308
  }
7156
- }
7157
- getFallbackXPosition(position) {
7158
- switch (position) {
7159
- case 'start':
7160
- return 'end';
7161
- case 'end':
7162
- return 'start';
7163
- case 'before':
7164
- return 'after';
7165
- case 'after':
7166
- return 'before';
7167
- case 'center':
7168
- default:
7169
- return 'center';
7309
+ this.refs.push(dialogRef);
7310
+ this.syncPageState();
7311
+ if (previousRef) {
7312
+ setTimeout(() => this.startReplacingAnimation(dialogRef, previousRef), DIALOG_HANDOVER_DELAY_MS);
7170
7313
  }
7171
- }
7172
- getSubMenuXPosition(position) {
7173
- return position === 'before' ? 'before' : 'after';
7174
- }
7175
- getFallbackYPosition(position) {
7176
- switch (position) {
7177
- case 'above':
7178
- return 'below';
7179
- case 'below':
7180
- return 'above';
7181
- case 'center':
7182
- default:
7183
- return 'center';
7314
+ else {
7315
+ this.startOpenAnimation(dialogRef);
7184
7316
  }
7317
+ // Restoring starts with the exit animation, so the dialog underneath is
7318
+ // already coming back while this one is fading out.
7319
+ dialogRef.beforeClosed().subscribe(() => this.restorePreviousDialog(dialogRef));
7320
+ dialogRef.afterClosed().subscribe(() => this.removeRef(dialogRef));
7321
+ // Hiding and showing can also come straight from the reference, so the
7322
+ // page state is driven by what the dialogs report rather than by the
7323
+ // calls that went through this service.
7324
+ dialogRef.hiddenChanged().subscribe(() => this.syncPageState());
7185
7325
  }
7186
- getSubMenuVerticalAlignment(position) {
7187
- return position === 'above' ? 'bottom' : 'top';
7326
+ /**
7327
+ * Hides every dialog that is on screen, the full screen one included, with
7328
+ * the closing animation. They stay alive with their content and state, the
7329
+ * page underneath becomes usable, and showAll() puts them back. The promise
7330
+ * resolves once they are all out of sight.
7331
+ */
7332
+ hideAll() {
7333
+ const visible = this.refs.filter((ref) => !ref.isHidden && !ref.isClosing);
7334
+ return Promise.all(visible.map((ref) => ref.hide())).then(() => undefined);
7188
7335
  }
7189
- getOriginClientRect(origin) {
7190
- if ('getBoundingClientRect' in origin) {
7191
- return origin.getBoundingClientRect();
7336
+ /**
7337
+ * Brings the dialogs back on screen, whether they were hidden one by one or
7338
+ * all at once: the dialog that was on top returns, along with the full
7339
+ * screen dialog it sits in. Dialogs hidden behind another dialog stay
7340
+ * hidden, since that is where they belong once the stack is back.
7341
+ */
7342
+ showAll() {
7343
+ const topRef = this.topRef();
7344
+ if (!topRef) {
7345
+ return;
7192
7346
  }
7193
- return {
7194
- x: origin.x,
7195
- y: origin.y,
7196
- top: origin.y,
7197
- right: origin.x,
7198
- bottom: origin.y,
7199
- left: origin.x,
7200
- width: 0,
7201
- height: 0,
7202
- };
7203
- }
7204
- getViewportMargin(configuredMargin) {
7205
- const minimumMargin = this.getHalfEmInPixels();
7206
- return Math.max(configuredMargin ?? minimumMargin, minimumMargin);
7207
- }
7208
- getHalfEmInPixels() {
7209
- const viewport = this.document.defaultView;
7210
- const fontSize = viewport?.getComputedStyle(this.document.documentElement).fontSize;
7211
- const pixels = fontSize ? Number.parseFloat(fontSize) * 0.5 : 8;
7212
- return Number.isFinite(pixels) ? pixels : 8;
7213
- }
7214
- getOffsetY(position, config) {
7215
- if (config.overlapTrigger || position === 'center') {
7216
- return 0;
7347
+ const fullScreenRef = this.fullScreenDialog;
7348
+ // The full screen dialog is the context the top one sits in, so it
7349
+ // comes back first and the dialog on top ends up on top, with the focus.
7350
+ if (fullScreenRef && fullScreenRef !== topRef && fullScreenRef.isHidden) {
7351
+ fullScreenRef.show();
7352
+ }
7353
+ if (topRef.isHidden) {
7354
+ topRef.show();
7217
7355
  }
7218
- return position === 'above'
7219
- ? config.offsetY * -1
7220
- : config.offsetY;
7221
7356
  }
7222
- getSubMenuOffsetX(position, offsetX) {
7223
- return position === 'before' ? offsetX * -1 : offsetX;
7357
+ /** Closes every open dialog, including the hidden and the full screen ones. */
7358
+ closeAll() {
7359
+ // Closed from the bottom up so a closing dialog never brings back a
7360
+ // hidden dialog that is about to close as well.
7361
+ return Promise.all([...this.refs].map((ref) => ref.close())).then(() => undefined);
7224
7362
  }
7225
- getSubMenuOffsetY(alignment, offsetY) {
7226
- return alignment === 'bottom' ? offsetY * -1 : offsetY;
7363
+ /** Closes the regular dialogs and leaves a full screen dialog alone. */
7364
+ closeRegularDialogs() {
7365
+ const closing = this.refs
7366
+ .filter((ref) => !ref.isFullScreen)
7367
+ .map((ref) => ref.close());
7368
+ return Promise.all(closing).then(() => undefined);
7227
7369
  }
7228
- getScrollStrategy(scrollStrategy) {
7229
- switch (scrollStrategy) {
7230
- case 'block':
7231
- return this.overlay.scrollStrategies.block();
7232
- case 'close':
7233
- return this.overlay.scrollStrategies.close();
7234
- case 'noop':
7235
- return this.overlay.scrollStrategies.noop();
7236
- case 'reposition':
7237
- default:
7238
- return this.overlay.scrollStrategies.reposition();
7239
- }
7370
+ mergeConfig(config) {
7371
+ return {
7372
+ data: config.data,
7373
+ bindDataToInputs: config.bindDataToInputs ?? false,
7374
+ disableCloseEvents: config.disableCloseEvents ?? false,
7375
+ previousDialog: config.previousDialog ?? 'close',
7376
+ role: config.role ?? 'dialog',
7377
+ ariaLabel: config.ariaLabel ?? '',
7378
+ ariaLabelledBy: config.ariaLabelledBy ?? '',
7379
+ ariaDescribedBy: config.ariaDescribedBy ?? '',
7380
+ scheme: config.scheme ?? 'inherit',
7381
+ direction: config.direction ?? null,
7382
+ viewContainerRef: config.viewContainerRef,
7383
+ injector: config.injector ?? this.injector,
7384
+ };
7240
7385
  }
7241
- isMouseEvent(origin) {
7242
- return typeof MouseEvent !== 'undefined' && origin instanceof MouseEvent;
7386
+ /**
7387
+ * Hides or closes the dialog that is on screen. Its scrim stays up until
7388
+ * the dialog taking over is ready for it, so the page behind never
7389
+ * brightens between the two dialogs.
7390
+ */
7391
+ dismissPrevious(previousRef, behavior) {
7392
+ previousRef.overlayRef.backdropElement?.classList.add(SCRIM_KEPT_CLASS);
7393
+ return behavior === 'hide' ? previousRef.hide() : this.closeRegularDialogs();
7243
7394
  }
7244
- isElementRef(origin) {
7245
- return typeof origin === 'object'
7246
- && origin !== null
7247
- && 'nativeElement' in origin
7248
- && origin.nativeElement instanceof HTMLElement;
7395
+ /**
7396
+ * The same choice for a full screen dialog, applied to everything that is
7397
+ * on screen: what it takes over from is a whole context, a full screen
7398
+ * dialog of its own included. `hide` keeps that context alive, and it comes
7399
+ * back as it was once this dialog closes.
7400
+ */
7401
+ dismissPreviousContext(behavior) {
7402
+ return behavior === 'hide' ? this.hideAll() : this.closeAll();
7249
7403
  }
7250
- startOpenAnimation(overlayRef) {
7404
+ startOpenAnimation(dialogRef) {
7405
+ const overlayRef = dialogRef.overlayRef;
7251
7406
  const panel = overlayRef.overlayElement;
7252
7407
  const backdrop = overlayRef.backdropElement;
7253
7408
  // The overlay is inserted already rendered. Keep an initial class for
@@ -7256,22 +7411,65 @@ class MenuService {
7256
7411
  requestAnimationFrame(() => {
7257
7412
  setTimeout(() => {
7258
7413
  panel.getBoundingClientRect();
7259
- panel.classList.remove('md3-menu-opening');
7260
- backdrop?.classList.remove('md3-menu-opening');
7414
+ panel.classList.remove(OPENING_CLASS);
7415
+ backdrop?.classList.remove(OPENING_CLASS);
7261
7416
  }, 100);
7262
7417
  });
7418
+ dialogRef.dialogInstance?.startEnterAnimation();
7419
+ }
7420
+ /** Opening animation for a dialog that took the place of another one. */
7421
+ startReplacingAnimation(dialogRef, previousRef) {
7422
+ if (dialogRef.isClosing) {
7423
+ return;
7424
+ }
7425
+ const panel = dialogRef.overlayRef.overlayElement;
7426
+ const backdrop = dialogRef.overlayRef.backdropElement;
7427
+ this.beginScrimHandover(previousRef, dialogRef);
7428
+ panel.getBoundingClientRect();
7429
+ panel.classList.remove(OPENING_CLASS);
7430
+ backdrop?.classList.remove(OPENING_CLASS);
7431
+ this.endScrimHandover(previousRef, dialogRef);
7432
+ dialogRef.dialogInstance?.startEnterAnimation();
7433
+ }
7434
+ /** Opening animation for a dialog coming back from behind a closing one. */
7435
+ startRestoringAnimation(closingRef, previousRef) {
7436
+ if (previousRef.isClosing) {
7437
+ return;
7438
+ }
7439
+ this.beginScrimHandover(closingRef, previousRef);
7440
+ previousRef.show();
7441
+ this.endScrimHandover(closingRef, previousRef);
7442
+ }
7443
+ /**
7444
+ * Moves both scrims to their new value in a single frame, without a
7445
+ * transition on either side, so the dim behind the dialogs never changes.
7446
+ */
7447
+ beginScrimHandover(fromRef, toRef) {
7448
+ const from = fromRef.overlayRef.backdropElement;
7449
+ const to = toRef.overlayRef.backdropElement;
7450
+ // Nothing to hand the scrim over to, e.g. a full screen dialog: the
7451
+ // outgoing scrim fades out on its own instead.
7452
+ if (!to) {
7453
+ return;
7454
+ }
7455
+ // Nothing to hand over either: the dialog leaving carries no scrim, so
7456
+ // the one coming in fades its own in rather than switching it on in a
7457
+ // single frame. This is a full screen dialog closing over a dialog that
7458
+ // was hidden underneath it.
7459
+ if (!from) {
7460
+ return;
7461
+ }
7462
+ from?.classList.add(SCRIM_INSTANT_CLASS);
7463
+ from?.classList.remove(SCRIM_KEPT_CLASS);
7464
+ to?.classList.add(SCRIM_INSTANT_CLASS);
7263
7465
  }
7264
- createInjector(component, config, menuRef) {
7265
- return Injector.create({
7266
- parent: config.injector,
7267
- providers: [
7268
- // These tokens make the menu controllable from the dynamic
7269
- // component without coupling that component to the service.
7270
- { provide: MenuRef, useValue: menuRef },
7271
- { provide: MENU_DATA, useValue: config.data },
7272
- { provide: MENU_CONFIG, useValue: config },
7273
- { provide: MENU_COMPONENT, useValue: component },
7274
- ],
7466
+ /** Gives the scrims their transitions back once the handover is painted. */
7467
+ endScrimHandover(fromRef, toRef) {
7468
+ const from = fromRef.overlayRef.backdropElement;
7469
+ const to = toRef.overlayRef.backdropElement;
7470
+ requestAnimationFrame(() => {
7471
+ from?.classList.remove(SCRIM_INSTANT_CLASS);
7472
+ to?.classList.remove(SCRIM_INSTANT_CLASS);
7275
7473
  });
7276
7474
  }
7277
7475
  bindDataToInputs(componentRef, config) {
@@ -7284,84 +7482,194 @@ class MenuService {
7284
7482
  componentRef.setInput(inputName, inputValue);
7285
7483
  });
7286
7484
  }
7287
- connectCloseEvents(overlayRef, menuRef) {
7288
- overlayRef.backdropClick().pipe(take(1)).subscribe(() => menuRef.close());
7289
- overlayRef.keydownEvents().pipe(filter((event) => event.key === 'Escape'), take(1)).subscribe(() => menuRef.close());
7485
+ canBindDataToInputs(data) {
7486
+ return typeof data === 'object' && data !== null && !Array.isArray(data);
7487
+ }
7488
+ /** Last dialog of the stack that is not closing yet. */
7489
+ topRef() {
7490
+ for (let index = this.refs.length - 1; index >= 0; index--) {
7491
+ if (!this.refs[index].isClosing) {
7492
+ return this.refs[index];
7493
+ }
7494
+ }
7495
+ return undefined;
7496
+ }
7497
+ /**
7498
+ * Same, without the full screen dialog: it hosts the dialogs opened on top
7499
+ * of it, so it is never the one that gets hidden or closed to make room.
7500
+ */
7501
+ topRegularRef() {
7502
+ const ref = this.topRef();
7503
+ return ref?.isFullScreen ? undefined : ref;
7504
+ }
7505
+ restorePreviousDialog(ref) {
7506
+ const index = this.refs.indexOf(ref);
7507
+ if (index < 1) {
7508
+ return;
7509
+ }
7510
+ for (let previousIndex = index - 1; previousIndex >= 0; previousIndex--) {
7511
+ const previousRef = this.refs[previousIndex];
7512
+ if (previousRef.isClosing) {
7513
+ continue;
7514
+ }
7515
+ // The closing dialog gets the same head start it was given when it
7516
+ // took the screen, and keeps the scrim until then. Nothing to keep
7517
+ // when the dialog underneath has no scrim of its own.
7518
+ if (previousRef.overlayRef.backdropElement) {
7519
+ ref.overlayRef.backdropElement?.classList.add(SCRIM_KEPT_CLASS);
7520
+ }
7521
+ setTimeout(() => {
7522
+ this.showHostFullScreenDialog(previousRef);
7523
+ this.startRestoringAnimation(ref, previousRef);
7524
+ }, DIALOG_HANDOVER_DELAY_MS);
7525
+ return;
7526
+ }
7527
+ }
7528
+ /**
7529
+ * Brings back the full screen dialog a restored dialog sits in. The two are
7530
+ * hidden together when a full screen dialog takes over with
7531
+ * `previousDialog: 'hide'`, so the context has to be on screen again before
7532
+ * the dialog that sits on top of it, exactly as showAll() puts them back.
7533
+ */
7534
+ showHostFullScreenDialog(ref) {
7535
+ if (ref.isFullScreen) {
7536
+ return;
7537
+ }
7538
+ const hostRef = this.fullScreenDialog;
7539
+ if (hostRef?.isHidden && this.refs.indexOf(hostRef) < this.refs.indexOf(ref)) {
7540
+ hostRef.show();
7541
+ }
7542
+ }
7543
+ removeRef(ref) {
7544
+ const index = this.refs.indexOf(ref);
7545
+ if (index === -1) {
7546
+ return;
7547
+ }
7548
+ this.refs.splice(index, 1);
7549
+ if (this.refs.length === 0) {
7550
+ this.rootTrigger = null;
7551
+ }
7552
+ this.syncPageState();
7553
+ }
7554
+ /**
7555
+ * State that belongs to the dialogs as a whole rather than to one of them:
7556
+ * page scrolling, the page being hidden from assistive technology, and
7557
+ * where focus sits. Applied in a task of its own, so a dialog handing the
7558
+ * screen over to another one does not give the page back and take it again
7559
+ * within the same frame.
7560
+ */
7561
+ syncPageState() {
7562
+ if (this.pageStateQueued) {
7563
+ return;
7564
+ }
7565
+ this.pageStateQueued = true;
7566
+ setTimeout(() => {
7567
+ this.pageStateQueued = false;
7568
+ this.applyPageState();
7569
+ });
7570
+ }
7571
+ applyPageState() {
7572
+ const hasVisibleDialog = this.refs.some((ref) => !ref.isHidden);
7573
+ if (hasVisibleDialog) {
7574
+ this.scrollBlock.enable();
7575
+ }
7576
+ else {
7577
+ this.scrollBlock.disable();
7578
+ }
7579
+ if (this.refs.length === 0) {
7580
+ // Nothing is open anymore: the CDK gives the page back to assistive
7581
+ // technology on its own, so there is nothing left to restore here.
7582
+ this.pageAriaHidden.clear();
7583
+ return;
7584
+ }
7585
+ if (hasVisibleDialog) {
7586
+ this.hidePageFromAssistiveTechnology();
7587
+ return;
7588
+ }
7589
+ this.exposePageToAssistiveTechnology();
7590
+ this.releaseFocus();
7591
+ }
7592
+ /**
7593
+ * Gives the page back to assistive technology while every dialog is hidden.
7594
+ * The CDK hides it for as long as a dialog is open, which would otherwise
7595
+ * leave nothing to read at all once the dialogs are out of sight.
7596
+ */
7597
+ exposePageToAssistiveTechnology() {
7598
+ if (this.pageAriaHidden.size > 0) {
7599
+ return;
7600
+ }
7601
+ const container = this.overlayContainer.getContainerElement();
7602
+ Array.from(container.parentElement?.children ?? []).forEach((sibling) => {
7603
+ const value = sibling.getAttribute('aria-hidden');
7604
+ if (sibling === container || value === null) {
7605
+ return;
7606
+ }
7607
+ this.pageAriaHidden.set(sibling, value);
7608
+ sibling.removeAttribute('aria-hidden');
7609
+ });
7610
+ }
7611
+ /** Hides the page again as soon as a dialog is back on screen. */
7612
+ hidePageFromAssistiveTechnology() {
7613
+ this.pageAriaHidden.forEach((value, element) => {
7614
+ element.setAttribute('aria-hidden', value);
7615
+ });
7616
+ this.pageAriaHidden.clear();
7617
+ }
7618
+ /**
7619
+ * Focus follows the dialogs off the screen: it goes back to whatever opened
7620
+ * the stack, so the page stays usable with the keyboard while the dialogs
7621
+ * wait behind it. Focus that already moved somewhere else is left alone.
7622
+ */
7623
+ releaseFocus() {
7624
+ const activeElement = this.document.activeElement;
7625
+ if (this.rootTrigger && (!activeElement || activeElement === this.document.body)) {
7626
+ this.rootTrigger.focus();
7627
+ }
7290
7628
  }
7291
7629
  getFocusedElement() {
7292
7630
  const activeElement = this.document.activeElement;
7293
7631
  return activeElement instanceof HTMLElement ? activeElement : null;
7294
7632
  }
7295
- canBindDataToInputs(data) {
7296
- return typeof data === 'object' && data !== null && !Array.isArray(data);
7297
- }
7298
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: MenuService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
7299
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: MenuService, providedIn: 'root' });
7633
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DialogService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
7634
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DialogService, providedIn: 'root' });
7300
7635
  }
7301
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: MenuService, decorators: [{
7636
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DialogService, decorators: [{
7302
7637
  type: Injectable,
7303
7638
  args: [{
7304
7639
  providedIn: 'root',
7305
7640
  }]
7306
7641
  }] });
7307
7642
 
7308
- class MenuGroup {
7309
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: MenuGroup, deps: [], target: i0.ɵɵFactoryTarget.Component });
7310
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.0", type: MenuGroup, isStandalone: true, selector: "md3-menu-group", host: { attributes: { "role": "group" } }, ngImport: i0, template: "<ng-content></ng-content>\n", styles: [":host{display:flex;flex-direction:column;gap:.125em;padding:.25em;background-color:var(--background-color);color:var(--foreground-color);border-radius:var(--menu-group-radius);box-shadow:var(--menu-group-shadow)}:host:first-child{border-start-start-radius:var(--border-radius);border-start-end-radius:var(--border-radius)}:host:last-child{border-end-start-radius:var(--border-radius);border-end-end-radius:var(--border-radius)}:host ::ng-deep md3-divider{margin:.125em .5em}:host ::ng-deep [md3-menu-item]:first-child .md3-menu-item-container{--border-top-start-r: var(--md-border-radius-medium);--border-top-end-r: var(--md-border-radius-medium);border-start-start-radius:var(--border-top-start-r);border-start-end-radius:var(--border-top-end-r)}:host ::ng-deep [md3-menu-item]:last-child .md3-menu-item-container{--border-bottom-start-r: var(--md-border-radius-medium);--border-bottom-end-r: var(--md-border-radius-medium);border-end-start-radius:var(--border-bottom-start-r);border-end-end-radius:var(--border-bottom-end-r)}.md3-menu-group-label{padding:.5rem var(--menu-item-inline-padding, .75rem) .25rem;color:rgb(var(--md-scheme-on-surface-variant));font-size:.75rem;font-weight:500;line-height:1rem;letter-spacing:.03125rem}\n"] });
7643
+ class DialogHeader {
7644
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DialogHeader, deps: [], target: i0.ɵɵFactoryTarget.Component });
7645
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.0", type: DialogHeader, isStandalone: true, selector: "md3-dialog-header", ngImport: i0, template: "<ng-content select=\"[md3-icon-element]\"></ng-content>\n<div class=\"md3-dialog-title\">\n <md3-type-headline size=\"small\">\n <ng-content></ng-content>\n </md3-type-headline>\n</div>", styles: [":host{display:block;margin:1.5em 1.5em 1em;text-align:center}:host .md3-dialog-title{text-align:start;color:rgb(var(--md-scheme-on-surface))}:host ::ng-deep [md3-icon-element]{color:rgb(var(--md-scheme-secondary))}:host ::ng-deep [md3-icon-element]+.md3-dialog-title{margin-top:1em;text-align:center}\n"], dependencies: [{ kind: "directive", type: TypeHeadline, selector: "md3-type-headline, [md3-type-headline]", inputs: ["size", "color", "emphasized"] }] });
7311
7646
  }
7312
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: MenuGroup, decorators: [{
7647
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DialogHeader, decorators: [{
7313
7648
  type: Component,
7314
- args: [{ selector: 'md3-menu-group', imports: [], host: {
7315
- 'role': 'group',
7316
- }, template: "<ng-content></ng-content>\n", styles: [":host{display:flex;flex-direction:column;gap:.125em;padding:.25em;background-color:var(--background-color);color:var(--foreground-color);border-radius:var(--menu-group-radius);box-shadow:var(--menu-group-shadow)}:host:first-child{border-start-start-radius:var(--border-radius);border-start-end-radius:var(--border-radius)}:host:last-child{border-end-start-radius:var(--border-radius);border-end-end-radius:var(--border-radius)}:host ::ng-deep md3-divider{margin:.125em .5em}:host ::ng-deep [md3-menu-item]:first-child .md3-menu-item-container{--border-top-start-r: var(--md-border-radius-medium);--border-top-end-r: var(--md-border-radius-medium);border-start-start-radius:var(--border-top-start-r);border-start-end-radius:var(--border-top-end-r)}:host ::ng-deep [md3-menu-item]:last-child .md3-menu-item-container{--border-bottom-start-r: var(--md-border-radius-medium);--border-bottom-end-r: var(--md-border-radius-medium);border-end-start-radius:var(--border-bottom-start-r);border-end-end-radius:var(--border-bottom-end-r)}.md3-menu-group-label{padding:.5rem var(--menu-item-inline-padding, .75rem) .25rem;color:rgb(var(--md-scheme-on-surface-variant));font-size:.75rem;font-weight:500;line-height:1rem;letter-spacing:.03125rem}\n"] }]
7649
+ args: [{ selector: 'md3-dialog-header', imports: [
7650
+ TypeHeadline
7651
+ ], template: "<ng-content select=\"[md3-icon-element]\"></ng-content>\n<div class=\"md3-dialog-title\">\n <md3-type-headline size=\"small\">\n <ng-content></ng-content>\n </md3-type-headline>\n</div>", styles: [":host{display:block;margin:1.5em 1.5em 1em;text-align:center}:host .md3-dialog-title{text-align:start;color:rgb(var(--md-scheme-on-surface))}:host ::ng-deep [md3-icon-element]{color:rgb(var(--md-scheme-secondary))}:host ::ng-deep [md3-icon-element]+.md3-dialog-title{margin-top:1em;text-align:center}\n"] }]
7317
7652
  }] });
7318
7653
 
7319
- class MenuItem {
7320
- el;
7321
- label = input('', { ...(ngDevMode ? { debugName: "label" } : /* istanbul ignore next */ {}), alias: 'label' });
7322
- supportingText = input(null, { ...(ngDevMode ? { debugName: "supportingText" } : /* istanbul ignore next */ {}), alias: 'supporting-text' });
7323
- trailingText = input(null, { ...(ngDevMode ? { debugName: "trailingText" } : /* istanbul ignore next */ {}), alias: 'trailing-text' });
7324
- selected = model(null, { ...(ngDevMode ? { debugName: "selected" } : /* istanbul ignore next */ {}), alias: 'selected' });
7325
- iconElements = contentChildren(IconElement, /* @ts-ignore */
7326
- ...(ngDevMode ? [{ debugName: "iconElements" }] : /* istanbul ignore next */ []));
7327
- get leading() {
7328
- return this.iconElements().find((icon) => icon.iconType() === 'leading');
7329
- }
7330
- get trailing() {
7331
- return this.iconElements().find((icon) => icon.iconType() === 'trailing');
7332
- }
7333
- get showLeadingIcon() {
7334
- return (!!this.leading && !this.selected()) || this.selected() == true;
7335
- }
7336
- onHostClick() {
7337
- const selected = this.selected();
7338
- if (selected === null) {
7339
- return;
7340
- }
7341
- this.selected.set(!selected);
7342
- }
7343
- constructor(el) {
7344
- this.el = el;
7345
- }
7346
- get element() {
7347
- return this.el.nativeElement;
7348
- }
7349
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: MenuItem, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
7350
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: MenuItem, isStandalone: true, selector: "button[md3-menu-item], a[md3-menu-item]", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, supportingText: { classPropertyName: "supportingText", publicName: "supporting-text", isSignal: true, isRequired: false, transformFunction: null }, trailingText: { classPropertyName: "trailingText", publicName: "trailing-text", isSignal: true, isRequired: false, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selected: "selectedChange" }, host: { listeners: { "click": "onHostClick()" } }, queries: [{ propertyName: "iconElements", predicate: IconElement, isSignal: true }], ngImport: i0, template: "@if (selected() !== null) {\n <input type=\"checkbox\" md3-input-element [checked]=\"selected()\">\n}\n\n<div class=\"md3-menu-item-container\" md3-state-component>\n <div class=\"md3-menu-item-leading\" [class.has-icon]=\"showLeadingIcon\">\n <ng-content select=\"[md3-icon-element=leading]\"></ng-content>\n @if (!leading) {\n <md3-icon>check</md3-icon>\n }\n </div>\n\n <div class=\"md3-menu-item-content\">\n <md3-type-label size=\"large\">{{ label() }}</md3-type-label>\n @if (supportingText()?.length) {\n <md3-type-body size=\"small\">{{ supportingText() }}</md3-type-body>\n }\n </div>\n\n <div class=\"md3-menu-item-trailing\">\n <ng-content select=\"md3-badge\"></ng-content>\n @if (trailingText()?.length) {\n <md3-type-title size=\"small\">{{ trailingText() }}</md3-type-title>\n }\n <ng-content select=\"[md3-icon-element=trailing]\"></ng-content>\n </div>\n</div>", styles: [":host{--state-color: var(--menu-item-state-color);--border-radius: var(--menu-item-border-radius);--outline-color: var(--menu-item-outline-color);--outline-width: var(--menu-item-outline-width);--outline-offset: var(--menu-item-outline-offset);position:relative;display:block;padding:0;border:0;background:transparent;color:var(--menu-item-label-color);font:inherit;text-align:start;text-decoration:none;cursor:pointer;outline:none;appearance:none;-webkit-user-select:none;user-select:none;z-index:0}:host:focus{z-index:1}:host:focus:not(:hover) .md3-menu-item-container:after{opacity:1}:host:active{z-index:2}:host:active .md3-menu-item-container:after{opacity:0}:host .md3-menu-item-container{display:flex;align-items:center;min-height:3em;padding:0 .75em;border-radius:var(--border-radius);transition:background-color var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing),color var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing)}:host .md3-menu-item-container .md3-menu-item-leading{display:inline-flex;align-items:center;justify-content:center;font-size:inherit;opacity:0;transform:scale(1.2);width:0;height:1.25em;color:var(--menu-item-supporting-color);transition:all var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing)}:host .md3-menu-item-container .md3-menu-item-leading ::ng-deep [md3-icon-element]{font-size:1.25em!important;height:1em!important;width:1em!important;line-height:1em!important}:host .md3-menu-item-container .md3-menu-item-leading.has-icon{margin-inline-end:.5em;opacity:1;transform:scale(1);height:1.25em;width:1.25em}:host .md3-menu-item-container .md3-menu-item-trailing{flex:0 1 content;display:inline-flex;gap:.5em;align-items:center;justify-content:center;font-size:inherit;margin-inline-start:.5em;color:var(--menu-item-supporting-color)}:host .md3-menu-item-container .md3-menu-item-trailing:empty{margin:0}:host .md3-menu-item-container .md3-menu-item-trailing ::ng-deep md3-badge{background-color:var(--menu-item-badge-background);color:var(--menu-item-badge-foreground)}:host .md3-menu-item-container .md3-menu-item-trailing .md3-text-title.md3-text-small{margin:0;padding:0;white-space:nowrap;letter-spacing:.007143em}:host .md3-menu-item-container .md3-menu-item-trailing ::ng-deep [md3-icon-element]{font-size:1.25em!important;height:1em!important;width:1em!important;line-height:1em!important}:host .md3-menu-item-container .md3-menu-item-content{flex:1 1 auto}:host .md3-menu-item-container .md3-menu-item-content .md3-text-label.md3-text-large{display:block;margin:0;padding:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;letter-spacing:.007143em;color:var(--menu-item-label-color)}:host .md3-menu-item-container .md3-menu-item-content .md3-text-body.md3-text-small{display:block;margin:0;padding:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;letter-spacing:.0333334em;color:var(--menu-item-supporting-color)}:host [md3-input-element]{position:absolute;height:0;width:0;opacity:0;pointer-events:none}:host [md3-input-element]+.md3-menu-item-container .md3-menu-item-leading{opacity:0;transform:scale(1.2);width:0}:host [md3-input-element]:checked+.md3-menu-item-container{--menu-item-label-color: var(--menu-item-selected-foreground);--menu-item-supporting-color: var(--menu-item-selected-foreground);--border-radius: var(--md-border-radius-medium);background-color:var(--menu-item-selected-background)}:host [md3-input-element]:checked+.md3-menu-item-container .md3-menu-item-leading{opacity:1;transform:scale(1);width:1.25em}:host:disabled{cursor:default}:host:disabled .md3-menu-item-container{--menu-item-label-color: var(--menu-item-label-color);--menu-item-supporting-color: var(--menu-item-label-color);--menu-item-selected-background: transparent;--menu-item-selected-foreground: var(--menu-item-label-color);pointer-events:none;opacity:.38}:host:disabled .md3-menu-item-container.md3-state-layer,:host:disabled .md3-menu-item-container:after{display:none}\n"], dependencies: [{ kind: "directive", type: InputElement, selector: "[md3-input-element]" }, { kind: "directive", type: StateComponent, selector: "[md3-state-component]" }, { kind: "component", type: MaterialIcon, selector: "md3-icon", inputs: ["icon-version", "filled", "icon-style", "icon-size", "bi-directional"] }, { kind: "directive", type: TypeLabel, selector: "md3-type-label, [md3-type-label]", inputs: ["size", "color", "emphasized"] }, { kind: "directive", type: TypeBody, selector: "md3-type-body, [md3-type-body]", inputs: ["size", "color", "emphasized"] }, { kind: "directive", type: TypeTitle, selector: "md3-type-title, [md3-type-title]", inputs: ["size", "color", "emphasized"] }] });
7654
+ class DialogBody {
7655
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DialogBody, deps: [], target: i0.ɵɵFactoryTarget.Component });
7656
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.0", type: DialogBody, isStandalone: true, selector: "md3-dialog-body", host: { classAttribute: "md3-scrollable" }, ngImport: i0, template: "<ng-content></ng-content>", styles: [":host{display:block;margin:1em 1.5em 1.5em;color:rgb(var(--md-scheme-on-surface-variant))}:host .md3-text-body.md3-text-medium{letter-spacing:.017857em}\n"] });
7351
7657
  }
7352
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: MenuItem, decorators: [{
7658
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DialogBody, decorators: [{
7353
7659
  type: Component,
7354
- args: [{ selector: 'button[md3-menu-item], a[md3-menu-item]', imports: [
7355
- InputElement,
7356
- StateComponent,
7357
- MaterialIcon,
7358
- TypeLabel,
7359
- TypeBody,
7360
- TypeTitle,
7361
- ], host: {
7362
- '(click)': 'onHostClick()',
7363
- }, template: "@if (selected() !== null) {\n <input type=\"checkbox\" md3-input-element [checked]=\"selected()\">\n}\n\n<div class=\"md3-menu-item-container\" md3-state-component>\n <div class=\"md3-menu-item-leading\" [class.has-icon]=\"showLeadingIcon\">\n <ng-content select=\"[md3-icon-element=leading]\"></ng-content>\n @if (!leading) {\n <md3-icon>check</md3-icon>\n }\n </div>\n\n <div class=\"md3-menu-item-content\">\n <md3-type-label size=\"large\">{{ label() }}</md3-type-label>\n @if (supportingText()?.length) {\n <md3-type-body size=\"small\">{{ supportingText() }}</md3-type-body>\n }\n </div>\n\n <div class=\"md3-menu-item-trailing\">\n <ng-content select=\"md3-badge\"></ng-content>\n @if (trailingText()?.length) {\n <md3-type-title size=\"small\">{{ trailingText() }}</md3-type-title>\n }\n <ng-content select=\"[md3-icon-element=trailing]\"></ng-content>\n </div>\n</div>", styles: [":host{--state-color: var(--menu-item-state-color);--border-radius: var(--menu-item-border-radius);--outline-color: var(--menu-item-outline-color);--outline-width: var(--menu-item-outline-width);--outline-offset: var(--menu-item-outline-offset);position:relative;display:block;padding:0;border:0;background:transparent;color:var(--menu-item-label-color);font:inherit;text-align:start;text-decoration:none;cursor:pointer;outline:none;appearance:none;-webkit-user-select:none;user-select:none;z-index:0}:host:focus{z-index:1}:host:focus:not(:hover) .md3-menu-item-container:after{opacity:1}:host:active{z-index:2}:host:active .md3-menu-item-container:after{opacity:0}:host .md3-menu-item-container{display:flex;align-items:center;min-height:3em;padding:0 .75em;border-radius:var(--border-radius);transition:background-color var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing),color var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing)}:host .md3-menu-item-container .md3-menu-item-leading{display:inline-flex;align-items:center;justify-content:center;font-size:inherit;opacity:0;transform:scale(1.2);width:0;height:1.25em;color:var(--menu-item-supporting-color);transition:all var(--md-motion-standard-fast-spatial-duration) var(--md-motion-standard-fast-spatial-easing)}:host .md3-menu-item-container .md3-menu-item-leading ::ng-deep [md3-icon-element]{font-size:1.25em!important;height:1em!important;width:1em!important;line-height:1em!important}:host .md3-menu-item-container .md3-menu-item-leading.has-icon{margin-inline-end:.5em;opacity:1;transform:scale(1);height:1.25em;width:1.25em}:host .md3-menu-item-container .md3-menu-item-trailing{flex:0 1 content;display:inline-flex;gap:.5em;align-items:center;justify-content:center;font-size:inherit;margin-inline-start:.5em;color:var(--menu-item-supporting-color)}:host .md3-menu-item-container .md3-menu-item-trailing:empty{margin:0}:host .md3-menu-item-container .md3-menu-item-trailing ::ng-deep md3-badge{background-color:var(--menu-item-badge-background);color:var(--menu-item-badge-foreground)}:host .md3-menu-item-container .md3-menu-item-trailing .md3-text-title.md3-text-small{margin:0;padding:0;white-space:nowrap;letter-spacing:.007143em}:host .md3-menu-item-container .md3-menu-item-trailing ::ng-deep [md3-icon-element]{font-size:1.25em!important;height:1em!important;width:1em!important;line-height:1em!important}:host .md3-menu-item-container .md3-menu-item-content{flex:1 1 auto}:host .md3-menu-item-container .md3-menu-item-content .md3-text-label.md3-text-large{display:block;margin:0;padding:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;letter-spacing:.007143em;color:var(--menu-item-label-color)}:host .md3-menu-item-container .md3-menu-item-content .md3-text-body.md3-text-small{display:block;margin:0;padding:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;letter-spacing:.0333334em;color:var(--menu-item-supporting-color)}:host [md3-input-element]{position:absolute;height:0;width:0;opacity:0;pointer-events:none}:host [md3-input-element]+.md3-menu-item-container .md3-menu-item-leading{opacity:0;transform:scale(1.2);width:0}:host [md3-input-element]:checked+.md3-menu-item-container{--menu-item-label-color: var(--menu-item-selected-foreground);--menu-item-supporting-color: var(--menu-item-selected-foreground);--border-radius: var(--md-border-radius-medium);background-color:var(--menu-item-selected-background)}:host [md3-input-element]:checked+.md3-menu-item-container .md3-menu-item-leading{opacity:1;transform:scale(1);width:1.25em}:host:disabled{cursor:default}:host:disabled .md3-menu-item-container{--menu-item-label-color: var(--menu-item-label-color);--menu-item-supporting-color: var(--menu-item-label-color);--menu-item-selected-background: transparent;--menu-item-selected-foreground: var(--menu-item-label-color);pointer-events:none;opacity:.38}:host:disabled .md3-menu-item-container.md3-state-layer,:host:disabled .md3-menu-item-container:after{display:none}\n"] }]
7364
- }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], supportingText: [{ type: i0.Input, args: [{ isSignal: true, alias: "supporting-text", required: false }] }], trailingText: [{ type: i0.Input, args: [{ isSignal: true, alias: "trailing-text", required: false }] }], selected: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }, { type: i0.Output, args: ["selectedChange"] }], iconElements: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => IconElement), { isSignal: true }] }] } });
7660
+ args: [{ selector: 'md3-dialog-body', imports: [], host: {
7661
+ 'class': 'md3-scrollable',
7662
+ }, template: "<ng-content></ng-content>", styles: [":host{display:block;margin:1em 1.5em 1.5em;color:rgb(var(--md-scheme-on-surface-variant))}:host .md3-text-body.md3-text-medium{letter-spacing:.017857em}\n"] }]
7663
+ }] });
7664
+
7665
+ class DialogActions {
7666
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DialogActions, deps: [], target: i0.ɵɵFactoryTarget.Component });
7667
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.0", type: DialogActions, isStandalone: true, selector: "md3-dialog-actions", ngImport: i0, template: "<div class=\"md3-actions\">\n <ng-content></ng-content>\n</div>", styles: [":host{display:block;margin:1.5em}:host .md3-actions{display:flex;gap:1em;flex-direction:row-reverse}\n"] });
7668
+ }
7669
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DialogActions, decorators: [{
7670
+ type: Component,
7671
+ args: [{ selector: 'md3-dialog-actions', imports: [], template: "<div class=\"md3-actions\">\n <ng-content></ng-content>\n</div>", styles: [":host{display:block;margin:1.5em}:host .md3-actions{display:flex;gap:1em;flex-direction:row-reverse}\n"] }]
7672
+ }] });
7365
7673
 
7366
7674
  class SideSheetHeader {
7367
7675
  title = input('', { ...(ngDevMode ? { debugName: "title" } : /* istanbul ignore next */ {}), alias: 'title' });
@@ -7764,5 +8072,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
7764
8072
  * Generated bundle index. Do not edit.
7765
8073
  */
7766
8074
 
7767
- export { AppBar, AppBarLogo, Avatar, BOTTOM_SHEET_COMPONENT, BOTTOM_SHEET_CONFIG, BOTTOM_SHEET_DATA, Badge, BottomSheetRef, BottomSheetService, Button, ButtonGroup, CAROUSEL_STRATEGIES, Card, Carousel, CarouselItem, Checkbox, ChipAvatar, Chips, CircularProgressIndicator, DIALOG_COMPONENT, DIALOG_CONFIG, DIALOG_DATA, Dialog, DialogActions, DialogBody, DialogHeader, DialogRef, DialogService, Divider, FloatingActionButton, FullScreenDialog, Grid, GridItem, IconButton, IconElement, InputElement, Layout, LayoutService, LinearProgressIndicator, List, ListItem, ListItemPrimaryAction, ListLeading, ListSlot, LoadingIndicator, MENU_COMPONENT, MENU_CONFIG, MENU_DATA, MaterialIcon, Menu, MenuGroup, MenuItem, MenuRef, MenuService, ModalBottomSheetRef, NavigationBar, NavigationGroup, NavigationItem, NavigationRail, RadioButton, SIDE_SHEET_COMPONENT, SIDE_SHEET_CONFIG, SIDE_SHEET_DATA, SNACKBAR_ACTION_LABEL, SNACKBAR_CONFIG, SNACKBAR_MESSAGE, Scaffold, ScaffoldBar, ScaffoldPane, ScaffoldRail, SheetsService, SideSheetActions, SideSheetBody, SideSheetHeader, SideSheetRef, Slider, Snackbar, SnackbarRef, SnackbarService, SplitButton, StandardBottomSheetRef, StateComponent, SupportingText, Switch, TextField, Toolbar, ToolbarItem, TypeBody, TypeDisplay, TypeHeadline, TypeLabel, TypeTitle, ViewportService, buildGeometry, clampIndex, indexForScrollOffset, multiBrowseStrategy, parseCarouselAspectRatio, resolveItemGeometry, resolveState, scrollOffsetForIndex, sizeBandFor };
8075
+ export { AppBar, AppBarLogo, Avatar, BOTTOM_SHEET_COMPONENT, BOTTOM_SHEET_CONFIG, BOTTOM_SHEET_DATA, Badge, BottomSheetRef, BottomSheetService, Button, ButtonGroup, CAROUSEL_STRATEGIES, Card, Carousel, CarouselItem, Checkbox, ChipAvatar, Chips, CircularProgressIndicator, DIALOG_COMPONENT, DIALOG_CONFIG, DIALOG_DATA, Dialog, DialogActions, DialogBody, DialogHeader, DialogRef, DialogService, Divider, FloatingActionButton, FullScreenDialog, Grid, GridItem, IconButton, IconElement, InputElement, Layout, LayoutService, LinearProgressIndicator, List, ListItem, ListItemPrimaryAction, ListLeading, ListSlot, LoadingIndicator, MENU_COMPONENT, MENU_CONFIG, MENU_DATA, MaterialIcon, Menu, MenuGroup, MenuItem, MenuRef, MenuService, ModalBottomSheetRef, NavigationBar, NavigationGroup, NavigationItem, NavigationRail, RadioButton, SIDE_SHEET_COMPONENT, SIDE_SHEET_CONFIG, SIDE_SHEET_DATA, SNACKBAR_ACTION_LABEL, SNACKBAR_CONFIG, SNACKBAR_MESSAGE, Scaffold, ScaffoldBar, ScaffoldPane, ScaffoldRail, SelectField, SheetsService, SideSheetActions, SideSheetBody, SideSheetHeader, SideSheetRef, Slider, Snackbar, SnackbarRef, SnackbarService, SplitButton, StandardBottomSheetRef, StateComponent, SupportingText, Switch, TextField, Toolbar, ToolbarItem, TypeBody, TypeDisplay, TypeHeadline, TypeLabel, TypeTitle, ViewportService, buildGeometry, clampIndex, indexForScrollOffset, multiBrowseStrategy, parseCarouselAspectRatio, resolveItemGeometry, resolveState, scrollOffsetForIndex, sizeBandFor };
7768
8076
  //# sourceMappingURL=almoamendev-ngx-md3.mjs.map