@use '@material/touch-target' as mdc-touch-target;

@use '../core/tokens/token-utils';
@use '../core/style/layout-common';
@use '../core/mdc-helpers/mdc-helpers';

// Adds styles necessary to provide stateful interactions with the button. This includes providing
// content for the state container's ::before and ::after so that they can be given a background
// color and opacity for states like hover, active, and focus. Additionally, adds styles to the
// ripple and state container so that they fill the button, match the border radius, and avoid
// pointer events.
@mixin mat-private-button-interactive() {
  -webkit-tap-highlight-color: transparent;

  // The ripple container should match the bounds of the entire button.
  .mat-mdc-button-ripple,
  .mat-mdc-button-persistent-ripple,
  .mat-mdc-button-persistent-ripple::before {
    @include layout-common.fill;

    // Disable pointer events for the ripple container and state overlay because the container
    // will overlay the user content and we don't want to disable mouse events on the user content.
    // Pointer events can be safely disabled because the ripple trigger element is the host element.
    pointer-events: none;

    // Inherit the border radius from the parent so that state overlay and ripples don't exceed the
    // parent button boundaries. Note that an inherited border radius does not work properly if
    // the actual button element does have a border because it causes the inner content to be
    // smaller. We have special logic for stroked buttons to handle this scenario.
    border-radius: inherit;
  }

  // This style used to be applied by the MatRipple
  // directive, which is no longer attached to this element.
  .mat-mdc-button-ripple {
    overflow: hidden;
  }

  // We use ::before so that we can reuse some of MDC's theming.
  .mat-mdc-button-persistent-ripple::before {
    content: '';
    opacity: 0;
  }

  // The content should appear over the state and ripple layers, otherwise they may adversely affect
  // the accessibility of the text content.
  .mdc-button__label {
    z-index: 1;
  }

  // The focus indicator should match the bounds of the entire button.
  .mat-mdc-focus-indicator {
    @include layout-common.fill();
  }

  &:focus .mat-mdc-focus-indicator::before {
    content: '';
  }
}

@mixin mat-private-button-ripple($prefix, $slots) {
  @include token-utils.use-tokens($prefix, $slots) {
    .mat-ripple-element {
      @include token-utils.create-token-slot(background-color, ripple-color);
    }

    .mat-mdc-button-persistent-ripple::before {
      @include token-utils.create-token-slot(background-color, state-layer-color);
    }

    &.mat-mdc-button-disabled .mat-mdc-button-persistent-ripple::before {
      @include token-utils.create-token-slot(background-color, disabled-state-layer-color);
    }

    &:hover .mat-mdc-button-persistent-ripple::before {
      @include token-utils.create-token-slot(opacity, hover-state-layer-opacity);
    }

    &.cdk-program-focused,
    &.cdk-keyboard-focused,
    &.mat-mdc-button-disabled-interactive:focus {
      .mat-mdc-button-persistent-ripple::before {
        @include token-utils.create-token-slot(opacity, focus-state-layer-opacity);
      }
    }

    &:active .mat-mdc-button-persistent-ripple::before {
      @include token-utils.create-token-slot(opacity, pressed-state-layer-opacity);
    }
  }
}

// MDC's disabled buttons define a default cursor with pointer-events none. However, they select
// :disabled for this, which does not affect anchor tags.
// TODO(andrewseguin): Discuss with the MDC team about a mixin we can call for applying this style,
// and note that having pointer-events may have unintended side-effects, e.g. allowing the user
// to click the target underneath the button.
@mixin mat-private-button-disabled() {
  // `[disabled]` shouldn't be necessary, but we keep it to maintain
  // compatibility with apps setting it through host bindings.
  &[disabled],
  &.mat-mdc-button-disabled {
    cursor: default;
    pointer-events: none;
    @content;
  }

  &.mat-mdc-button-disabled-interactive {
    pointer-events: auto;
  }
}

// Adds an elevation shadow to a button.
@mixin mat-private-button-elevation($token-name) {
  // MDC outputs a variable that is the same as the token name, but suffixed with `-shadow`.
  box-shadow: var(#{token-utils.get-token-variable($token-name) + '-shadow'});
}

@mixin mat-private-button-touch-target($is-square, $prefix, $slots) {
  // Element used to ensure that the button has a touch target that meets the required minimum.
  // Note that we use this, instead of MDC's built-in `mdc-button--touch` class, because the MDC
  // class is implemented as `margin-top: 6px; margin-bottom: 6px` on the host element which
  // goes against our rule of not having margins on the host node. Furthermore, having the margin on
  // the button itself would require us to wrap it in another div. See:
  // https://github.com/material-components/material-components-web/tree/master/packages/mdc-button#making-buttons-accessible
  .mat-mdc-button-touch-target {
    @include mdc-touch-target.touch-target(
      $set-width: $is-square,
      $query: mdc-helpers.$mdc-base-styles-query);

    @include token-utils.use-tokens($prefix, $slots) {
      @include token-utils.create-token-slot(display, touch-target-display);
    }
  }
}

@mixin mat-private-button-horizontal-layout($prefix, $slots, $has-with-icon-padding) {
  @include token-utils.use-tokens($prefix, $slots) {
    $icon-spacing: token-utils.get-token-variable-reference(icon-spacing, true);
    $icon-offset: token-utils.get-token-variable-reference(icon-offset, true);
    $horizontal-padding: token-utils.get-token-variable-reference(horizontal-padding, true);
    padding: 0 $horizontal-padding;

    @if ($has-with-icon-padding) {
      $with-icon-horizontal-padding:
        token-utils.get-token-variable-reference(with-icon-horizontal-padding, true);

      // stylelint-disable-next-line selector-class-pattern
      &:has(.material-icons, mat-icon, [matButtonIcon]) {
        padding: 0 $with-icon-horizontal-padding;
      }
    }

    // MDC expects button icons to contain this HTML content:
    // ```html
    //   <span class="mdc-button__icon material-icons">favorite</span>
    // ```
    // However, Angular Material expects a `mat-icon` instead. The following
    // styles will lay out the icons appropriately.
    & > .mat-icon {
      margin-right: $icon-spacing;
      margin-left: $icon-offset;

      [dir='rtl'] & {
        margin-right: $icon-offset;
        margin-left: $icon-spacing;
      }
    }

    .mdc-button__label + .mat-icon {
      margin-right: $icon-offset;
      margin-left: $icon-spacing;

      [dir='rtl'] & {
        margin-right: $icon-spacing;
        margin-left: $icon-offset;
      }
    }
  }
}
