Skip to content
122 changes: 105 additions & 17 deletions src/material-experimental/theming/_config-validation.scss
Original file line number Diff line number Diff line change
@@ -1,30 +1,82 @@
@use 'sass:list';
@use 'sass:map';
@use 'sass:meta';
@use 'sass:string';
@use '@angular/material' as mat;
@use './m3-palettes';

/// Creates an error message by finding `$config` in the existing message and appending a suffix to
/// it.
/// @param {List|String} $err The error message.
/// @param {String} $suffix The suffix to add.
/// @return {List|String} The updated error message.
@function _create-dollar-config-error-message($err, $suffix) {
@if meta.type-of($err) == 'list' {
@for $i from 1 through list.length($err) {
$err: list.set-nth($err, $i,
_create-dollar-config-error-message(list.nth($err, $i), $suffix));
}
}
@else if meta.type-of($err) == 'string' {
$start: string.index($err, '$config');
@if $start {
$err: string.insert($err, $suffix, $start + 7);
}
}
@return $err;
}

/// Validates that the given object is an M3 palette.
/// @param {*} $palette The object to test
/// @return {Boolean|null} null if it is a valid M3 palette, else true.
@function _validate-palette($palette) {
@if not meta.type-of($palette) == 'map' {
@return true;
}
$keys: map.keys($palette);
$expected-keys: map.keys(m3-palettes.$red-palette);
@if mat.private-validate-allowed-values($keys, $expected-keys...) or
mat.private-validate-required-values($keys, $expected-keys...) {
@return true;
}
$nv-keys: map.keys(map.get($palette, neutral-variant));
$expected-nv-keys: map.keys(map.get(m3-palettes.$red-palette, neutral-variant));
@if mat.private-validate-allowed-values($nv-keys, $expected-nv-keys...) or
mat.private-validate-required-values($nv-keys, $expected-nv-keys...) {
@return true;
}
@return null;
}

