// =============================================================================
// TOOLS / #MIXINS
// =============================================================================

// @include add-color()
// ==============================================

// $name - Name of the color
// $value - Hex value of the color
//
// For example:
//
//   @include add-color(sky1, #0076be);
//
//   .foo {
//     color: color(sky1);
//   }
@mixin add-color($key, $value) {
  $new-color: ($key: $value);

  @if type-of($value) == color {
    $colors: map-merge($colors, $new-color) !global;
  } @else {
    @warn "#{$value} is not a valid color.";
  }
}

// @include breakout()
// ==============================================

// Breakout of a parent element's `max-width` to fill the entire viewport width.
@mixin breakout() {
  position: relative !important;
  width: 100vw !important;
  left: 50% !important;
  margin-left: -50vw !important;
}

// @include focus-styles()
// ==============================================

// Generates a consistent outline shadow for use on `:focus` or `.has-focus`.
@mixin focus-styles($color: default) {
  outline: none;

  @if ($color == "default") {
    box-shadow: 0 1px 3px rgba(color(black), 0.4), 0 1px 15px 3px rgba(color(highlight), 0.75);
  } @else if ($color == "invert") {
    box-shadow: 0 1px 3px rgba(color(black), 0.75), 0 1px 15px 3px rgba(color(white), 0.75);
  } @else {
    @warn "`#{$color}` is not a valid outline color, it must be `default` or `invert`.";
  }
}

// @include hocus()
// ==============================================

// Generate `:hover` and `:focus` styles in one go.
// http://www.456bereastreet.com/archive/201004/whenever_you_use_hover_also_use_focus/
@mixin hocus() {
  &:hover,
  &:focus {
    @content;
  }
}

// @include hide-visually()
// ==============================================

// Visually hide content, leaving it accessible to screenreaders and ATs.
// http://snook.ca/archives/html_and_css/hiding-content-for-accessibility
@mixin hide-visually($option: default, $important: true) {
  // Hiding elements visually overrides any matching property declarations
  $default: (
    border: 0,
    clip: rect(0 0 0 0),
    height: 1px,
    margin: -1px,
    overflow: hidden,
    padding: 0,
    position: absolute,
    width: 1px,
    white-space: nowrap,
  );

  // Override the default to re-enable display
  $reset: (
    clip: auto,
    height: auto,
    margin: 0,
    overflow: visible,
    position: static,
    width: auto,
    white-space: normal,
  );

  // Select the map to use
  $apply-properties: $default;

  @if ($option == "default") {
    $apply-properties: $default;
  } @else if ($option == "reset") {
    $apply-properties: $reset;
  } @else {
    @warn "`#{$option}` is not valid, it must be `default` or `reset`";
  }

  // Generate rules
  @each $prop, $value in $apply-properties {
    #{$prop}: #{$value}#{_important($important)};
  }
}

// @include hide-completely()
// ==============================================

// Completely hide content both visually from the screen, and from screenreaders
// and ATs.
@mixin hide-completely() {
  /* Hiding elements completely overrides any matching property declarations */
  display: none !important;
}

// @include clearfix()
// ==============================================

@mixin clearfix() {
  &::after {
    content: "";
    display: table;
    clear: both;
  }
}

// @include responsivize()
// ==============================================

// This mixin can be applied to any class and will produce classes that apply at
// breakpoints as well as one that applies to all breakpoints.
// The mixin takes one argument - a list of keys separated by spaces.
// The keys in the argument must be present in the $mq-breakpoints map
// (small, medium, large, x-large).
//
// If no argument is provided, it is presumed that you want classes generating
// for all breakpoints in the $mq-breakpoints map.
//
// See `docs/` for usage.
@mixin responsivize($breakpoints: $mq-breakpoints) {
  @at-root #{&} {
    @content;
  }

  @each $alias, $bp in $breakpoints {
    @if map-has-key($mq-breakpoints, $alias) {
      @include mq($from: map-get($mq-breakpoints, $alias)) {
        @at-root #{&}\@#{$alias} {
          @content;
        }
      }
    } @else {
      @warn "Unrecognised breakpoint - `#{$alias}` does not match any of the keys in the $mq-breakpoints map";
    }
  }
}

// @include edge-position()
// ==============================================

// Used to simplify and avoid repeated use of individual edge positioning.
// See `docs/` for usage.
@mixin edge-position($edge-position-offset: 0) {
  top: $edge-position-offset;
  right: $edge-position-offset;
  bottom: $edge-position-offset;
  left: $edge-position-offset;
}

// @include height-sizing()
// ==============================================

// Used to calculate fixed height elements via line-height and height or
// padding-based spacing.
// See `docs/` for usage.
@mixin height-sizing($height: fixed, $line-height: 26px) {
  // Note: 26px is the hard-coded value of `line-height(text-body, large)`.
  $default-border-width: $global-border-width;
  $default-height: $global-spacing-unit-large;
  $default-height-rounded: decimal-round(convert-to-em($default-height), 2);

  @if $height == fixed {
    padding-top: 0;
    padding-bottom: 0;
    height: $default-height-rounded;
    line-height: $default-height-rounded;
  } @else if $height == padding {
    @if type-of($line-height) == number {
      $padding-vertical: ($default-height - $line-height) / 2;
      padding-top: $padding-vertical - $default-border-width;
      padding-bottom: $padding-vertical - $default-border-width;
      line-height: $line-height;
    } @else {
      @warn "#{$line-height}` is not a valid option. Line-height can only be a sizing unit";
    }
  } @else {
    @warn "`#{$height}` is not a valid option. Height can only be `fixed` or `padding`-based.";
  }
}

// @include browser-hack()
// ==============================================
//  Sources for hacks:
// - IE11: https://stackoverflow.com/questions/20541306/how-to-write-a-css-hack-for-ie-11#22085269
// - Edge: https://stackoverflow.com/questions/32201586/how-to-identify-microsoft-edge-browser-via-css#32202953
@mixin browser-hack($browser...) {
  @each $version in $browser {
    @if $version == ie11 {
      @at-root _:-ms-fullscreen, :root & {
        @content;
      }
    } @else if $version == edge {
      @supports (-ms-ime-align: auto) {
        @content;
      }
    } @else {
      @warn "#{browser} is not a valid option. $browser can be one of 'ie11' or 'edge'";
    }
  }
}
