@charset 'UTF-8';

////
/// Flavor SCSS Mixins Animation
/// @group mixins-animation
/// @author blackmirror1980
////

/// Animation name modes supported
///
/// @access private
/// @type list
$animation-name-modes: array-concat((none), $css-default-modes);

/// Checks if animation name is string or animation-name-mode (none, inherit, initial)
///
/// @link https://www.w3schools.com/cssref/css3_pr_animation-name.asp W3Schools animation-name docs
/// @access public
/// @param {string} $an - the animation-name value
/// @return {boolean} - true if is animation-name value
@function is-animation-name($an) {
  @return is-string($an) or array-contains($animation-name-modes, $an);
}

/// Checks if animation time is a correct time value
///
/// @link https://www.w3schools.com/cssref/css3_pr_animation-duration.asp W3Schools animation-duration docs
/// @access public
/// @param {string} $at - the animation-time value
/// @return {boolean} - true if is animation-time value
@function is-animation-time($at) {
  @return is-time($at) or is-css-default($at);
}

/// Animation direction modes supported
///
/// @access private
/// @type list
$animation-direction-modes: array-concat((normal, reverse, alternate, alternate-reverse), $css-default-modes);

/// Checks if animation direction is a correct direction mode
///
/// @link https://www.w3schools.com/cssref/css3_pr_animation-direction.asp W3Schools animation-direction docs
/// @access public
/// @param {string} $ad - the animation-direction mode
/// @return {boolean} - true if is animation-direction mode
@function is-animation-direction($ad) {
  @return is-defined($ad) and array-contains($animation-direction-modes, $ad);
}

/// Animation iteration count modes supported
///
/// @access private
/// @type list
$animation-iteration-count-modes: array-concat((infinite), $css-default-modes);

/// Checks if animation iteration count is a correct iteration count value (integer or infinite, initial, inherit)
///
/// @link https://www.w3schools.com/cssref/css3_pr_animation-iteration-count.asp W3Schools animation-iteration-count docs
/// @access public
/// @param {string} $aic - the animation-iteration-count value
/// @return {boolean} - true if is animation-iteration-count value
@function is-animation-iteration-count($aic) {
  @return is-defined($aic) and (is-integer($aic) or array-contains($animation-iteration-count-modes, $aic));
}

/// Animation fill modes supported
///
/// @access private
/// @type list
$animation-fill-modes: array-concat((none, forwards, backwards, both), $css-default-modes);

/// Checks if animation fill mode is a correct fill mode value (integer or none, forwards, backwards, both, initial, inherit)
///
/// @link https://www.w3schools.com/cssref/css3_pr_animation-fill-mode.asp W3Schools animation-fill-mode docs
/// @access public
/// @param {string} $afm - the animation-fill mode
/// @return {boolean} - true if is animation-fill mode
@function is-animation-fill($afm) {
  @return is-defined($afm) and array-contains($animation-fill-modes, $afm);
}

/// Animation timing function modes supported
///
/// @access private
/// @type list
$animation-timing-function-modes: array-concat((linear, ease, ease-in, ease-out, ease-in-out, step-start, step-end), $css-default-modes);

/// Checks if animation timing function mode is a correct timing function (steps(), cubid-bezier(), linear, ease, ease-in, ease-out, ease-in-out, step-start, step-end, initial, inherit)
///
/// @link https://www.w3schools.com/cssref/css3_pr_animation-timing-function.asp W3Schools animation-timing-function docs
/// @requires {function} array-contains
/// @requires {function} is-defined
/// @requires {function} string-starts-with
/// @requires {variable} animation-timing-function-modes
/// @access public
/// @param {string} $atf - the animation-timing-function value
/// @return {boolean} - true if is animation-timing-function value
@function is-animation-timing-function($atf) {
  @return is-defined($atf) and (string-starts-with($atf, 'steps') or string-starts-with($atf, 'cubic-bezier') or array-contains($animation-timing-function-modes, $atf));
}

/// Animation play state supported
///
/// @access private
/// @type list
$animation-play-state-modes: array-concat((paused, running), $css-default-modes);

