// FOUNDATION MOTION UI
// Table of Contents
//
// 0. Variables
// 1. Base Transitions
//      a. Slide
//      b. Fade
//      c. Hinge
//      d. Scale
//      e. Spin
// 2. Base Animations
//      a. Shake
//      b. Spinners
//      c. Wiggle
// 3. HTML Attributes

// 0. Variables
// - - - - - - - - - - - - - - - - - - - - - - - - -

/// @Foundation.settings
// Motion UI
// Classes to use when triggering in/out animations
$motion-class: (
    in: "ng-enter",
    out: "ng-leave"
) !default;
$motion-class-active: (
    in: "ng-enter-active",
    out: "ng-leave-active"
) !default;
$motion-class-stagger: (
    in: "ng-enter-stagger",
    out: "ng-leave-stagger"
) !default;
$motion-class-showhide: (
    in: "ng-hide-remove",
    out: "ng-hide-add"
);
$motion-class-showhide-active: (
    in: "ng-hide-remove-active",
    out: "ng-hide-add-active"
);

// Set if movement-based transitions should also fade the element in and out
$motion-slide-and-fade: false !default;
$motion-hinge-and-fade: true !default;
$motion-scale-and-fade: true !default;
$motion-spin-and-fade: true !default;

// Default speed for transitions and animations
$motion-duration-default: 500ms !default;

// Slow and fast modifiders
$motion-duration-slow: 750ms !default;
$motion-duration-fast: 250ms !default;
$motion-stagger-duration-default: 150ms !default;
$motion-stagger-duration-short: 50ms !default;
$motion-stagger-duration-long: 300ms !default;

// Default timing function for transitions and animations
$motion-timing-default: ease !default;

// Built-in and custom easing functions
// Every item in this map becomes a CSS class
$motion-timings: (
    linear: linear,
    ease: ease,
    easeIn: ease-in,
    easeOut: ease-out,
    easeInOut: ease-in-out,
    bounceIn: cubic-bezier(0.485, 0.155, 0.24, 1.245),
    bounceOut: cubic-bezier(0.485, 0.155, 0.515, 0.845),
    bounceInOut: cubic-bezier(0.76, -0.245, 0.24, 1.245)
) !default;

// Default delay for all transitions and animations
$motion-delay-default: 0 !default;
// Short and long delay modifiers
$motion-delay-short: 300ms !default;
$motion-delay-long: 700ms !default;
///

// Looks for a timing function in the list of presets
// If none are found, returns the value as-is.
@function get-timing($timing) {
    @if map-has-key($motion-timings, $timing) {
        @return map-get($motion-timings, $timing);
    } @else {
        @return $timing;
    }
}

