353 lines
11 KiB
SCSS
353 lines
11 KiB
SCSS
|
//
|
||
|
// Copyright 2017 Google Inc.
|
||
|
//
|
||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
// of this software and associated documentation files (the "Software"), to deal
|
||
|
// in the Software without restriction, including without limitation the rights
|
||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
// copies of the Software, and to permit persons to whom the Software is
|
||
|
// furnished to do so, subject to the following conditions:
|
||
|
//
|
||
|
// The above copyright notice and this permission notice shall be included in
|
||
|
// all copies or substantial portions of the Software.
|
||
|
//
|
||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||
|
// THE SOFTWARE.
|
||
|
//
|
||
|
|
||
|
@use 'sass:list';
|
||
|
@use 'sass:map';
|
||
|
@use 'sass:meta';
|
||
|
@use '@material/feature-targeting/feature-targeting';
|
||
|
@use './css';
|
||
|
@use './custom-properties';
|
||
|
@use './gss';
|
||
|
@use './keys';
|
||
|
@use './replace';
|
||
|
@use './theme-color';
|
||
|
|
||
|
@mixin core-styles($query: feature-targeting.all()) {
|
||
|
$feat-color: feature-targeting.create-target($query, color);
|
||
|
|
||
|
:root {
|
||
|
@include feature-targeting.targets($feat-color) {
|
||
|
@each $style in theme-color.get-theme-keys() {
|
||
|
@include custom-properties.declaration(
|
||
|
keys.create-custom-property($style)
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@each $style in theme-color.get-theme-keys() {
|
||
|
@if $style != 'background' and $style != 'surface' {
|
||
|
.mdc-theme--#{$style} {
|
||
|
@include feature-targeting.targets($feat-color) {
|
||
|
@include property(color, $style, $important: true);
|
||
|
}
|
||
|
}
|
||
|
} @else {
|
||
|
.mdc-theme--#{$style} {
|
||
|
@include feature-targeting.targets($feat-color) {
|
||
|
@include property(background-color, $style);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// CSS rules for using primary and secondary (plus light/dark variants) as background colors.
|
||
|
@each $style in ('primary', 'secondary') {
|
||
|
.mdc-theme--#{$style}-bg {
|
||
|
@include feature-targeting.targets($feat-color) {
|
||
|
@include property(background-color, $style, $important: true);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Applies a dynamic value to the specified property. This mixin should be used
|
||
|
/// in theme style mixins when setting properties.
|
||
|
///
|
||
|
/// The value may be any of the following:
|
||
|
/// - a standard CSS value
|
||
|
/// - a custom property Map, e.g. (varname: --mdc-foo, fallback: blue)
|
||
|
/// - a Material theme key String, e.g. 'primary', 'on-primary'
|
||
|
///
|
||
|
/// @example
|
||
|
/// @include theme.property(color, teal);
|
||
|
/// @include theme.property(color, custom-properties.create(foo, blue));
|
||
|
/// @include theme.property(color, primary);
|
||
|
///
|
||
|
/// A `$replace` Map parameter may be provided to replace key/value pairs for
|
||
|
/// string values. This can be used to substitute parameters in complex string
|
||
|
/// values such as `calc()` with custom properties.
|
||
|
///
|
||
|
/// @example
|
||
|
/// @include theme.property(
|
||
|
/// width,
|
||
|
/// calc(foo + bar),
|
||
|
/// $replace: (foo: custom-properties.create(foo), bar: 8px)
|
||
|
/// );
|
||
|
///
|
||
|
/// Note: Material theme key Strings (e.g. `primary`) are not supported as
|
||
|
/// replacement values.
|
||
|
///
|
||
|
/// A CSS custom property declaration may be emitted by providing a custom
|
||
|
/// property Map to `$property`. The fallback value (or `$value` if provided)
|
||
|
/// will be used as the declaration value.
|
||
|
///
|
||
|
/// @example - scss
|
||
|
/// .foo {
|
||
|
/// @include theme.property(custom-properties.create(foo, teal));
|
||
|
/// @include theme.property(custom-properties.create(bar, teal), blue);
|
||
|
/// }
|
||
|
///
|
||
|
/// @example - css
|
||
|
/// .foo {
|
||
|
/// --mdc-foo: teal;
|
||
|
/// --mdc-bar: blue;
|
||
|
/// }
|
||
|
///
|
||
|
/// @param {String | Map} $property - The name of the CSS property. May also be
|
||
|
/// a custom property Map to emit a custom propery declaration.
|
||
|
/// @param {String | Number | Color | List | Map} $value - The property's value.
|
||
|
/// This parameter may be omitted if `$property` is a custom property Map.
|
||
|
/// @param {Map} $gss - Optional Map of GSS annotations to set.
|
||
|
/// @param {Map} $replace - An optional Map of replacement key/value pairs if
|
||
|
/// the `$value` is a string.
|
||
|
/// @param {Bool} $important - Set to true to add an `!important` rule. Defaults
|
||
|
/// to false.
|
||
|
@mixin property(
|
||
|
$property,
|
||
|
$value: null,
|
||
|
$gss: (),
|
||
|
$replace: null,
|
||
|
$important: false
|
||
|
) {
|
||
|
@if custom-properties.is-custom-prop($property) {
|
||
|
// $property is a custom property Map
|
||
|
// --mdc-foo: value;
|
||
|
@if $value {
|
||
|
$property: custom-properties.set-fallback(
|
||
|
$property,
|
||
|
$value,
|
||
|
$shallow: true
|
||
|
);
|
||
|
}
|
||
|
|
||
|
@include custom-properties.declaration(
|
||
|
$property,
|
||
|
$gss: $gss,
|
||
|
$important: $important
|
||
|
);
|
||
|
} @else if custom-properties.is-custom-prop($value) {
|
||
|
// $value is a custom property Map
|
||
|
// property: var(--mdc-foo, fallback);
|
||
|
@include custom-properties.declaration(
|
||
|
$property,
|
||
|
$value,
|
||
|
$gss: $gss,
|
||
|
$important: $important
|
||
|
);
|
||
|
} @else if keys.is-key($value) {
|
||
|
// $value is a key String
|
||
|
// property: key;
|
||
|
$custom-prop: keys.create-custom-property($value);
|
||
|
|
||
|
@if theme-color.is-theme-key($value) {
|
||
|
// Determine if we need to use a compile-time updated value to support
|
||
|
// Angular.
|
||
|
$key: $value;
|
||
|
// (changed: Bool, value: *)
|
||
|
$result: theme-color.deprecated-get-global-theme-key-value-if-changed(
|
||
|
$key
|
||
|
);
|
||
|
|
||
|
@if map.get($result, changed) {
|
||
|
// $mdc-theme-property-values was changed at compile time. Use the
|
||
|
// global value instead. Otherwise if it was not changed, continue
|
||
|
// using the key store normally.
|
||
|
$custom-prop: keys.create-custom-property($key);
|
||
|
$custom-prop: custom-properties.set-fallback(
|
||
|
$custom-prop,
|
||
|
map.get($result, value)
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@include custom-properties.declaration(
|
||
|
$property,
|
||
|
$custom-prop,
|
||
|
$gss: $gss,
|
||
|
$important: $important
|
||
|
);
|
||
|
} @else {
|
||
|
// $value is a standard CSS value
|
||
|
// property: value;
|
||
|
$fallback: null;
|
||
|
@if $replace {
|
||
|
// If any replacements are null, treat the entire value as null (do not
|
||
|
// emit anything).
|
||
|
@each $name, $replacement in $replace {
|
||
|
@if $replacement == null {
|
||
|
$value: null;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@if $replace and $value {
|
||
|
@if meta.type-of($replace) != 'map' {
|
||
|
@error 'mdc-theme: Invalid replacement #{$replace}. Must be a Map.';
|
||
|
}
|
||
|
|
||
|
$replace-map-fallback: ();
|
||
|
$replace-map-value: ();
|
||
|
$needs-fallback: false;
|
||
|
@each $name, $replacement in $replace {
|
||
|
@if custom-properties.is-custom-prop($replacement) {
|
||
|
$replace-value: custom-properties.get-declaration-value($replacement);
|
||
|
$replace-fallback: custom-properties.get-declaration-fallback(
|
||
|
$replacement
|
||
|
);
|
||
|
@if $replace-fallback {
|
||
|
$needs-fallback: true;
|
||
|
}
|
||
|
|
||
|
$replace-map-value: map.set(
|
||
|
$replace-map-value,
|
||
|
$name,
|
||
|
$replace-value
|
||
|
);
|
||
|
$replace-map-fallback: map.set(
|
||
|
$replace-map-fallback,
|
||
|
$name,
|
||
|
$replace-fallback
|
||
|
);
|
||
|
} @else {
|
||
|
$replace-map-value: map.set($replace-map-value, $name, $replacement);
|
||
|
$replace-map-fallback: map.set(
|
||
|
$replace-map-fallback,
|
||
|
$name,
|
||
|
$replacement
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@if meta.type-of($value) == 'string' {
|
||
|
@if $needs-fallback {
|
||
|
$fallback: replace.replace-string($value, $replace-map-fallback);
|
||
|
}
|
||
|
$value: replace.replace-string($value, $replace-map-value);
|
||
|
} @else if meta.type-of($value) == 'list' {
|
||
|
@if $needs-fallback {
|
||
|
$fallback: replace.replace-list($value, $replace-map-fallback);
|
||
|
}
|
||
|
$value: replace.replace-list($value, $replace-map-value);
|
||
|
} @else {
|
||
|
@error 'mdc-theme: Invalid replacement value #{$value}. $replace may only be used with string or list values.';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@include css.declaration(
|
||
|
$property,
|
||
|
$value,
|
||
|
$fallback-value: $fallback,
|
||
|
$gss: $gss,
|
||
|
$important: $important
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// @deprecated use the `property()` mixin instead
|
||
|
@mixin prop($property, $style, $important: false) {
|
||
|
@include property($property, $style, $important: $important);
|
||
|
}
|
||
|
|
||
|
/// Validates theme configuration keys by comparing it with original theme
|
||
|
/// configuration, also validates theme values to see if it has any unsupported
|
||
|
/// value formats.
|
||
|
/// @param {Map} $origin-theme - Original theme configuration in Sass map format
|
||
|
/// that has all supported keys.
|
||
|
/// @param {Map} $custom-theme - Provided theme configuration in Sass map format
|
||
|
/// that should be validated against `$origin-theme`.
|
||
|
/// @examples
|
||
|
/// @mixin theme($theme) {
|
||
|
/// @include theme.validate-theme($light-theme, $theme);
|
||
|
///
|
||
|
/// // ...
|
||
|
/// }
|
||
|
@mixin validate-theme($origin-theme, $custom-theme, $test-only: false) {
|
||
|
@include validate-theme-keys(
|
||
|
$origin-theme,
|
||
|
$custom-theme,
|
||
|
$test-only: $test-only
|
||
|
);
|
||
|
@include _validate-theme-values($custom-theme, $test-only: $test-only);
|
||
|
}
|
||
|
|
||
|
/// Validates theme configuration keys by comparing it with original theme
|
||
|
/// configuration.
|
||
|
/// @see Use `validate-theme()` to validate both theme keys and theme values.
|
||
|
/// @param {Map} $origin-theme - Original theme configuration in Sass map format
|
||
|
/// that has all supported keys.
|
||
|
/// @param {Map} $custom-theme - Provided theme configuration in Sass map format
|
||
|
/// that should be validated against `$origin-theme`.
|
||
|
@mixin validate-theme-keys($origin-theme, $custom-theme, $test-only: false) {
|
||
|
$origin-keys: map.keys($origin-theme);
|
||
|
$unsupported-keys: ();
|
||
|
|
||
|
@each $key, $value in $custom-theme {
|
||
|
@if (not list.index($origin-keys, $key)) {
|
||
|
$unsupported-keys: list.append(
|
||
|
$unsupported-keys,
|
||
|
$key,
|
||
|
$separator: comma
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@if list.length($unsupported-keys) > 0 {
|
||
|
$error-message: 'Unsupported keys found: #{$unsupported-keys}. Expected one of: #{$origin-keys}.';
|
||
|
|
||
|
@if $test-only {
|
||
|
content: $error-message;
|
||
|
} @else {
|
||
|
@error $error-message;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Validates theme configuration values to see if it has any unsupported value
|
||
|
/// formats.
|
||
|
/// @see Use `validate-theme()` to validate both theme keys and theme values.
|
||
|
/// @param {Map} $custom-theme - Provided theme configuration in Sass map format
|
||
|
/// that needs to be validated.
|
||
|
@mixin _validate-theme-values($custom-theme, $test-only: false) {
|
||
|
$unsupported-custom-prop-keys: ();
|
||
|
|
||
|
@each $key, $value in $custom-theme {
|
||
|
@if custom-properties.is-custom-prop($value) {
|
||
|
$unsupported-custom-prop-keys: list.append(
|
||
|
$unsupported-custom-prop-keys,
|
||
|
$key,
|
||
|
$separator: comma
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@if list.length($unsupported-custom-prop-keys) > 0 {
|
||
|
$error-message: 'Custom properties are not supported for theme map keys: #{$unsupported-custom-prop-keys}';
|
||
|
|
||
|
@if $test-only {
|
||
|
content: $error-message;
|
||
|
} @else {
|
||
|
@error $error-message;
|
||
|
}
|
||
|
}
|
||
|
}
|