/// Checks if animation play state is a correct play state mode (paused, running, initial, inherit)
///
/// @link https://www.w3schools.com/cssref/css3_pr_animation-play-state.asp W3Schools animation-play-state docs
/// @access public
/// @param {string} $aps - the animation-play-state mode
/// @return {boolean} - true if is animation-play-state mode
@function is-animation-play-state-modes($aps) {
  @return is-defined($aps) and array-contains($animation-play-state-modes, $aps);
}

/// Keyframe Mixin
///
/// @example scss - Usage - Fade Out
///   @include keyframes(fade-out) {
///     0% {
///       opacity: 1;
///     }
///     100% {
///       opacity: 0;
///     }
///   }
///
/// @example css - Output - Fade Out
///   @keyframes fade-out {
///     0% {
///       opacity: 1;
///     }
///     100% {
///       opacity: 0;
///     }
///   }
///
/// @example scss - Usage - Change Color
///   @include keyframes(change-color) {
///     0% {
///       color: #000;
///     }
///     100% {
///       color: #FFF;
///     }
///   }
///
/// @example css - Output - Change Color
///   @keyframes change-color {
///     0% {
///       color: #000;
///     }
///     100% {
///       color: #FFF;
///     }
///   }
/// @access public
/// @param {string} $animation-name - the animation name
@mixin keyframes($animation-name) {
  @keyframes #{$animation-name} {
    @content;
  }
}

