// Foundation for Sites by ZURB
// foundation.zurb.com
// Licensed under MIT Open Source

////
/// @group flex-grid
////

$-zf-flex-justify: (
  'left': flex-start,
  'right': flex-end,
  'center': center,
  'justify': space-between,
  'spaced': space-around,
);

$-zf-flex-align: (
  'top': flex-start,
  'bottom': flex-end,
  'middle': center,
  'stretch': stretch,
);

/// Creates a container for a flex grid row.
///
/// @param {Keyword|List} $behavior [null]
///   Modifications to the default grid styles. `nest` indicates the row will be placed inside another row. `collapse` indicates that the columns inside this row will not have padding. `nest collapse` combines both behaviors.
/// @param {Number} $width [$grid-row-width] - Maximum width of the row.
/// @param {Number} $columns [null] - Number of columns to use for this row. If set to `null` (the default), the global column count will be used.
/// @param {Boolean} $base [true] - Set to `false` to prevent basic styles from being output. Useful if you're calling this mixin on the same element twice, as it prevents duplicate CSS output.
/// @param {Number} $gutter [$grid-column-gutter] - Gutter to use when inverting margins, in case the row is nested.
@mixin flex-grid-row(
  $behavior: null,
  $width: $grid-row-width,
  $columns: null,
  $base: true,
  $gutter: $grid-column-gutter
) {
  $behavior: -zf-get-options($behavior, nest collapse);
  $margin: auto;

  @if map-get($behavior, nest) {
    @include grid-row-nest($gutter);

    @if map-get($behavior, collapse) {
      margin-left: 0;
      margin-right: 0;
    }
  }
  @else {
    max-width: $width;
    margin-left: auto;
    margin-right: auto;
  }

  @if $base {
    display: flex;
    flex-flow: row wrap;
  }

  @if $columns != null {
    @include grid-context($columns, $base) {
      @content;
    }
  }
}

/// Calculates the `flex` property for a flex grid column. It accepts all of the same values as the basic `grid-column()` function, along with two extras:
///   - `null` (the default) will make the column expand to fill space.
///   - `shrink` will make the column contract, so it only takes up the horizontal space it needs.
///
/// @param {Mixed} $columns [null] - Width of the column.
@function flex-grid-column($columns: null) {
  $flex: 1 1 0px;

  @if $columns == shrink {
    $flex: 0 0 auto;
  }
  @else if $columns != null {
    $flex: 0 0 grid-column($columns);
  }

  @return $flex;
}

/// Creates a column for a flex grid. By default, the column will stretch to the full width of its container, but this can be overridden with sizing classes, or by using the `unstack` class on the parent flex row.
///
/// @param {Mixed} $columns [null] - Width of the column. Refer to the `flex-grid-column()` function to see possible values.
/// @param {Number} $gutter [$grid-column-gutter] - Space between columns, added as a left and right padding.
@mixin flex-grid-column(
  $columns: null,
  $gutter: $grid-column-gutter
) {
  @if $gutter != null {
    $gutter: rem-calc($gutter) / 2;
  }
  @else {
    @each $breakpoint, $gutter in $grid-column-responsive-gutter {
      $padding: rem-calc($gutter) / 2;
      
      @include breakpoint($breakpoint) {
        padding-left: $padding;
        padding-right: $padding;
      }
    }
  }

  flex: flex-grid-column($columns);
  padding-left: $gutter;
  padding-right: $gutter;

  // max-width fixes IE 10/11 not respecting the flex-basis property
  @if $columns != null and $columns != shrink {
    max-width: grid-column($columns);
  }
}

/// Changes the source order of a flex grid column. Columns with lower numbers appear first in the layout.
/// @param {Number} $order [0] - Order number to apply.
@mixin flex-grid-order($order: 0) {
  order: $order;
}