// Applies transition settings common to all mixins
@mixin transition-basics(
    $duration: $motion-duration-default,
    $timing: $motion-timing-default,
    $delay: $motion-delay-default
) {
    transition-duration: $duration;
    transition-timing-function: get-timing($timing);
    transition-delay: $delay;
}
// Wraps content in an enter/leave class, chained to the parent selector
// Define the initial state of a transition here
@mixin transition-start($dir) {
    $sel1: map-get($motion-class, $dir);
    $sel2: map-get($motion-class-showhide, $dir);
    &.#{$sel1},
    &.#{$sel2} {
        @content;
    }
}
// Wraps content in an enter/leave active class, chained to the matching
// enter/leave class, chained to the parent selector
// Define the end state of a transition here
@mixin transition-end($dir) {
    $sel1: map-get($motion-class, $dir);
    $sel1A: map-get($motion-class-active, $dir);
    $sel2: map-get($motion-class-showhide, $dir);
    $sel2A: map-get($motion-class-showhide-active, $dir);
    &.#{$sel1}.#{$sel1A},
    &.#{$sel2}.#{$sel2A} {
        @content;
    }
}
@mixin stagger($delay-amount) {
    transition-delay: $delay-amount;
    // this is to avoid accidental CSS inheritance
    transition-duration: 0;
}
// 1. Base Transitions
// - - - - - - - - - - - - - - - - - - - - - - - - -
// SLIDE
@mixin slide(
    $dir: in,
    $from: left,
    $fade: $motion-slide-and-fade,
    $duration: $motion-duration-default,
    $timing: $motion-timing-default,
    $delay: $motion-delay-default
) {
    $slideDirections: (
        top: translateY(-100%),
        right: translateX(100%),
        bottom: translateY(100%),
        left: translateX(-100%)
    );
    $start: "";
    $end: "";
    @if $dir == in {
        $start: map-get($slideDirections, $from);
        $end: translateX(0) translateY(0);
    } @else {
        $start: translateX(0) translateY(0);
        $end: map-get($slideDirections, $from);
    }
    // CSS Output
    @include transition-start($dir) {
        @include transition-basics($duration, $timing, $delay);
        transition-property: transform, opacity;
        backface-visibility: hidden;
        transform: $start;
        @if $fade {
            opacity: if($dir == in, 0, 1);
        }
    }
    @include transition-end($dir) {
        transform: $end;
        @if $fade {
            opacity: if($dir == in, 1, 0);
        }
    }
}
// FADE
@mixin fade(
    $dir: in,
    $from: 0,
    $to: 1,
    $duration: $motion-duration-default,
    $timing: $motion-timing-default,
    $delay: $motion-delay-default
) {
    @include transition-start($dir) {
        @include transition-basics($duration, $timing, $delay);

        transition-property: opacity;
        opacity: $from;
    }
    @include transition-end($dir) {
        opacity: $to;
    }
}
// HINGE
@mixin hinge(
    $dir: in,
    $from: left,
    $axis: edge,
    $perspective: 2000px,
    $turn-origin: from-back,
    $fade: $motion-hinge-and-fade,
    $duration: $motion-duration-default,
    $timing: $motion-timing-default,
    $delay: $motion-delay-default
) {
    // Rotation directions when hinging from back vs. front
    $rotationAmount: 90deg;
    $rotationsBack: (
        top: rotateX($rotationAmount * -1),
        right: rotateY($rotationAmount * -1),
        bottom: rotateX($rotationAmount),
        left: rotateY($rotationAmount)
    );
    $rotationsFrom: (
        top: rotateX($rotationAmount),
        right: rotateY($rotationAmount),
        bottom: rotateX($rotationAmount * -1),
        left: rotateY($rotationAmount * -1)
    ); // Rotation origin
    $rotation: "";
    @if $turn-origin == from-front {
        $rotation: map-get($rotationsFrom, $from);
    } @else if $turn-origin == from-back {
        $rotation: map-get($rotationsBack, $from);
    } @else {
        @warn "`$turn-origin` must be either `from-back` or `from-front`";
    }
    // Start and end state
    $start: "";
    $end: "";
    @if $dir == in {
        $start: perspective($perspective) $rotation;
        $end: rotate(0deg);
    } @else {
        $start: rotate(0deg);
        $end: perspective($perspective) $rotation;
    }
    // Turn axis
    $origin: "";
    @if $axis == edge {
        $origin: $from;
    } @else {
        $origin: center;
    }
    @include transition-start($dir) {
        @include transition-basics($duration, $timing, $delay);
        transition-property: transform, opacity;
        transform: $start;
        transform-origin: $origin;
        @if $fade {
            opacity: if($dir == in, 0, 1);
        }
    }
    @include transition-end($dir) {
        transform: $end;
        @if $fade {
            opacity: if($dir == in, 1, 0);
        }
    }
}
// SCALE
@mixin scale(
    $dir: in,
    $from: 1.5,
    $to: 1,
    $fade: $motion-scale-and-fade,
    $duration: $motion-duration-default,
    $timing: $motion-timing-default,
    $delay: $motion-delay-default
) {
    @include transition-start($dir) {
        @include transition-basics($duration, $timing, $delay);
        transition-property: transform, property;
        transform: scale($from);
        @if $fade {
            opacity: if($dir == in, 0, 1);
        }
    }
    @include transition-end($dir) {
        transform: scale($to);
        @if $fade {
            opacity: if($dir == in, 1, 0);
        }
    }
}
// SPIN
@mixin spin(
    $dir: in,
    $amount: 0.75turn,
    $ccw: false,
    $fade: $motion-spin-and-fade,
    $duration: $motion-duration-default,
    $timing: $motion-timing-default,
    $delay: $motion-delay-default
) {
    $amount: turn-to-deg($amount);
    $start: 0;
    $end: 0;
    @if $dir == in {
        $start: if($ccw, $amount, $amount * -1);
        $end: 0;
    } @else {
        $start: 0;
        $end: if($ccw, $amount * -1, $amount);
    }
    @include transition-start($dir) {
        @include transition-basics($duration, $timing, $delay);
        transition-property: transform, opacity;
        transform: rotate($start);
        @if $fade {
            opacity: if($dir == in, 0, 1);
        }
    }
    @include transition-end($dir) {
        transform: rotate($end);
        @if $fade {
            opacity: if($dir == in, 1, 0);
        }
    }
}
// 2. Base Animations
// - - - - - - - - - - - - - - - - - - - - - - - - -
// SHAKE
@keyframes shake {
    0%,
    10%,
    20%,
    30%,
    40%,
    50%,
    60%,
    70%,
    80%,
    90% {
        transform: translateX(7%);
    }
    5%,
    15%,
    25%,
    35%,
    45%,
    55%,
    65%,
    75%,
    85%,
    95% {
        transform: translateX(-7%);
    }
    100% {
        transform: translateX(0);
    }
}
// SPINNERS
@keyframes spin-cw {
    0% {
        transform: rotate(0deg);
    }
    100% {
        transform: rotate(360deg);
    }
}
@keyframes spin-ccw {
    0% {
        transform: rotate(0deg);
    }
    100% {
        transform: rotate(-360deg);
    }
}
// WIGGLE
@keyframes wiggle {
    40%,
    50%,
    60% {
        transform: rotate(7deg);
    }

    35%,
    45%,
    55%,
    65% {
        transform: rotate(-7deg);
    }
    0%,
    30%,
    70%,
    100% {
        transform: rotate(0);
    }
}
@mixin animation(
    $animation,
    $duration: $motion-duration-default,
    $timing: $motion-timing-default,
    $delay: $motion-delay-default,
    $iterations: null
) {
    animation-name: $animation;
    animation-duration: $duration;
    animation-timing-function: $timing;
    backface-visibility: hidden;
    transform: translate3d(0, 0, 0);
    @if $delay != null {
        animation-delay: $delay;
    }
    @if $iterations != null {
        animation-iteration-count: $iterations;
    }
    @if $animation == null {
        @warn "Please include an animation name";
    }
}
// 3. HTML Exports
// - - - - - - - - - - - - - - - - - - - - - - - - -
@include exports(motion) {
    /* Transitions */
    // Slide
    .slideInUp {
        @include slide($from: bottom);
    }
    .slideInRight {
        @include slide($from: left);
    }
    .slideInDown {
        @include slide($from: top);
    }
    .slideInLeft {
        @include slide($from: right);
    }
    .slideOutUp {
        @include slide($dir: out, $from: top);
    }
    .slideOutRight {
        @include slide($dir: out, $from: right);
    }
    .slideOutDown {
        @include slide($dir: out, $from: bottom);
    }
    .slideOutLeft {
        @include slide($dir: out, $from: left);
    }
    // Fade
    .fadeIn {
        @include fade(in, 0, 1);
    }
    .fadeOut {
        @include fade(out, 1, 0);
    }
    // Hinge
    .hingeInFromTop {
        @include hinge($dir: in, $from: top);
    }
    .hingeInFromRight {
        @include hinge($dir: in, $from: right);
    }
    .hingeInFromBottom {
        @include hinge($dir: in, $from: bottom);
    }
    .hingeInFromLeft {
        @include hinge($dir: in, $from: left);
    }
    .hingeInFromMiddleX {
        @include hinge($dir: in, $from: top, $axis: center);
    }
    .hingeInFromMiddleY {
        @include hinge($dir: in, $from: right, $axis: center);
    }
    .hingeOutFromTop {
        @include hinge($dir: out, $from: top);
    }
    .hingeOutFromRight {
        @include hinge($dir: out, $from: right);
    }
    .hingeOutFromBottom {
        @include hinge($dir: out, $from: bottom);
    }
    .hingeOutFromLeft {
        @include hinge($dir: out, $from: left);
    }
    .hingeOutFromMiddleX {
        @include hinge($dir: out, $from: top, $axis: center);
    }
    .hingeOutFromMiddleY {
        @include hinge($dir: out, $from: right, $axis: center);
    }
    // Scale
    .zoomIn {
        @include scale(in, 1.5, 1);
    }
    .zoomOut {
        @include scale(out, 0.5, 1);
    }
    // Spin
    .spinIn {
        @include spin(in, 0.75turn);
    }
    .spinOut {
        @include spin(out, 0.75turn);
    }
    .spinInCCW {
        @include spin(in, 0.75turn, true);
    }
    .spinOutCCW {
        @include spin(out, 0.75turn, true);
    }
    /* Transition modifiers */ // Duration
    .slow {
        transition-duration: $motion-duration-slow !important;
    }
    .fast {
        transition-duration: $motion-duration-fast !important;
    }
    // Easing
    @each $easing in map-keys($motion-timings) {
        .#{$easing} {
            transition-timing-function: map-get(
                $motion-timings,
                $easing
            ) !important;
        }
    }
    // Delay
    .delay {
        transition-delay: $motion-delay-short !important;
    }
    .long-delay {
        transition-delay: $motion-delay-long !important;
    }
    /* Animations */
    .shake {
        @include animation(shake);
    }
    .spin-cw {
        @include animation(spin-cw);
    }
    .spin-ccw {
        @include animation(spin-ccw);
    }
    .wiggle {
        @include animation(wiggle);
    }
    /* Animation modifiers */
    .shake,
    .spin-cw,
    .spin-ccw,
    .wiggle {
        // Repeat
        &.infinite {
            animation-iteration-count: infinite;
        }
        // Easing
        @each $timing in map-keys($motion-timings) {
            &.#{$timing} {
                animation-timing-function: map-get(
                    $motion-timings,
                    $timing
                ) !important;
            }
        } // Duration
        &.slow {
            animation-duration: $motion-duration-slow !important;
        }
        &.fast {
            animation-duration: $motion-duration-fast !important;
        } // Delay
        &.delay {
            animation-delay: $motion-delay-short !important;
        }
        &.long-delay {
            animation-delay: $motion-delay-long !important;
        }
    }
    .stagger {
        @include stagger($motion-stagger-duration-default);
    }
    .stort-stagger {
        @include stagger($motion-stagger-duration-default);
    }
    .long-stagger {
        @include stagger($motion-stagger-duration-default);
    }
}
// View animation classes
// - - - - - - - - - - - - - - - - - - - -
// Applied to the immediate parent of the animating views
.position-absolute {
    overflow: hidden;
    position: relative;
}
// Applied to the animating views
[ui-view] {
    &.ng-enter-active,
    &.ng-leave-active {
        position: absolute !important;
        backface-visibility: hidden;
        -webkit-transform-style: preserve-3d;
        top: 0;
        right: 0;
        bottom: 0;
        left: 0;
    }
}
