@use '@angular/cdk';

@use '../core/tokens/m2/mat/sidenav' as tokens-mat-sidenav;
@use '../core/tokens/token-utils';
@use '../core/style/variables';
@use '../core/style/layout-common';

$drawer-content-z-index: 1;
$drawer-side-drawer-z-index: 2;
$drawer-backdrop-z-index: 3;
$drawer-over-drawer-z-index: 4;

// Mixin that creates a new stacking context.
// see https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Positioning/Understanding_z_index/The_stacking_context
@mixin drawer-stacking-context($z-index: 1) {
  position: relative;

  // Use a z-index to create a new stacking context. (We can't use transform because it breaks fixed
  // positioning inside of the transformed element).
  z-index: $z-index;
}

.mat-drawer-container {
  // We need a stacking context here so that the backdrop and drawers are clipped to the
  // MatDrawerContainer. This creates a new z-index stack so we use low numbered z-indices.
  // We create another stacking context in the '.mat-drawer-content' and in each drawer so that
  // the application content does not get messed up with our own CSS.
  @include drawer-stacking-context();

  @include token-utils.use-tokens(
    tokens-mat-sidenav.$prefix,
    tokens-mat-sidenav.get-token-slots()
  ) {
    @include token-utils.create-token-slot(color, content-text-color);
    @include token-utils.create-token-slot(background-color, content-background-color);
  }

  box-sizing: border-box;

  // Need this to take up space in the layout.
  display: block;

  // Hide the drawers when they're closed.
  overflow: hidden;

  // TODO(hansl): Update this with a more robust solution.
  &[fullscreen] {
    @include layout-common.fill();

    &.mat-drawer-container-has-open {
      overflow: hidden;
    }
  }

  // When the consumer explicitly enabled the backdrop,
  // we have to pull the side drawers above it.
  &.mat-drawer-container-explicit-backdrop .mat-drawer-side {
    z-index: $drawer-backdrop-z-index;
  }

  // Note that the `NoopAnimationsModule` is being handled inside of the component code.
  &.ng-animate-disabled,
  .ng-animate-disabled & {
    .mat-drawer-backdrop,
    .mat-drawer-content {
      transition: none;
    }
  }
}

.mat-drawer-backdrop {
  @include layout-common.fill();

  display: block;

  // Because of the new stacking context, the z-index stack is new and we can use our own
  // numbers.
  z-index: $drawer-backdrop-z-index;

  // We use 'visibility: hidden | visible' because 'display: none' will not animate any
  // transitions, while visibility will interpolate transitions properly.
  // see https://developer.mozilla.org/en-US/docs/Web/CSS/visibility, the Interpolation
  // section.
  visibility: hidden;

  &.mat-drawer-shown {
    visibility: visible;

    @include token-utils.use-tokens(
      tokens-mat-sidenav.$prefix,
      tokens-mat-sidenav.get-token-slots()
    ) {
      @include token-utils.create-token-slot(background-color, scrim-color);
    }
  }

  .mat-drawer-transition & {
    transition: {
      duration: variables.$swift-ease-out-duration;
      timing-function: variables.$swift-ease-out-timing-function;
      property: background-color, visibility;
    }
  }

  @include cdk.high-contrast {
    opacity: 0.5;
  }
}

.mat-drawer-content {
  @include drawer-stacking-context($drawer-content-z-index);

  display: block;
  height: 100%;
  overflow: auto;

  &.mat-drawer-content-hidden {
    opacity: 0;
  }

  .mat-drawer-transition & {
    transition: {
      duration: variables.$swift-ease-out-duration;
      timing-function: variables.$swift-ease-out-timing-function;
      property: transform, margin-left, margin-right;
    }
  }
}