/// Validates a theme config.
/// @param {Map} $config The config to test.
/// @return {List} null if no error, else the error message
@function validate-theme-config($config) {
$err: mat.private-validate-type($config, 'map', 'null');
@if $err {
@return (#{'$config'} #{'should be a color configuration object. Got:'} $config);
@return (#{'$config should be a configuration object. Got:'} $config);
}
$err: mat.private-validate-allowed-values(map.keys($config or ()), color, typography, density);
$allowed: (color, typography, density);
$err: mat.private-validate-allowed-values(map.keys($config or ()), $allowed...);
@if $err {
@return (#{'$config'} #{'has unexpected properties:'} $err);
@return (
#{'$config has unexpected properties. Valid properties are'}
#{'#{$allowed}.'}
#{'Found:'}
$err
);
}
$err: validate-color-config(map.get($config, color));
@if $err {
@return list.set-nth($err, 1, #{'#{list.nth($err, 1)}.color'});
@return _create-dollar-config-error-message($err, '.color');
}
$err: validate-typography-config(map.get($config, typography));
@if $err {
@return list.set-nth($err, 1, #{'#{list.nth($err, 1)}.typography'});
@return _create-dollar-config-error-message($err, '.typography');
}
$err: validate-density-config(map.get($config, density));
@if $err {
@return list.set-nth($err, 1, #{'#{list.nth($err, 1)}.density'});
@return _create-dollar-config-error-message($err, '.density');
}
@return null;
}
Expand All @@ -35,12 +87,35 @@
@function validate-color-config($config) {
$err: mat.private-validate-type($config, 'map', 'null');
@if $err {
@return (#{'$config'} #{'should be a color configuration object. Got:'} $config);
@return (#{'$config should be a color configuration object. Got:'} $config);
}
$err: mat.private-validate-allowed-values(
map.keys($config or ()), theme-type, primary, secondary, tertiary);
$allowed: (theme-type, primary, secondary, tertiary);
$err: mat.private-validate-allowed-values(map.keys($config or ()), $allowed...);
@if $err {
@return (#{'$config'} #{'has unexpected properties:'} $err);
@return (
#{'$config has unexpected properties. Valid properties are'}
#{'#{$allowed}.'}
#{'Found:'}
$err
);
}
@if $config and map.has-key($config, theme-type) and
not list.index((light, dark), map.get($config, theme-type)) {
@return (
#{'Expected $config.theme-type to be one of: light, dark. Got:'}
map.get($config, theme-type)
);
}
@each $palette in (primary, secondary, tertiary) {
@if $config and map.has-key($config, $palette) {
$err: _validate-palette(map.get($config, $palette));
@if $err {
@return (
#{'Expected $config.#{$palette} to be a valid M3 palette. Got:'}
map.get($config, $palette)
);
}
}
}
@return null;
}
Expand All @@ -51,13 +126,17 @@
@function validate-typography-config($config) {
$err: mat.private-validate-type($config, 'map', 'null');
@if $err {
@return (#{'$config'} #{'should be a typography configuration object. Got:'} $config);
@return (#{'$config should be a typography configuration object. Got:'} $config);
}
$err: mat.private-validate-allowed-values(
map.keys($config or ()), brand-family, plain-family, bold-weight, medium-weight,
regular-weight);
$allowed: (brand-family, plain-family, bold-weight, medium-weight, regular-weight);
$err: mat.private-validate-allowed-values(map.keys($config or ()), $allowed...);
@if $err {
@return (#{'$config'} #{'has unexpected properties:'} $err);
@return (
#{'$config has unexpected properties. Valid properties are'}
#{'#{$allowed}.'}
#{'Found:'}
$err
);
}
@return null;
}
Expand All @@ -68,11 +147,20 @@
@function validate-density-config($config) {
$err: mat.private-validate-type($config, 'map', 'null');
@if $err {
@return (#{'$config'} #{'should be a density configuration object. Got:'} $config);
@return (#{'$config should be a density configuration object. Got:'} $config);
}
$err: mat.private-validate-allowed-values(map.keys($config or ()), scale);
@if $err {
@return (#{'$config'} #{'has unexpected properties:'} $err);
@return (#{'$config has unexpected properties. Valid properties are: scale. Found:'} $err);
}
@if $config and map.has-key($config, scale) {
$allowed-scales: (0, -1, -2, -3, -4 -5, minimum, maximum);
@if mat.private-validate-allowed-values(map.get($config, scale), $allowed-scales...) {
@return (
#{'Expected $config.scale to be one of: #{$allowed-scales}. Got:'}
map.get($config, scale)
);
}
}
@return null;
}
10 changes: 9 additions & 1 deletion src/material-experimental/theming/_definition.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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)
)
Expand All @@ -61,7 +69,7 @@ $theme-version: 1;
@error $err;
}

$plain: map.get($config, plain-family) or Roboto, sans-serif;
$plain: map.get($config, plain-family) or (Roboto, sans-serif);
$brand: map.get($config, brand-family) or $plain;
$bold: map.get($config, bold-weight) or 700;
$medium: map.get($config, medium-weight) or 500;
Expand Down
1 change: 1 addition & 0 deletions src/material-experimental/theming/_m3-palettes.scss
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ $yellow-palette: (
90: #e6e3d1,
95: #f4f1df,
99: #fffbff,
100: #fff,
),
);

Expand Down
5 changes: 5 additions & 0 deletions src/material/_index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,8 @@
private-typography-config-level-from-mdc, private-if-touch-targets-unsupported,
$private-mdc-base-styles-query, $private-mdc-base-styles-without-animation-query,
$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,
private-get-theme-type, private-get-theme-color, private-get-theme-typography,
private-get-theme-density, private-theme-has;
14 changes: 14 additions & 0 deletions src/material/core/style/_validation.scss
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,17 @@
}
@return if(list.length($invalid) > 0, $invalid, null);
}

/// Validates that a list contains all values from the required list of values.
/// @param {List} $list The list to test
/// @param {List} $required The required values
/// @return {List} null if no error, else the list of missing values.
@function validate-required-values($list, $required...) {
$invalid: ();
@each $element in $required {
@if not list.index($list, $element) {
$invalid: list.append($invalid, $element);
}
}
@return if(list.length($invalid) > 0, $invalid, null);
}
Loading