From 1a85dd299b4ac38d201c3cc38c396b2f08398adc Mon Sep 17 00:00:00 2001 From: Miles Malerba Date: Tue, 8 Aug 2023 22:18:02 +0000 Subject: [PATCH] feat(material/theming): Add APIs to get color info from theme --- .../theming/_definition.scss | 8 ++ src/material/_index.scss | 3 +- src/material/core/theming/_inspection.scss | 80 ++++++++++++++- .../tests/theming-inspection-api.spec.ts | 99 ++++++++++++++++--- 4 files changed, 174 insertions(+), 16 deletions(-) diff --git a/src/material-experimental/theming/_definition.scss b/src/material-experimental/theming/_definition.scss index 2ce0ff16e245..5c2036bb76b5 100644 --- a/src/material-experimental/theming/_definition.scss +++ b/src/material-experimental/theming/_definition.scss @@ -46,6 +46,14 @@ $theme-version: 1; $internals: ( theme-version: $theme-version, theme-type: $type, + palettes: ( + primary: map.remove($primary, neutral-variant), + secondary: map.remove($secondary, neutral-variant), + tertiary: map.remove($tertiary, neutral-variant), + neutral: m3-palettes.$neutral-palette, + neutral-variant: map.get($primary, neutral-variant), + error: m3-palettes.$red-palette + ), color-tokens: m3-tokens.generate-color-tokens($type, $primary, $secondary, $tertiary, m3-palettes.$neutral-palette, m3-palettes.$red-palette) ) diff --git a/src/material/_index.scss b/src/material/_index.scss index 0bfc68809902..c2e2076abb86 100644 --- a/src/material/_index.scss +++ b/src/material/_index.scss @@ -144,4 +144,5 @@ list-density, list-base; $private-mdc-theme-styles-query, $private-mdc-typography-styles-query; // New theming APIs, currently in development: -@forward './core/theming/inspection' as private-* show private-get-theme-version; +@forward './core/theming/inspection' as private-* show private-get-theme-version, + private-get-theme-type, private-get-theme-color; diff --git a/src/material/core/theming/_inspection.scss b/src/material/core/theming/_inspection.scss index 476fdd72da65..de6a3446adde 100644 --- a/src/material/core/theming/_inspection.scss +++ b/src/material/core/theming/_inspection.scss @@ -24,6 +24,81 @@ $_internals: _mat-theming-internals-do-not-access; @return if($err, 0, map.get($theme, $_internals, theme-version) or 0); } +/// Gets the type of theme represented by a theme object (light or dark). +/// @param {Map} $theme The theme +/// @return {String} The type of theme (either `light` or `dark`). +@function get-theme-type($theme) { + $err: _validate-theme-object($theme); + @if $err { + // TODO(mmalerba): implement for old style theme objects. + @error #{'get-theme-type does not support legacy theme objects.'}; + } + @return map.get($theme, $_internals, theme-type) or light; +} + +/// Gets a color from a theme object. This function can take 2 or 3 arguments. If 2 arguments are +/// passed, the second argument is treated as the name of a color role. If 3 arguments are passed, +/// the second argument is treated as the name of a color palette (primary, secondary, etc.) and the +/// third is treated as the palette hue (10, 50, etc.) +/// @param {Map} $theme The theme +/// @param {String} $color-role-or-palette-name The name of the color role to get, or the name of a +/// color palette. +/// @param {Number} $hue The palette hue to get (passing this argument means the second argument is +/// interpreted as a palette name). +/// @return {Color} The requested theme color. +@function get-theme-color($theme, $args...) { + $args-count: list.length($args); + @if $args-count == 1 { + @return _get-theme-role-color($theme, $args...); + } @else if $args-count == 2 { + @return _get-theme-palette-color($theme, $args...); + } + @error #{'Expected 2 or 3 arguments. Got:'} $args-count + 1; +} + +/// Gets a role color from a theme object. +/// @param {Map} $theme The theme +/// @param {String} $color-role-name The name of the color role to get. +/// @return {Color} The requested role color. +@function _get-theme-role-color($theme, $color-role-name) { + $err: _validate-theme-object($theme); + @if $err { + // TODO(mmalerba): implement for old style theme objects. + @error #{'get-theme-color does not support legacy theme objects.'}; + } + $color-roles: map.get($theme, $_internals, color-tokens, (mdc, theme)); + $result: map.get($color-roles, $color-role-name); + @if not $result { + @error #{'Valid color roles are: #{map.keys($color-roles)}. Got:'} $color-role-name; + } + @return $result; +} + +/// Gets a palette color from a theme object. +/// @param {Map} $theme The theme +/// @param {String} $palette-name The name of the palette to get the color from. +/// @param {Number} $hue The hue to read from the palette. +/// @return {Color} The requested palette color. +@function _get-theme-palette-color($theme, $palette-name, $hue) { + $err: _validate-theme-object($theme); + @if $err { + // TODO(mmalerba): implement for old style theme objects. + @error #{'get-theme-color does not support legacy theme objects.'}; + } + $palettes: map.get($theme, $_internals, palettes); + $palette: map.get($palettes, $palette-name); + @if not $palette { + $supported-palettes: map.keys($palettes); + @error #{'Valid palettes are: #{$supported-palettes}. Got:'} $palette-name; + } + $result: map.get($palette, $hue); + @if not $result { + $supported-hues: map.keys($palette); + @error #{'Valid hues for'} $palette-name #{'are: #{$supported-hues}. Got:'} $hue; + } + @return $result; +} + /// Gets the set of tokens from the given theme, limited to those affected by the requested theming /// systems. /// @param {Map} $theme The theme to get tokens from. @@ -38,9 +113,8 @@ $_internals: _mat-theming-internals-do-not-access; } $err: validation.validate-allowed-values($systems, color, typography, density, base); @if $err { - @error - #{'Expected $systems to contain valid system names (color, typographt, density, or base).'} - #{'Got invalid system names:'} $err; + @error #{'Expected $systems to contain valid system names (color, typography, density, or'} + #{'base). Got invalid system names:'} $err; } $result: (); @each $system in $systems { diff --git a/src/material/core/theming/tests/theming-inspection-api.spec.ts b/src/material/core/theming/tests/theming-inspection-api.spec.ts index c4ca492dad8c..188199eaf069 100644 --- a/src/material/core/theming/tests/theming-inspection-api.spec.ts +++ b/src/material/core/theming/tests/theming-inspection-api.spec.ts @@ -124,13 +124,12 @@ describe('theming inspection api', () => { it('should get theme version', () => { expect( transpile(` - $theme: ${defineM2Theme()}; - - div { - content: mat.private-get-theme-version($theme); - } - `), - ).toMatch('content: 0;'); + $theme: ${defineM2Theme()}; + div { + content: mat.private-get-theme-version($theme); + } + `), + ).toMatch(/content: 0;/); }); }); @@ -139,12 +138,88 @@ describe('theming inspection api', () => { expect( transpile(` $theme: ${defineM3Theme()}; + div { + content: mat.private-get-theme-version($theme); + } + `), + ).toMatch(/content: 1;/); + }); + + it('should get theme type', () => { + expect( + transpile(` + $theme: ${defineM3Theme()}; + div { + content: mat.private-get-theme-type($theme); + } + `), + ).toMatch(/content: light;/); + }); + + it('should get role color', () => { + expect( + transpile(` + $theme: ${defineM3Theme()}; + div { + content: mat.private-get-theme-color($theme, primary-container); + } + `), + ).toMatch(/content: #e0e0ff;/); + }); + + it('should error on invalid color role', () => { + expect(() => + transpile(` + $theme: ${defineM3Theme()}; + div { + content: mat.private-get-theme-color($theme, fake-role); + } + `), + ).toThrowError(/Valid color roles are.*Got: fake-role/); + }); + + it('should get palette color', () => { + expect( + transpile(` + $theme: ${defineM3Theme()}; + div { + content: mat.private-get-theme-color($theme, tertiary, 20); + } + `), + ).toMatch(/content: #323200;/); + }); + + it('should error on invalid color palette', () => { + expect(() => + transpile(` + $theme: ${defineM3Theme()}; + div { + content: mat.private-get-theme-color($theme, fake-palette, 20); + } + `), + ).toThrowError(/Valid palettes are.*Got: fake-palette/); + }); + + it('should error on invalid color hue', () => { + expect(() => + transpile(` + $theme: ${defineM3Theme()}; + div { + content: mat.private-get-theme-color($theme, neutral, 11); + } + `), + ).toThrowError(/Valid hues for neutral are.*Got: 11/); + }); - div { - content: mat.private-get-theme-version($theme); - } - `), - ).toMatch('content: 1;'); + it('should error on wrong number of get-color-theme args', () => { + expect(() => + transpile(` + $theme: ${defineM3Theme()}; + div { + content: mat.private-get-theme-color($theme); + } + `), + ).toThrowError(/Expected 2 or 3 arguments. Got: 1/); }); }); });