.mat-drawer {
  $high-contrast-border: solid 1px currentColor;

  @include drawer-stacking-context($drawer-over-drawer-z-index);

  @include token-utils.use-tokens(
    tokens-mat-sidenav.$prefix,
    tokens-mat-sidenav.get-token-slots()
  ) {
    @include token-utils.create-token-slot(color, container-text-color);
    @include token-utils.create-token-slot(box-shadow, container-elevation-shadow);
    @include token-utils.create-token-slot(background-color, container-background-color);
    @include token-utils.create-token-slot(border-top-right-radius, container-shape);
    @include token-utils.create-token-slot(border-bottom-right-radius, container-shape);
    @include token-utils.create-token-slot(width, container-width);
  }

  display: block;
  position: absolute;
  top: 0;
  bottom: 0;
  z-index: 3;
  outline: 0;
  box-sizing: border-box;
  overflow-y: auto; // TODO(kara): revisit scrolling behavior for drawers
  transform: translate3d(-100%, 0, 0);

  &,
  [dir='rtl'] &.mat-drawer-end {
    @include cdk.high-contrast {
      border-right: $high-contrast-border;
    }
  }

  [dir='rtl'] &,
  &.mat-drawer-end {
    @include cdk.high-contrast {
      border-left: $high-contrast-border;
      border-right: none;
    }
  }

  &.mat-drawer-side {
    z-index: $drawer-side-drawer-z-index;
  }

  &.mat-drawer-end {
    right: 0;
    transform: translate3d(100%, 0, 0);

    @include token-utils.use-tokens(
      tokens-mat-sidenav.$prefix,
      tokens-mat-sidenav.get-token-slots()
    ) {
      @include token-utils.create-token-slot(border-top-left-radius, container-shape);
      @include token-utils.create-token-slot(border-bottom-left-radius, container-shape);
      border-top-right-radius: 0;
      border-bottom-right-radius: 0;
    }
  }

  [dir='rtl'] & {
    @include token-utils.use-tokens(
      tokens-mat-sidenav.$prefix,
      tokens-mat-sidenav.get-token-slots()
    ) {
      @include token-utils.create-token-slot(border-top-left-radius, container-shape);
      @include token-utils.create-token-slot(border-bottom-left-radius, container-shape);
      border-top-right-radius: 0;
      border-bottom-right-radius: 0;
      transform: translate3d(100%, 0, 0);

      &.mat-drawer-end {
        @include token-utils.create-token-slot(border-top-right-radius, container-shape);
        @include token-utils.create-token-slot(border-bottom-right-radius, container-shape);
        border-top-left-radius: 0;
        border-bottom-left-radius: 0;
        left: 0;
        right: auto;
        transform: translate3d(-100%, 0, 0);
      }
    }
  }

  // Usually the `visibility: hidden` added by the animation is enough to prevent focus from
  // entering the hidden drawer content, but children with their own `visibility` can override it.
  // This is a fallback that completely hides the content when the element becomes hidden.
  // Note that we can't do this in the animation definition, because the style gets recomputed too
  // late, breaking the animation because Angular didn't have time to figure out the target
  // transform. This can also be achieved with JS, but it has issues when starting an
  // animation before the previous one has finished.
  &[style*='visibility: hidden'] {
    display: none;
  }
}

.mat-drawer-side {
  box-shadow: none;

  @include token-utils.use-tokens(
    tokens-mat-sidenav.$prefix,
    tokens-mat-sidenav.get-token-slots()
  ) {
    @include token-utils.create-token-slot(border-right-color, container-divider-color);
    border-right-width: 1px;
    border-right-style: solid;

    &.mat-drawer-end {
      @include token-utils.create-token-slot(border-left-color, container-divider-color);
      border-left-width: 1px;
      border-left-style: solid;
      border-right: none;
    }

    [dir='rtl'] & {
      @include token-utils.create-token-slot(border-left-color, container-divider-color);
      border-left-width: 1px;
      border-left-style: solid;
      border-right: none; // Clears the default LTR border.

      &.mat-drawer-end {
        @include token-utils.create-token-slot(border-right-color, container-divider-color);
        border-right-width: 1px;
        border-right-style: solid;
        border-left: none;
      }
    }
  }
}

// Note that this div isn't strictly necessary on all browsers, however we need it in
// order to avoid a layout issue in Chrome. The issue is that in RTL mode the browser doesn't
// account for the sidenav's scrollbar while positioning, which ends up pushing it partially
// out of the screen. We work around the issue by having the scrollbar be on this inner container.
.mat-drawer-inner-container {
  width: 100%;
  height: 100%;
  overflow: auto;
}

.mat-sidenav-fixed {
  position: fixed;
}