/// Animation Mixin
///
/// @example scss - Usage
///   .animated-element {
///     @include animation(change-color 10s 5s);
///   }
///
/// @example css - Output
///   .animated-element {
///     -webkit-animation-name: change-color;
///     -moz-animation-name: change-color;
///     -ms-animation-name: change-color;
///     -o-animation-name: change-color;
///     animation-name: change-color;
///
///     -webkit-animation-duration: 10s;
///     -moz-animation-duration: 10s;
///     -ms-animation-duration: 10s;
///     -o-animation-duration: 10s;
///     animation-duration: 10s;
///
///     -webkit-animation-delay: 5s;
///     -moz-animation-delay: 5s;
///     -ms-animation-delay: 5s;
///     -o-animation-delay: 5s;
///     animation-delay: 5s;
///
///     -webkit-animation-direction: normal;
///     -moz-animation-direction: normal;
///     -ms-animation-direction: normal;
///     -o-animation-direction: normal;
///     animation-direction: normal;
///
///     -webkit-animation-iteration-count: 0;
///     -moz-animation-iteration-count: 0;
///     -ms-animation-iteration-count: 0;
///     -o-animation-iteration-count: 0;
///     animation-iteration-count: 0;
///
///     -webkit-animation-timing-function: ease;
///     -moz-animation-timing-function: ease;
///     -ms-animation-timing-function: ease;
///     -o-animation-timing-function: ease;
///     animation-timing-function: ease;
///
///     -webkit-animation-fill-mode: forwards;
///     -moz-animation-fill-mode: forwards;
///     -ms-animation-fill-mode: forwards;
///     -o-animation-fill-mode: forwards;
///     animation-fill-mode: forwards;
///
///     -webkit-animation-play-state: initial;
///     -moz-animation-play-state: initial;
///     -ms-animation-play-state: initial;
///     -o-animation-play-state: initial;
///     animation-play-state: initial;
///   }
/// @requires {function} extend
/// @requires {function} is-defined
/// @requires {function} is-animation-time
/// @requires {function} is-animation-direction
/// @requires {function} nth-value
/// @requires {mixin} prefixer
/// @access public
/// @param {object | map} $options - animation options
/// @param {string | animation-name-mode} $options.name [none] - animation name
/// @param {time} $options.duration [0s] - animation duration
/// @param {time} $options.delay [0s] - animation delay
/// @param {string} $options.direction [normal] - animation direction
/// @param {integer} $options.iteration-count [0] - animation iteration count
/// @param {string} $options.timing-function [ease] - animation timing function (easing)
/// @param {string} $options.fill-mode [forwards] - animation fill mode
/// @param {string} $options.play-state [initial] - animation play state
/// @param {boolean} $important [false] - if true, will render the important rule
@mixin animation($options, $important: false) {
  @if is-css-default($options) {
    @include prefixer(animation, $options, null, $important);
  }
  @else {
    $settings: (name: none, duration: 0s, delay: 0s, direction: normal, iteration-count: 0, timing-function: ease, fill-mode: forwards, play-state: initial);

    @if is-object($options) {
      $settings: extend($settings, $options);
    }
    @else {
      $name: nth-value($options, 1);

      @if is-defined($name) {
        $settings: extend($settings, (name: $name));
      }

      $duration: nth-value($options, 2);

      @if is-defined($duration) {
        $settings: extend($settings, (duration: $duration));
      }

      $delay: nth-value($options, 3);

      @if is-defined($delay) {
        $settings: extend($settings, (delay: $delay));
      }

      $direction: nth-value($options, 4);

      @if is-defined($direction) {
        $settings: extend($settings, (direction: $direction));
      }

      $iteration-count: nth-value($options, 5);

      @if is-defined($iteration-count) {
        $settings: extend($settings, (iteration-count: $iteration-count));
      }

      $timing-function: nth-value($options, 6);

      @if is-defined($timing-function) {
        $settings: extend($settings, (timing-function: $timing-function));
      }

      $fill-mode: nth-value($options, 7);

      @if is-defined($fill-mode) {
        $settings: extend($settings, (fill-mode: $fill-mode));
      }

      $play-state: nth-value($options, 8);

      @if is-defined($play-state) {
        $settings: extend($settings, (play-state: $play-state));
      }
    }

    $animation: ();

    $animation-name: map-get($settings, name);

    @if is-animation-name($animation-name) {
      @include prefixer(animation-name, $animation-name, null, $important);
    }
    @else {
      @error '`animation-name: #{$animation-name}` is mandatory parameter';
    }

    $animation-duration: map-get($settings, duration);

    @if is-animation-time($animation-duration) {
      @include prefixer(animation-duration, $animation-duration, null, $important);
    }
    @else {
      @warn '`animation-duration: #{$animation-duration}` is not a valid animation time value (only time & css defaults are allowed)';
    }

    $animation-delay: map-get($settings, delay);

    @if is-animation-time($animation-delay) {
      @include prefixer(animation-delay, $animation-delay, null, $important);
    }
    @else {
      @warn '`animation-delay: #{$animation-delay}` is not a valid animation time value (only time & css defaults are allowed)';
    }

    $animation-direction: map-get($settings, direction);

    @if is-animation-direction($animation-direction) {
      @include prefixer(animation-direction, $animation-direction, null, $important);
    }
    @else {
      @warn '`animation-direction: #{$animation-direction}` is not a valid animation direction value';
    }

    $animation-iteration-count: map-get($settings, iteration-count);

    @if is-animation-iteration-count($animation-iteration-count) {
      @include prefixer(animation-iteration-count, $animation-iteration-count, null, $important);
    }
    @else {
      @warn '`animation-iteration-count: #{$animation-iteration-count}` is not a valid animation iteration count value';
    }

    $animation-timing-function: map-get($settings, timing-function);

    @if is-animation-timing-function($animation-timing-function) {
      @include prefixer(animation-timing-function, $animation-timing-function, null, $important);
    }
    @else {
      @warn '`animation-timing-function: #{$animation-timing-function}` is not a valid animation timing function value';
    }

    $animation-fill-mode: map-get($settings, fill-mode);

    @if is-animation-fill($animation-fill-mode) {
      @include prefixer(animation-fill-mode, $animation-fill-mode, null, $important);
    }
    @else {
      @warn '`animation-fill-mode: #{$animation-fill-mode}` is not a valid animation fill mode value';
    }

    $animation-play-state: map-get($settings, play-state);

    @if is-animation-direction($animation-play-state) {
      @include prefixer(animation-play-state, $animation-play-state, null, $important);
    }
    @else {
      @warn '`animation-play-state: #{$animation-play-state}` is not a valid animation play state value';
    }
  }
}
