@use 'sass:list';
@use 'sass:map';

$theme: null !default;

/* Breakpoint viewport sizes and media queries.

Breakpoints are defined as a map of (name: minimum width), order from small to large:
   (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px)

 The map defined in the `$breakpoint` global variable is used as the `$breakpoints` argument by default.

 Name of the next breakpoint, or null for the last breakpoint.

  >> breakpoint-next(sm)
   md
  >> breakpoint-next(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
   md
  >> breakpoint-next(sm, $breakpoint-names: (xs sm md lg xl))
   md
*/

@function next(
  $name,
  $breakpoints: map.get($theme, 'breakpoint'),
  $breakpoint-names: map.keys($breakpoints)
) {
  $n: list.index($breakpoint-names, $name);

  @return if(
    $n < list.length($breakpoint-names),
    list.nth($breakpoint-names, $n + 1),
    null
  );
}

/* Minimum breakpoint width. Null for the smallest (first) breakpoint.
    >> ecl-min(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
    576px
*/
@function ecl-min($name, $breakpoints: map.get($theme, 'breakpoint')) {
  $min: map.get($breakpoints, $name);

  @return if($min != 0, $min, null);
}

/* Maximum breakpoint width. Null for the largest (last) breakpoint.
   The maximum value is calculated as the minimum of the next one less 0.1.

   >> ecl-max(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
   767px
*/
@function ecl-max($name, $breakpoints: map.get($theme, 'breakpoint')) {
  $next: next($name, $breakpoints);

  @return if($next, ecl-min($next, $breakpoints) - 1px, null);
}

/* Returns a blank string if smallest breakpoint, otherwise returns the name with a dash infront.
    Useful for making responsive utilities.

    >> infix(xs, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
    ""  (Returns a blank string)
    >> infix(sm, (xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px))
    "-sm"
*/
@function infix($name, $breakpoints: map.get($theme, 'breakpoint')) {
  @return if(ecl-min($name, $breakpoints) == null, '', '-#{$name}');
}

// Media of at least the minimum breakpoint width. No query for the smallest breakpoint.
// Makes the @content apply to the given breakpoint and wider.
@mixin up($name, $breakpoints: map.get($theme, 'breakpoint')) {
  @if map.has-key($breakpoints, $name) {
    $min: ecl-min($name, $breakpoints);

    @if $min {
      // stylelint-disable-next-line media-query-no-invalid
      @media (min-width: $min) {
        @content;
      }
    } @else {
      @content;
    }
  } @else {
    @content;
  }
}

// Media of at most the maximum breakpoint width. No query for the largest breakpoint.
// Makes the @content apply to the given breakpoint and narrower.
@mixin down($name, $breakpoints: map.get($theme, 'breakpoint')) {
  @if map.has-key($breakpoints, $name) {
    $max: ecl-max($name, $breakpoints);

    @if $max {
      // stylelint-disable-next-line media-query-no-invalid
      @media (max-width: $max) {
        @content;
      }
    } @else {
      @content;
    }
  } @else {
    @content;
  }
}

// Media that spans multiple breakpoint widths.
// Makes the @content apply between the min and max breakpoints
@mixin between($lower, $upper, $breakpoints: map.get($theme, 'breakpoint')) {
  @include up($lower, $breakpoints) {
    @include down($upper, $breakpoints) {
      @content;
    }
  }
}

// Media between the breakpoint"s minimum and maximum widths.
// No minimum for the smallest breakpoint, and no maximum for the largest one.
// Makes the @content apply only to the given breakpoint, not viewports any wider or narrower.
@mixin only($name, $breakpoints: map.get($theme, 'breakpoint')) {
  @include between($name, $name, $breakpoints) {
    @content;
  }
}
