@use '../compiled/tokens/scss/brightness';
@use '../compiled/tokens/scss/color';
@use '../compiled/tokens/scss/ease';
@use '../compiled/tokens/scss/font-weight';
@use '../compiled/tokens/scss/scale';
@use '../compiled/tokens/scss/size';
@use '../compiled/tokens/scss/transition';
@use './focus';
@use './ms';
@use './unit';
@use './theme';

/**
 * Themed properties
 */

@include theme.props {
  --theme-color-background-button-default: #{color.$brand-primary};
  --theme-color-background-button-primary: #{color.$base-purple};
  --theme-color-background-button-secondary: var(--theme-color-background-base);
  --theme-color-border-button-default: #{color.$brand-primary-dark};
  --theme-color-border-button-primary: #{color.$base-purple-dark};
  --theme-color-border-button-secondary: #{color.$brand-primary-light};
  --theme-color-text-button-default: #{color.$text-light-emphasis};
  --theme-color-text-button-primary: #{color.$text-light-emphasis};
  --theme-color-text-button-secondary: var(--theme-color-text-action);
  --theme-color-text-button-tertiary: var(--theme-color-text-action);
}

@include theme.props(dark) {
  --theme-color-background-button-default: #{color.$brand-primary-dark};
  --theme-color-background-button-primary: #{color.$base-white};
  --theme-color-background-button-secondary: var(--theme-color-background-base);
  --theme-color-border-button-default: #{color.$brand-primary-darker};
  --theme-color-border-button-primary: #{color.$brand-primary-darker};
  --theme-color-border-button-secondary: #{color.$brand-primary-darker};
  --theme-color-text-button-default: #{color.$text-light-emphasis};
  --theme-color-text-button-primary: #{color.$brand-primary};
  --theme-color-text-button-secondary: var(--theme-color-text-action);
  --theme-color-text-button-tertiary: var(--theme-color-text-action);
}

/**
 * Button object
 *
 * 1. We set a height to coordinate consistently with `c-input`. Unfortunately
 *    browsers don't consistently size inputs without setting `height`.
 * 2. More overtly actionable cursor appearance. Recently this tweak has come
 *    with some contention, but our experience has been that the average user
 *    doesn't differentiate cursor styles between different styles of action.
 * 3. Although we will be overtly setting some typographic properties, the
 *    shorthand is helpful for completely overriding all system font styles as
 *    a starting point.
 */

@mixin default {
  --icon-size: #{size.$icon-medium};
  align-items: center;
  background: color.$brand-primary;
  background: var(--theme-color-background-button-default);
  block-size: size.$height-control-default; /* 1 */
  border-color: color.$brand-primary-dark;
  border-color: var(--theme-color-border-button-default);
  border-radius: size.$border-radius-medium;
  border-style: solid;
  border-width: size.$edge-control;
  color: color.$text-light-emphasis;
  color: var(--theme-color-text-button-default);
  cursor: pointer; /* 2 */
  display: inline-flex;
  font: inherit; /* 3 */
  font-style: normal;
  font-weight: font-weight.$semibold;
  justify-content: center;
  line-height: normal;
  padding: size.$padding-control-vertical size.$padding-control-horizontal;
  position: relative;
  text-align: center;
  text-decoration: none;
  transition: filter transition.$slow ease.$out,
    transform transition.$quick ease.$out;
  user-select: none;
  vertical-align: middle;
  white-space: nowrap;

  @include focus.focus-visible {
    box-shadow: 0 0 0 size.$edge-large color.$brand-primary-lighter;
  }

  /**
   * Transform, opacity and filter changes are performant and will work across
   * many more stylistic modifiers than specific color shifts.
   *
   * We optionally support custom properties to allow the amount of scaling to
   * be adjusted by other patterns. For example, an Input Group might disable
   * the scaling so the button always appeared joined to adjacent elements.
   */

  &:hover {
    filter: brightness(brightness.$control-brighten);
    transform: scale(var(--scale-effect-grow, #{scale.$effect-grow}));
  }

  &:active {
    filter: brightness(brightness.$control-dim);
    transform: scale(var(--scale-effect-shrink, #{scale.$effect-shrink}));
  }

  &:disabled,
  &[aria-disabled='true'] {
    cursor: not-allowed;
    filter: grayscale(60%);
    opacity: 0.85;
    transform: none;
  }

  /**
   * Loading state
   */

  &.is-loading {
    /**
     * Display a spinner. Static if the user prefers reduced motion, animated
     * otherwise.
     */

    &::after {
      animation: transition.$glacial rotate-clockwise linear infinite;
      block-size: size.$icon-medium;
      border: size.$edge-control solid currentColor;
      border-block-start-color: transparent;
      border-radius: size.$border-radius-full;
      content: '';
      inline-size: size.$icon-medium;
      inset: 0;
      margin: auto;
      position: absolute;

      @media (prefers-reduced-motion: reduce) {
        animation: none;
        border-block-start-color: currentColor;
        border-style: dashed;
      }
    }

    /**
     * Hide child elements (content, extra). We use this technique because it is
     * performant and will not negate any keyboard events.
     *
     * @link https: //stackoverflow.com/a/34529598
     */

    > * {
      opacity: 0;
    }
  }
}

/**
 * Modifier: Primary
 */

@mixin primary {
  background-color: var(--theme-color-background-button-primary);
  border-color: var(--theme-color-border-button-primary);
  color: var(--theme-color-text-button-primary);
}

/**
 * Modifier: Secondary
 */

@mixin secondary {
  background-color: var(--theme-color-background-button-secondary);
  border-color: var(--theme-color-border-button-secondary);
  color: var(--theme-color-text-button-secondary);
}

/**
 * Modifier: Tertiary
 */

@mixin tertiary {
  background-color: transparent;
  border-color: transparent;
  color: var(--theme-color-text-button-tertiary);
}

/// Rules for content element, used to contain button label.
@mixin content {
  /// We use opacity because it will not paint but it also won't remove the
  /// element's keyboard events (if any).
  /// @link https://stackoverflow.com/a/34529598
  .is-loading & {
    opacity: 0;
  }
}

/// Rules for extra element. Used to contain icons or other visual affordances.
@mixin extra {
  display: flex;
  margin: 0 size.$spacing-gap-button-extra;
  position: relative;

  &:first-child {
    margin-inline-start: size.$spacing-gap-button-extra * -1;
  }

  &:last-child {
    margin-inline-end: size.$spacing-gap-button-extra * -1;
  }
}

/// Clip path used for extra element when button's slashed state is active
$extra-slashed-clip-path: polygon(
  -5% -5%,
  105% -5%,
  105% 23%,
  -5% 87%,
  -5% 101.4711%,
  105% 37.9626%,
  105% 105%,
  -5% 105%
);

// Styles for extra specific to slashed button state
@mixin extra-slashed {
  &::after {
    background: currentColor;
    block-size: size.$edge-medium;
    border-radius: size.$border-radius-full;
    content: '';
    inline-size: 100%;
    inset-block-start: 50%;
    inset-inline-start: 0;
    position: absolute;
    transform: rotate(-30deg);
  }

  // 1. Fixes a bug where some browsers would render the element as
  //    slightly offset from others.
  // 2. Helps normalize the box the clipping path renders to.
  > * {
    clip-path: $extra-slashed-clip-path;
    transform: translate(0, 0); // 1

    @supports (clip-path: view-box) {
      clip-path: view-box $extra-slashed-clip-path; // 2
    }
  }
}