/// Horizontally or vertically aligns the columns within a flex row. Apply this mixin to a flex row.
///
/// @param {Keyword} $x [null] - Horizontal alignment to use. Can be `left`, `right`, `center`, `justify`, or `spaced`. Or, set it to `null` (the default) to not set horizontal alignment.
/// @param {Keyword} $y [null] - Vertical alignment to use. Can be `top`, `bottom`, `middle`, or `stretch`. Or, set it to `null` (the default) to not set vertical alignment.
@mixin flex-grid-row-align($x: null, $y: null) {
  @if $x {
    @if map-has-key($-zf-flex-justify, $x) {
      $x: map-get($-zf-flex-justify, $x);
    }
    @else {
      @warn 'flex-grid-row-align(): #{$x} is not a valid value for horizontal alignment. Use left, right, center, justify, or spaced.'
    }
  }

  @if $y {
    @if map-has-key($-zf-flex-align, $y) {
      $y: map-get($-zf-flex-align, $y);
    }
    @else {
      @warn 'flex-grid-row-align(): #{$y} is not a valid value for vertical alignment. Use top, bottom, middle, or stretch.'
    }
  }

  justify-content: $x;
  align-items: $y;
}

/// Vertically align a single column within a flex row. Apply this mixin to a flex column.
///
/// @param {Keyword} $y [null] - Vertical alignment to use. Can be `top`, `bottom`, `middle`, or `stretch`. Or, set it to `null` (the default) to not set vertical alignment.
@mixin flex-grid-column-align($y: null) {
  @if $y {
    @if map-has-key($-zf-flex-align, $y) {
      $y: map-get($-zf-flex-align, $y);
    }
    @else {
      @warn 'flex-grid-column-align(): #{$y} is not a valid value for alignment. Use top, bottom, middle, or stretch.'
    }
  }

  align-self: $y;
}

@mixin foundation-flex-grid {
  // Row
  .row {
    @include flex-grid-row;

    // Nesting behavior
    & &,
    .column-row & {
      @include flex-grid-row(nest, $base: false);
    }

    // Expanded row
    &.expanded {
      max-width: none;
    }

    &.collapse {
      > .column { @include grid-col-collapse; }
    }
  }

  // Column
  .column {
    @include flex-grid-column;
  }

  @include -zf-each-breakpoint {
    @for $i from 1 through $grid-column-count {
      // Sizing (percentage)
      .#{$-zf-size}-#{$i} {
        flex: flex-grid-column($i);
        max-width: grid-column($i);
      }

      // Offsets
      $o: $i - 1;

      .#{$-zf-size}-offset-#{$o} {
        @include grid-column-offset($o);
      }
    }

    @for $i from 1 through 6 {
      // Source ordering
      .#{$-zf-size}-order-#{$i} {
        @include flex-grid-order($i);
      }
    }

    @if $-zf-size != small {
      // Sizing (expand)
      @include breakpoint($-zf-size) {
        .#{$-zf-size}-expand {
          flex: flex-grid-column();
        }
      }

      // Auto-stacking/unstacking
      @at-root (without: media) {
        .row.#{$-zf-size}-unstack {
          .column {
            flex: flex-grid-column(100%);

            @include breakpoint($-zf-size) {
              flex: flex-grid-column();
            }
          }
        }
      }
    }

    // Responsive collapsing
    .#{$-zf-size}-collapse {
      > .column { @include grid-col-collapse; }
    }

    .#{$-zf-size}-uncollapse {
      @if $grid-column-gutter {
        $gutter: $grid-column-gutter;
      }
      @else {
        $gutter: -zf-get-bp-val($grid-column-responsive-gutter, $-zf-size);
      }
      > .column { @include grid-col-uncollapse($gutter); }
    }
  }

  // Sizing (shrink)
  .shrink {
    flex: flex-grid-column(shrink);
  }

  // Horizontal alignment using justify-content
  @each $hdir, $prop in map-remove($-zf-flex-justify, left) {
    .row.align-#{$hdir} {
      @include flex-grid-row-align($x: $hdir);
    }
  }

  // Vertical alignment using align-items and align-self
  @each $vdir, $prop in $-zf-flex-align {
    .row.align-#{$vdir} {
      @include flex-grid-row-align($y: $vdir);
    }

    .column.align-#{$vdir} {
      @include flex-grid-column-align($vdir);
    }
  }

  .columns {
    // scss-lint:disable PlaceholderInExtend
    @extend .column;
  }
}
