From 624d4ff31da1680ea2f1ff1ac5edf072f80e930e Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Sat, 18 May 2024 11:31:01 +0200 Subject: [PATCH] refactor(material/dialog): simplify structural styles Simplifies the dialog styles to make them smaller and easier to follow. --- src/material/core/tokens/m2/mat/_dialog.scss | 2 + src/material/core/tokens/m2/mdc/_dialog.scss | 6 +- src/material/core/tokens/m3/mat/_dialog.scss | 1 + src/material/core/tokens/m3/mdc/_dialog.scss | 16 -- src/material/dialog/_dialog-theme.scss | 13 +- .../_mdc-dialog-structure-overrides.scss | 58 ----- src/material/dialog/dialog-container.html | 2 +- src/material/dialog/dialog.scss | 243 ++++++++++++++---- 8 files changed, 203 insertions(+), 138 deletions(-) delete mode 100644 src/material/dialog/_mdc-dialog-structure-overrides.scss diff --git a/src/material/core/tokens/m2/mat/_dialog.scss b/src/material/core/tokens/m2/mat/_dialog.scss index 4f299526f94a..ec1e888f4888 100644 --- a/src/material/core/tokens/m2/mat/_dialog.scss +++ b/src/material/core/tokens/m2/mat/_dialog.scss @@ -1,3 +1,4 @@ +@use '@material/elevation/elevation-theme' as mdc-elevation; @use '../../token-utils'; @use '../../../style/sass-utils'; @@ -8,6 +9,7 @@ $prefix: (mat, dialog); // but may be in a future version of the theming API. @function get-unthemable-tokens() { @return ( + container-elevation-shadow: mdc-elevation.elevation-box-shadow(24), container-max-width: 80vw, container-small-max-width: 80vw, container-min-width: 0, diff --git a/src/material/core/tokens/m2/mdc/_dialog.scss b/src/material/core/tokens/m2/mdc/_dialog.scss index 51d13ae801aa..86725a7ab302 100644 --- a/src/material/core/tokens/m2/mdc/_dialog.scss +++ b/src/material/core/tokens/m2/mdc/_dialog.scss @@ -13,15 +13,13 @@ $prefix: (mdc, dialog); // our CSS. @function get-unthemable-tokens() { @return ( - // Height of the container's elevation. - container-elevation: 24, - // Color of the elevation shadow. - container-shadow-color: #000, // Border radius of the container. container-shape: 4px, // ============================================================================================= // = TOKENS NOT USED IN ANGULAR MATERIAL = // ============================================================================================= + container-elevation: null, + container-shadow-color: null, with-divider-divider-height: null, with-divider-divider-color: null, with-icon-icon-size: null, diff --git a/src/material/core/tokens/m3/mat/_dialog.scss b/src/material/core/tokens/m3/mat/_dialog.scss index 0524b04e48a3..f63cecf0961d 100644 --- a/src/material/core/tokens/m3/mat/_dialog.scss +++ b/src/material/core/tokens/m3/mat/_dialog.scss @@ -10,6 +10,7 @@ $prefix: (mat, dialog); /// @return {Map} A set of custom tokens for the dialog @function get-tokens($systems, $exclude-hardcoded, $token-slots) { $tokens: ( + container-elevation-shadow: token-utils.hardcode(none, $exclude-hardcoded), container-max-width: token-utils.hardcode(560px, $exclude-hardcoded), container-small-max-width: token-utils.hardcode(calc(100vw - 32px), $exclude-hardcoded), container-min-width: token-utils.hardcode(280px, $exclude-hardcoded), diff --git a/src/material/core/tokens/m3/mdc/_dialog.scss b/src/material/core/tokens/m3/mdc/_dialog.scss index 9f20cbb00820..0c5803cf1b23 100644 --- a/src/material/core/tokens/m3/mdc/_dialog.scss +++ b/src/material/core/tokens/m3/mdc/_dialog.scss @@ -1,4 +1,3 @@ -@use 'sass:map'; @use '../../token-utils'; // The prefix used to generate the fully qualified name for tokens in this file. @@ -20,20 +19,5 @@ $prefix: (mdc, dialog); headline-weight: subhead-weight, )); - @if map.get($tokens, container-elevation) != null { - $tokens: map.merge($tokens, ( - // The spec has the dialog at an elevation of 3 which is consistent with the current - // version of the tokens (0_161), however both the designs and MDC's implementation - // have the elevation set to 0. Set it manually to 0 here since the value in the - // exported tokens is likely outdated. - container-elevation: 0, - - // This color needs to be supplied for MDC to produce the shadow. Technically we don't - // have to provide it since the elevation is set to 0 above, but we do it in case - // the value changes in the future. - container-shadow-color: #000, - )); - } - @return token-utils.namespace-tokens($prefix, $tokens, $token-slots); } diff --git a/src/material/dialog/_dialog-theme.scss b/src/material/dialog/_dialog-theme.scss index 617852226160..3e02b5c4319d 100644 --- a/src/material/dialog/_dialog-theme.scss +++ b/src/material/dialog/_dialog-theme.scss @@ -1,5 +1,4 @@ @use 'sass:map'; -@use '@material/dialog/dialog-theme' as mdc-dialog-theme; @use '../core/style/sass-utils'; @use '../core/tokens/m2/mdc/dialog' as tokens-mdc-dialog; @use '../core/tokens/m2/mat/dialog' as tokens-mat-dialog; @@ -16,7 +15,8 @@ @else { // Add default values for tokens not related to color, typography, or density. @include sass-utils.current-selector-or-root() { - @include mdc-dialog-theme.theme(tokens-mdc-dialog.get-unthemable-tokens()); + @include token-utils.create-token-values( + tokens-mdc-dialog.$prefix, tokens-mdc-dialog.get-unthemable-tokens()); @include token-utils.create-token-values( tokens-mat-dialog.$prefix, tokens-mat-dialog.get-unthemable-tokens()); } @@ -29,7 +29,8 @@ } @else { @include sass-utils.current-selector-or-root() { - @include mdc-dialog-theme.theme(tokens-mdc-dialog.get-color-tokens($theme)); + @include token-utils.create-token-values( + tokens-mdc-dialog.$prefix, tokens-mdc-dialog.get-color-tokens($theme)); @include token-utils.create-token-values( tokens-mat-dialog.$prefix, tokens-mat-dialog.get-color-tokens($theme)); } @@ -42,7 +43,8 @@ } @else { @include sass-utils.current-selector-or-root() { - @include mdc-dialog-theme.theme(tokens-mdc-dialog.get-typography-tokens($theme)); + @include token-utils.create-token-values( + tokens-mdc-dialog.$prefix, tokens-mdc-dialog.get-typography-tokens($theme)); @include token-utils.create-token-values( tokens-mat-dialog.$prefix, tokens-mat-dialog.get-typography-tokens($theme)); } @@ -88,7 +90,8 @@ @include validation.selector-defined( 'Calls to Angular Material theme mixins with an M3 theme must be wrapped in a selector'); @if ($tokens != ()) { - @include mdc-dialog-theme.theme(map.get($tokens, tokens-mdc-dialog.$prefix)); + @include token-utils.create-token-values( + tokens-mdc-dialog.$prefix, map.get($tokens, tokens-mdc-dialog.$prefix)); @include token-utils.create-token-values( tokens-mat-dialog.$prefix, map.get($tokens, tokens-mat-dialog.$prefix)); } diff --git a/src/material/dialog/_mdc-dialog-structure-overrides.scss b/src/material/dialog/_mdc-dialog-structure-overrides.scss deleted file mode 100644 index 037fd7c8887c..000000000000 --- a/src/material/dialog/_mdc-dialog-structure-overrides.scss +++ /dev/null @@ -1,58 +0,0 @@ -// Mixin that can be included to override the default MDC dialog styles to fit -// our needs. See individual comments for context on why certain styles need to be modified. -@mixin private-dialog-structure-overrides($content-max-height) { - // MDC dialog sets max-height and max-width on the `mdc-dialog__surface` element. This - // element is the parent of the portal outlet. This means that the actual user-content - // is scrollable, but as per Material Design specification, only the dialog content - // (indicated by `matDialogContent`) should be scrollable while title and actions are fixed. - // This is not an issue for MDC because they make the assumption that the content, title and - // action elements are direct children of the surface element. We work around this by setting - // an explicit max-height for the content element, so that the content is scrollable. This - // matches the behavior from the non-MDC dialog and is backwards compatible. - .mat-mdc-dialog-content { - max-height: $content-max-height; - } - - .mat-mdc-dialog-container { - // MDC by default expands the dialog using a fixed position to the viewport dimensions. - // MDC does this because the dialog element contains the backdrop/scrim too. This is not - // the case for our implementation of the dialog that uses the CDK overlay backdrop. We - // update the dialog to a `static` position and reset the height/width so that the dialog - // scales based on the content, respecting the boundaries of the CDK overlay container. - // This is necessary to support for custom position strategies in the overlay. - position: static; - // The MDC dialog is designed to always stay in the DOM. Hence MDC sets `display: none` - // when the dialog is closed. Since we only attach the dialog to the DOM when the dialog - // should open, and we want to be able to focus the dialog immediately when we attach it, - // we override the default `display` so that the dialog is focusable. - display: block; - - // MDC sets `min-height`, `max-height`, `min-width` and `max-height` We can't rely on - // this for the dialog content scrolling (as explained above), and since we allow for - // custom positioning through overlay configuration, we remove the rectangle - // restrictions and scale based on the CDK overlay container. - &, .mdc-dialog__container, .mdc-dialog__surface { - max-height: inherit; - min-height: inherit; - min-width: inherit; - max-width: inherit; - } - - .mdc-dialog__surface { - width: 100%; - height: 100%; - } - } - - // When a component is passed into the dialog, the host element interrupts - // the `display:flex` from affecting the dialog title, content, and - // actions. To fix this, we make the component host `display: contents` by - // marking its host with the `mat-mdc-dialog-component-host` class. - // - // Note that this problem does not exist when a template ref is used since - // the title, contents, and actions are then nested directly under the - // dialog surface. - .mat-mdc-dialog-component-host { - display: contents; - } -} diff --git a/src/material/dialog/dialog-container.html b/src/material/dialog/dialog-container.html index ed6b033bd75e..b22dd496289b 100644 --- a/src/material/dialog/dialog-container.html +++ b/src/material/dialog/dialog-container.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/material/dialog/dialog.scss b/src/material/dialog/dialog.scss index 37446b734fed..8b0a1a3085f4 100644 --- a/src/material/dialog/dialog.scss +++ b/src/material/dialog/dialog.scss @@ -1,12 +1,10 @@ -@use '@material/dialog' as mdc-dialog; -@use '@material/animation/animation' as mdc-animation; -@use '@material/dialog/dialog-theme' as mdc-dialog-theme; +@use '@angular/cdk'; +@use '@material/typography/typography' as mdc-typography; @use '../core/tokens/m2/mdc/dialog' as tokens-mdc-dialog; @use '../core/tokens/m2/mat/dialog' as tokens-mat-dialog; @use '../core/mdc-helpers/mdc-helpers'; @use '../core/tokens/token-utils'; @use '../core/style/variables'; -@use './mdc-dialog-structure-overrides'; // Dialog content max height. This has been copied from the standard dialog // and is needed to make the dialog content scrollable. @@ -18,92 +16,199 @@ $mat-dialog-button-horizontal-margin: 8px !default; // Whether to emit fallback values for the structural styles. Previously MDC was emitting // paddings in the static styles which meant that users would get them even if they didn't // include the `dialog-base`. Eventually we should clean up the usages of this flag. -$_emit-fallbacks: true; +$_emit-structure-fallbacks: true; -// Note that we disable fallback declarations, but we don't disable fallback -// values, because there are a lot of internal apps that don't include a proper -// theme in their tests. -@include mdc-helpers.disable-mdc-fallback-declarations { - @include mdc-dialog.static-styles($query: mdc-helpers.$mdc-base-styles-query); - @include mdc-dialog-structure-overrides.private-dialog-structure-overrides( - $mat-dialog-content-max-height); - - .mat-mdc-dialog-container { - // Apply the theming slots to the container using an initial set of - // values that will be overridden in the theme styles. - @include mdc-dialog-theme.theme-styles(tokens-mdc-dialog.get-token-slots()); - - // MDC's `theme-styles` generates a variable called `--mdc-dialog-container-elevation-shadow` - // (note the `--shadow`) while it uses `--mdc-dialog-container-elevation` to assign the - // `box-shadow`. Remap it so the token value is picked up. - --mdc-dialog-container-elevation: var(--mdc-dialog-container-elevation-shadow); - - // The dialog container is focusable. We remove the default outline shown in browsers. - outline: 0; - - .mdc-dialog__container { - transition: opacity linear var(--mat-dialog-transition-duration, 0ms); - } - - .mdc-dialog__surface { - transition: mdc-animation.enter(transform, var(--mat-dialog-transition-duration, 0ms)); - } - - // Angular Material supports disabling all animations when NoopAnimationsModule is imported. - // TODO(devversion): Look into using MDC's Sass queries to separate the animation styles and - // conditionally add them. Consider the size cost and churn when deciding whether to switch. - &._mat-animation-noopable { - .mdc-dialog__container, .mdc-dialog__surface { - transition: none; - } +@mixin _use-mat-tokens { + @include mdc-helpers.disable-mdc-fallback-declarations { + @include token-utils.use-tokens(tokens-mat-dialog.$prefix, + tokens-mat-dialog.get-token-slots()) { + @content; } } } -@mixin _use-mat-tokens { +@mixin _use-mdc-tokens { @include mdc-helpers.disable-mdc-fallback-declarations { - @include token-utils.use-tokens(tokens-mat-dialog.$prefix, - tokens-mat-dialog.get-token-slots()) { + @include token-utils.use-tokens(tokens-mdc-dialog.$prefix, + tokens-mdc-dialog.get-token-slots()) { @content; } } } +// Note that we disable fallback declarations, but we don't disable fallback +// values, because there are a lot of internal apps that don't include a proper +// theme in their tests. +.mat-mdc-dialog-container { + width: 100%; + height: 100%; + display: block; + box-sizing: border-box; + max-height: inherit; + min-height: inherit; + min-width: inherit; + max-width: inherit; + + // The dialog container is focusable. We remove the default outline shown in browsers. + outline: 0; +} + // This needs extra specificity so it doesn't get overwritten by the CDK structural styles. .cdk-overlay-pane.mat-mdc-dialog-panel { @include _use-mat-tokens { - @include token-utils.create-token-slot(max-width, container-max-width, $_emit-fallbacks); - @include token-utils.create-token-slot(min-width, container-min-width, $_emit-fallbacks); + @include token-utils.create-token-slot(max-width, container-max-width, + $_emit-structure-fallbacks); + @include token-utils.create-token-slot(min-width, container-min-width, + $_emit-structure-fallbacks); @media (variables.$xsmall) { @include token-utils.create-token-slot(max-width, container-small-max-width, - $_emit-fallbacks); + $_emit-structure-fallbacks); } } } +.mat-mdc-dialog-inner-container { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-around; + box-sizing: border-box; + height: 100%; + opacity: 0; + transition: opacity linear var(--mat-dialog-transition-duration, 0ms); + max-height: inherit; + min-height: inherit; + min-width: inherit; + max-width: inherit; + + .mdc-dialog--closing & { + transition: opacity 75ms linear; + transform: none; + } + + .mdc-dialog--open & { + opacity: 1; + } + + ._mat-animation-noopable & { + transition: none; + } +} + +.mat-mdc-dialog-surface { + display: flex; + flex-direction: column; + flex-grow: 0; + flex-shrink: 0; + box-sizing: border-box; + width: 100%; + height: 100%; + position: relative; + overflow-y: auto; + outline: 0; + transform: scale(0.8); + transition: transform var(--mat-dialog-transition-duration, 0ms) cubic-bezier(0, 0, 0.2, 1); + max-height: inherit; + min-height: inherit; + min-width: inherit; + max-width: inherit; + + [dir='rtl'] & { + text-align: right; + } + + .mdc-dialog--open &, + .mdc-dialog--closing & { + transform: none; + } + + ._mat-animation-noopable & { + transition: none; + } + + @include cdk.high-contrast(active, off) { + outline: 2px solid windowText; + } + + @include _use-mat-tokens { + @include token-utils.create-token-slot(box-shadow, container-elevation-shadow); + } + + @include _use-mdc-tokens { + @include token-utils.create-token-slot(border-radius, container-shape); + @include token-utils.create-token-slot(background-color, container-color); + } +} + .mat-mdc-dialog-title { + @include mdc-typography.text-baseline($top: 40px, $display: block, $lineHeight: null); + position: relative; + flex-shrink: 0; + box-sizing: border-box; + margin: 0 0 1px; + + [dir='rtl'] & { + text-align: right; + } + @include _use-mat-tokens { - @include token-utils.create-token-slot(padding, headline-padding, $_emit-fallbacks); + @include token-utils.create-token-slot(padding, headline-padding, $_emit-structure-fallbacks); + } + + // Nested to maintain the old specificity. + .mat-mdc-dialog-container & { + @include _use-mdc-tokens { + @include token-utils.create-token-slot(color, subhead-color); + @include token-utils.create-token-slot(font-family, subhead-font); + @include token-utils.create-token-slot(line-height, subhead-line-height); + @include token-utils.create-token-slot(font-size, subhead-size); + @include token-utils.create-token-slot(font-weight, subhead-weight); + @include token-utils.create-token-slot(letter-spacing, subhead-tracking); + } } } -// MDC sets the display behavior for title and actions, but not for content. Since we support -// using the `mdc-dialog__content` as custom element, we need to set the element to `block`. .mat-mdc-dialog-content { display: block; + flex-grow: 1; + box-sizing: border-box; + margin: 0; + overflow: auto; + padding: 20px 24px; + max-height: $mat-dialog-content-max-height; + + > :first-child { + margin-top: 0; + } + + > :last-child { + margin-bottom: 0; + } + + // Nested to maintain the old specificity. + .mat-mdc-dialog-container & { + @include _use-mdc-tokens { + @include token-utils.create-token-slot(color, supporting-text-color); + @include token-utils.create-token-slot(font-family, supporting-text-font); + @include token-utils.create-token-slot(line-height, supporting-text-line-height); + @include token-utils.create-token-slot(font-size, supporting-text-size); + @include token-utils.create-token-slot(font-weight, supporting-text-weight); + @include token-utils.create-token-slot(letter-spacing, supporting-text-tracking); + } + } @include _use-mat-tokens { // These styles need a bit more specificity. .mat-mdc-dialog-container & { - @include token-utils.create-token-slot(padding, content-padding, $_emit-fallbacks); + @include token-utils.create-token-slot(padding, content-padding, $_emit-structure-fallbacks); } // Note: we can achieve this with a `:has` selector, but it results in an // increased specificity which breaks a lot of internal clients. .mat-mdc-dialog-container-with-actions & { @include token-utils.create-token-slot(padding, with-actions-content-padding, - $_emit-fallbacks); + $_emit-structure-fallbacks); } } @@ -113,11 +218,28 @@ $_emit-fallbacks: true; } .mat-mdc-dialog-actions { + display: flex; + position: relative; + flex-shrink: 0; + flex-wrap: wrap; + align-items: center; + justify-content: flex-end; + box-sizing: border-box; + min-height: 52px; + margin: 0; + padding: 8px; + border-top: 1px solid transparent; + + @include cdk.high-contrast(active, off) { + border-top-color: CanvasText; + } + // For backwards compatibility, actions align at start by default. MDC usually // aligns actions at the end of the container. @include _use-mat-tokens { - @include token-utils.create-token-slot(padding, actions-padding, $_emit-fallbacks); - @include token-utils.create-token-slot(justify-content, actions-alignment, $_emit-fallbacks); + @include token-utils.create-token-slot(padding, actions-padding, $_emit-structure-fallbacks); + @include token-utils.create-token-slot(justify-content, actions-alignment, + $_emit-structure-fallbacks); } // .mat-mdc-dialog-actions-align-{start|center|end} are set by directive input "align" @@ -148,3 +270,16 @@ $_emit-fallbacks: true; } } } + +// When a component is passed into the dialog, the host element interrupts +// the `display:flex` from affecting the dialog title, content, and +// actions. To fix this, we make the component host `display: contents` by +// marking its host with the `mat-mdc-dialog-component-host` class. +// +// Note that this problem does not exist when a template ref is used since +// the title, contents, and actions are then nested directly under the +// dialog surface. +.mat-mdc-dialog-component-host { + display: contents; +} +