| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316 |
- //
- // Copyright 2020 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 'sass:string';
- @use './css';
- @use './gss';
- /// Configuration for custom properties.
- /// @see {mixin} configure
- $_config: (
- emit-custom-properties: true,
- emit-fallback-values: true,
- emit-fallback-vars: true,
- varname-prefix: 'mdc',
- );
- /// Configure options for custom properties. The configuration will be applied
- /// within the scope of the mixin's content and reset when the mixin scope ends.
- ///
- /// @example - scss
- /// @include configure($emit-fallback-values: false) {
- /// // No fallback values will be emitted within this mixin scope
- /// @include another-mixin();
- /// }
- ///
- /// All parameters must be provided as argument lists.
- ///
- /// @link https://sass-lang.com/documentation/values/lists#argument-lists
- ///
- /// @param {Bool} $emit-custom-properties [true] - Enable or disable all
- /// custom property emission.
- /// @param {Bool} $emit-fallback-values [true] - Enable or disable emission of
- /// fallback CSS static values. This does not include dynamic `var()`
- /// fallback values.
- /// @param {Bool} $emit-fallback-vars [true] - Enable or disable emission of
- /// fallback `var()` values.
- /// @param {String} $varname-prefix ['mdc'] - Global prefix for all custom properties.
- @mixin configure($config...) {
- @if not meta.content-exists() {
- @error 'content is required for configure()';
- }
- $config: meta.keywords($config);
- @each $key, $value in $config {
- @if $value == null {
- $config: map.remove($config, $key);
- }
- }
- @if list.length($config) == 0 {
- @content;
- } @else {
- $previous: $_config;
- // Use !global to avoid shadowing
- // https://sass-lang.com/documentation/variables#shadowing
- $_config: map.merge($_config, $config) !global;
- @content;
- $_config: $previous !global;
- }
- }
- /// Returns true if the parameter is a custom property Map.
- ///
- /// @param {*} $value - the value to test.
- /// @return true if the value is a custom property Map, or false if not.
- @function is-custom-prop($value) {
- @return meta.type-of($value) == 'map' and map.has-key($value, 'varname');
- }
- /// Indicates whether or not a value is a custom property var() string.
- ///
- /// @example - scss
- /// $is-prop-string: is-custom-prop-string('var(--foo)'); // true
- ///
- /// @param {*} $value - The value to test.
- /// @return {Bool} True if the value is a custom property var() string, or
- /// false if not.
- @function is-custom-prop-string($value) {
- @return meta.type-of($value) == 'string' and string.slice($value, 1, 4) ==
- 'var(';
- }
- /// Returns true if $prop1's varname and fallback values are deeply equal to
- /// $prop2's varname and fallback values.
- ///
- /// @param {Map} $prop1 - the first value to test.
- /// @param {Map} $prop2 - the second value to test.
- /// @return true if both properties are deeply equal
- @function are-equal($prop1, $prop2) {
- @return create-var($prop1) == create-var($prop2);
- }
- /// Creates a custom property Map.
- ///
- /// @param {String} $varname - the custom property name.
- /// @param {String | Number | Map} - the fallback value (may be another custom
- /// property Map). May be null.
- /// @return a custom property Map.
- @function create($varname, $fallback: null) {
- @if string.slice($varname, 1, 2) != '--' {
- $varname: create-varname($varname);
- }
- @return (varname: $varname, fallback: $fallback);
- }
- /// Create a custom property variable name with the global prefix.
- ///
- /// @example - scss
- /// $varname: create-varname(foo); // --mdc-foo
- ///
- /// @param {String} $name - The name of the custom property.
- /// @return {String} The full CSS custom property variable name.
- @function create-varname($name) {
- $varname-prefix: map.get($_config, 'varname-prefix');
- @return --#{$varname-prefix}-#{$name};
- }
- /// Returns the custom property variable name of a custom property Map.
- ///
- /// @param {Map} $custom-prop - a custom property Map.
- /// @return the custom property variable name defined by the Map.
- @function get-varname($custom-prop) {
- @return map.get($custom-prop, 'varname');
- }
- /// Returns the fallback value of a custom property Map. May be null if the
- /// custom property does not have a fallback.
- ///
- /// @param {Map} $custom-prop - a custom property Map.
- /// @param {Bool} $shallow - if true, return the first fallback value, which
- /// may be another custom property Map. Defaults to false, which will return
- /// the deep final fallback value.
- /// @return the fallback value of a custom property Map. May be null.
- @function get-fallback($custom-prop, $shallow: false) {
- $fallback: map.get($custom-prop, 'fallback');
- @if is-custom-prop($fallback) and not $shallow {
- @return get-fallback($fallback);
- }
- @return $fallback;
- }
- /// Creates a new custom property Map and returns it with the specified new
- /// fallback value.
- ///
- /// @param {Map} $custom-prop - the custom property Map to copy.
- /// @param {String | Number | Map} $new-fallback - the new fallback value of the
- /// custom property Map. May be null.
- /// @param {Bool} $shallow - if true, set the first fallback value. Defaults to
- /// false, which will set the deep final fallback value.
- /// @return a new custom property Map with the new fallback value.
- @function set-fallback($custom-prop, $new-fallback, $shallow: false) {
- $varname: get-varname($custom-prop);
- $first-fallback: get-fallback($custom-prop, $shallow: true);
- @if is-custom-prop($first-fallback) and not $shallow {
- // The first fallback is a custom property and $shallow is false. Deeply
- // set the fallback value of the custom property and get the new custom
- // property Map returned.
- $new-fallback: set-fallback($first-fallback, $new-fallback);
- }
- @return create($varname, $new-fallback);
- }
- /// Creates and returns a CSS `var()` function value represented by the provided
- /// custom property Map.
- ///
- /// If custom properties are disabled, this function will return the custom
- /// property's fallback value instead, which may be `null` and result in an
- /// empty declaration.
- ///
- /// @param {Map} $custom-prop - A custom property Map.
- /// @return {*} A CSS value, typically a `var()` function, representing the
- /// custom property. The returned value may change depending on the current
- /// configuration options for custom properties and whether or not a
- /// fallback value is present in the custom property Map.
- @function create-var($custom-prop) {
- @if not map.get($_config, emit-custom-properties) {
- // If configured not to emit custom properties and a request is made for a
- // custom prop's CSS value, return its fallback value. If this is null, it
- // will result in an empty declaration.
- @return get-fallback($custom-prop);
- }
- $varname: get-varname($custom-prop);
- $fallback: get-fallback($custom-prop, $shallow: true);
- $emit-fallback-vars: map.get($_config, emit-fallback-vars);
- $fallback-is-prop: is-custom-prop($fallback);
- @if $fallback-is-prop and $emit-fallback-vars {
- @return var($varname, create-var($fallback));
- }
- $emit-fallback-values: map.get($_config, emit-fallback-values);
- @if $fallback and not $fallback-is-prop and $emit-fallback-values {
- @return var($varname, $fallback);
- }
- @return var($varname);
- }
- /// Retrieves the CSS declaration value for a custom property Map. This is
- /// typically a `var()` function.
- ///
- /// If custom properties are disabled, the custom property's fallback value
- /// will be returned instead.
- ///
- /// @param {Map} $custom-prop - The custom property Map to retrieve a
- /// declaration value for.
- /// @return {*} The CSS declaration value.
- @function get-declaration-value($custom-prop) {
- // This will return just the fallback value if `emit-custom-properties` is false.
- @return create-var($custom-prop);
- }
- /// Retrieves the CSS fallback declaration value for a custom property Map.
- /// This is typically a static CSS value if a custom property has a fallback, or
- /// null if it does not.
- ///
- /// This function will always return `null` if custom properties or fallback
- /// values are disabled.
- ///
- /// @param {Map} $custom-prop - The custom property Map to retrieve a fallback
- /// declaration value for.
- /// @return {String | null} The CSS fallback declaration value.
- @function get-declaration-fallback($custom-prop) {
- $emit-custom-properties: map.get($_config, emit-custom-properties);
- $emit-fallback-values: map.get($_config, emit-fallback-values);
- @if not $emit-custom-properties or not $emit-fallback-values {
- @return null;
- }
- @return get-fallback($custom-prop);
- }
- /// Emits a CSS declaration for a custom property. A custom property may either
- /// be provided as the value to a CSS property string to be emitted as a var()
- /// function, or as a standalone value to be emitted as a custom property
- /// declaration.
- ///
- /// @example - scss
- /// @include declaration(color, create(--foo, teal));
- /// // color: var(--foo, teal);
- /// @include declaration(create(--foo, teal));
- /// // --foo: teal;
- ///
- /// Standalone custom properties must have a fallback value to emit as a CSS
- /// declaration.
- ///
- /// The declaration emitted for a custom property may change or be ignored
- /// based on the current configuration for custom properties.
- ///
- /// @see {mixin} css.declaration
- /// @see {mixin} configuration
- ///
- /// @param {String} $property - The CSS property of the declaration or the
- /// custom property Map to emit.
- /// @param {Map} $custom-prop - A custom property Map for the property's value.
- /// Optional if $property is the custom property Map to emit.
- /// @param {Map} $gss - An optional Map of GSS annotations to add.
- /// @param {Bool} $important - If true, add `!important` to the declaration.
- @mixin declaration($property, $custom-prop: null, $gss: (), $important: false) {
- @if $property {
- $value: null;
- $fallback-value: null;
- @if is-custom-prop($property) {
- @if map.get($_config, emit-custom-properties) {
- $custom-prop: $property;
- $property: get-varname($custom-prop);
- $value: get-fallback($custom-prop, $shallow: true);
- @if is-custom-prop($value) {
- $value: create-var($value);
- }
- }
- } @else {
- @if not is-custom-prop($custom-prop) {
- @error "Invalid custom property: #{$custom-prop}. Must be a Map with 'varname' and 'fallback'.";
- }
- $value: get-declaration-value($custom-prop);
- $fallback-value: get-declaration-fallback($custom-prop);
- }
- @include css.declaration(
- $property,
- $value,
- $fallback-value: $fallback-value,
- $gss: $gss,
- $important: $important
- );
- }
- }
|