@import (reference) "@beanbag/ink/lib/ink.less";
@import (reference) "djblets/css/ui/spinner.less";

@import (reference) "rb/css/ui/colors.less";
@import (reference) "rb/css/ui/menus.less";


#rb-ns-ui() {
  .buttons() {
    @_color-vars: #rb-ns-ui.colors();

    @border-color: @_color-vars[@grey-60];
    @border-radius: 4px;
    @border-size: 1px;
    @border: @border-size @border-color solid;

    @font-size: 13px;

    @padding-horiz: 8px;
    @padding-vert: 3px;

    @transition-time: 0.1s;
    @transition: @transition-time linear all;

    /* Standard button presentation. */
    @bg-gradient-color-start: @_color-vars[@white];
    @bg-gradient-color-end: @_color-vars[@grey-80];
    @text-color: @_color-vars[@grey-10];

    /* Disabled buttons. */
    @disabled-text-color: @_color-vars[@grey-70];

    /* Buttons with the -is-danger modifier. */
    @danger-bg-gradient-color-start: @_color-vars[@red-60];
    @danger-bg-gradient-color-end: @_color-vars[@red-50];
    @danger-text-color: @_color-vars[@white];

    /* Buttons with the -is-primary modifier. */
    @primary-bg-gradient-color-start: @_color-vars[@blue-40];
    @primary-bg-gradient-color-end: @_color-vars[@blue-30];
    @primary-text-color: @_color-vars[@white];

    /**
     * Set values for the border and border radius on one side of a button.
     *
     * This must be applied within a selector for the button.
     *
     * Args:
     *     @side (string):
     *         The side to set. This must be ``top``, ``bottom``, ``left``,
     *         or ``right``.
     *
     *     @border-size (string):
     *         The value for the border size to set.
     *
     *     @border-radius (string):
     *         The value for the border radiuses to set, for the corners
     *         touching the side.
     */
    .set-side-border(@side, @border-size: null, @border-radius: null) {
      & when not (@border-size = null) {
        border-@{side}: @border-size;
      }

      & when not (@border-radius = null) {
        & when (@side = top), (@side = bottom) {
          border-@{side}-left-radius: @border-radius;
          border-@{side}-right-radius: @border-radius;
        }

        & when (@side = left), (@side = right) {
          border-top-@{side}-radius: @border-radius;
          border-bottom-@{side}-radius: @border-radius;
        }
      }
    }

    /**
     * Set a button to only show an icon.
     *
     * This will hide the label and fix padding to appear ideal for showing
     * just an icon.
     *
     * Callers should take care to set appropriate ARIA attributes on the
     * button and to use both ``.rb-c-button__icon`` and
     * ``.rb-c-button__label``.
     *
     * Version Added:
     *     6.0
     */
    .set-icon-only() {
      @_padding-vert: #rb-ns-ui.buttons()[@padding-vert];

      padding: @_padding-vert (@_padding-vert * 2);

      > .rb-c-button__label {
        display: none;
      }
    }

    /**
     * Set a FontAwesome icon for a button.
     *
     * This will style the button icon to render a FontAwesome icon. It
     * requires an existing ``.rb-c-button__icon`` part in the component.
     *
     * Version Added:
     *     6.0
     *
     * Args:
     *     @icon (string):
     *         The FontAwesome icon variable.
     */
    .set-fa-icon(@icon) {
      > .rb-c-button__icon::before {
        .fa-icon();
        content: @icon;
      }
    }
  }

  .button-groups() {
    /**
     * Apply rules for all buttons in a button group.
     *
     * Args:
     *     @rules (ruleset):
     *         The rules to apply.
     */
    .for-buttons(@ruleset) {
      > .btn-segment, /* Deprecated */
      > button,
      > .rb-c-button {
        @ruleset();
      }
    }

    /**
     * Set a side of a button group to be flush with another element.
     *
     * This will turn off the borders on one side and remove the border
     * radiuses that touch that side.
     *
     * This must be applied within a selector for the button group.
     *
     * Args:
     *     @side (string):
     *         The side to make flush. This must be ``top``, ``bottom``,
     *         ``left``, or ``right``.
     */
    .set-flush-side(@side) {
      ._disable-border() {
        #rb-ns-ui.buttons.set-side-border(@side: @side,
                                          @border-radius: 0);
      }

      ._disable-border-for-edge-button(@first-child-side, @last-child-side) {
        & when (@side = @first-child-side) {
          &:first-child {
            ._disable-border();
          }
        }

        & when (@side = @last-child-side) {
          &:last-child {
            ._disable-border();
          }
        }
      }

      #rb-ns-ui.buttons.set-side-border(@side: @side,
                                        @border-radius: 0);

      #rb-ns-ui.button-groups.for-buttons({
        & when (@side = top), (@side = bottom) {
          ._disable-border()
        }

        & when (@side = left), (@side = right) {
          ._disable-border-for-edge-button(left, right)
        }
      });

      &.-is-vertical {
        #rb-ns-ui.button-groups.for-buttons({
          & when (@side = top), (@side = bottom) {
            @_button-vars: #rb-ns-ui.buttons();

            #rb-ns-ui.buttons.set-side-border(
              @side: @side,
              @border-radius: inherit);
            ._disable-border-for-edge-button(top, bottom);
          }

          & when (@side = left), (@side = right) {
            ._disable-border();
          }
        });
      }
    }
  }

  .menu-buttons() {
    @transition-time: 0.25s;
    @transition: @transition-time linear border;
  }
}


/**
 * A button.
 *
 * Buttons are clickable and perform an action when clicked. They generally
 * contain text or other markup (like an embedded icon).
 *
 * This component is automatically used for ``<button>``,
 * ``<input type="submit">``, and ``<input type="button">`` elements. It
 * can be used explicitly for other elements (such as an ``<a>``), in which
 * case ``aria-role="button"`` should also be set.
 *
 * Buttons may contain a ``<span class="djblets-o-spinner"></span>`` to show
 * a spinner. This will be themed properly for the state of the button.
 *
 * They may also contain ``.rb-c-button__icon`` and ``.rb-c-button__label``
 * children to style an icon and label. These will be spaced appropriately.
 *
 * Deprecated:
 *     7.0:
 *     Use ``.ink-c-button`` instead.
 *
 * Modifiers:
 *     -is-danger:
 *         Indicates that the button performs an operation that might result
 *         in data loss (such as discarding a form or deleting data).
 *
 *     -is-disabled:
 *         Indicates that the button is disabled. This is only needed when
 *         applying the ``rb-c-button`` class to something other than a
 *         ``<button>`` or ``<input>``, which have their own ``disabled``
 *         DOM attribute.
 *
 *     -is-primary:
 *         Indicates that the button is the primary button on a form. This
 *         generally implies that the button is the default.
 *
 * DOM Attributes:
 *     aria-role (optional):
 *         Should be set to "button" if using an element other than
 *         ``<button>`` or ``<input>``.
 *
 *     data-action (optional):
 *         An action the button should perform.
 *
 *         This may be used by implementations to differentiate buttons, or to
 *         handle styling per-button (using ``&[data-action="..."]``).
 *
 *         Version Added:
 *             6.0
 *
 *     disabled (optional):
 *         Disables the button. This is used instead of the ``-is-disabled``
 *         modifier when the element is a ``<button>`` or ``<input>``.
 *
 *     role (optional):
 *         This should always be set to "button" when on an element other
 *         than ``<button>`` or ``<input>``. The exception is when using
 *         the ``-is-segmented`` modifier.
 *
 *     type (optional):
 *         Set to "button" if this is a ``<button>`` inside of a form, and
 *         you don't want it to submit the form.
 *
 * Structure:
 *     <button class="[modifiers]">...</button>
 *
 *     <button class="rb-c-button [modifiers]">...</button>
 *
 *     <button class="rb-c-button [modifiers]">
 *      <span class="rb-c-button__icon"></span>
 *      <label class="rb-c-button__label">...</label>
 *     </button>
 *
 *     <input type="submit" class="rb-c-button [modifiers]">
 *
 *     <input type="submit" class="[modifiers]">
 *
 *     <input type="button" class="rb-c-button [modifiers]">
 *
 *     <input type="button" class="[modifiers]">
 *
 *     <a class="rb-c-button [modifiers]" role="button" href="...">...</a>
 *
 *     <a class="rb-c-button [modifiers]" role="button" href="...">
 *      <span class="rb-c-button__icon"></span>
 *      <label class="rb-c-button__label">...</label>
 *     </a>
 */

.rb-c-button {
  @_button-vars: #rb-ns-ui.buttons();

  #ink-ns-ui.deprecated();

  border: @_button-vars[@border];
  border-radius: @_button-vars[@border-radius];
  color: @_button-vars[@text-color];
  cursor: pointer;
  display: inline-flex;
  gap: 0.5em;
  align-items: center;
  justify-content: center;
  font-family: var(--ink-ff-default);
  font-weight: normal;
  font-size: @_button-vars[@font-size];
  line-height: normal;
  padding: @_button-vars[@padding-vert] @_button-vars[@padding-horiz];
  text-decoration: none;
  transition: @_button-vars[@transition];

  #gradient.vertical-three-colors(
    @_button-vars[@bg-gradient-color-start],
    @_button-vars[@bg-gradient-color-start],
    25%,
    @_button-vars[@bg-gradient-color-end]);

  &:hover,
  &.js-hover {
    background-position: 0 -15px;
    color: @_button-vars[@text-color];
    text-decoration: none;
  }

  &:link,
  &:visited {
    color: @_button-vars[@text-color];
  }

  &[disabled],
  &.-is-disabled {
    color: @_button-vars[@disabled-text-color];
    cursor: default;
    background-image: none;
    .ie-reset-gradients();
  }

  &.-is-danger {
    color: @_button-vars[@danger-text-color] !important;
    .gradientBar(
      @_button-vars[@danger-bg-gradient-color-start],
      @_button-vars[@danger-bg-gradient-color-end]);
  }

  &.-is-primary {
    color: @_button-vars[@primary-text-color];
    .gradientBar(
      @_button-vars[@primary-bg-gradient-color-start],
      @_button-vars[@primary-bg-gradient-color-end]);
  }

  &.-is-danger,
  &.-is-primary {
    .djblets-o-spinner {
      #djblets-ns-ui.spinners.set-theme(light);
    }
  }

  > * {
    cursor: inherit;
  }
}


/*
 * NOTE: The following are provided separately due to the use of &:extend()
 *       below, which would cause these definitions to be lost if part of
 *       ``.rb-c-button`` above.
 */

/**
 * The icon for a button.
 *
 * Consumers are responsible for styling the icon, using Review Board
 * icons, FontAwesome, or some other mechanism. This can be done by
 * attaching additional CSS classes or styling in CSS.
 *
 * If including any text-based content for the icon, consider using
 * ``aria-hidden="true"``.
 *
 * Version Added:
 *     6.0
 *
 * DOM Attributes:
 *     aria-hidden (boolean):
 *         Should always be set to ``true``.
 *
 * Structure:
 *     <span class="rb-c-button__icon [...]"
 *           aria-hidden="true">
 *      [...]
 *     </span>
 */
.rb-c-button__icon {
  min-width: 1em;
}


/**
 * The label for a button.
 *
 * This should be used when the button also contains an icon, to properly
 * handle styling of both the icon and label.
 *
 * Version Added:
 *     6.0
 *
 * Structure:
 *     <label class="rb-c-button__label">text...</label>
 */
.rb-c-button__label {
}


/*
 * Give all standard buttons the .rb-c-button component by default.
 */
button,
input[type="button"],
input[type="submit"] {
  &:not(.ink-c-button) {
    &:extend(.rb-c-button all);
  }
}


/**
 * A group of buttons arranged compactly in a row.
 *
 * Button groups have the appearance of one large button split into
 * multiple clickable areas. They're useful for things like buttons with
 * dropdowns and toolbars.
 *
 * Children must be ``.rb-c-button`` components or ``<button>`` elements.
 *
 * Deprecated:
 *     7.0:
 *     Use ``.ink-c-button-group`` instead.
 *
 * Modifiers:
 *     -is-vertical:
 *         Lays out the buttons vertically instead of horizontally.
 *
 * DOM Attributes:
 *     aria-label (required):
 *         A descriptive label for screen readers (e.g., "Text formatting").
 *
 *     role (required):
 *         This should be set to "group" in most cases. If this button
 *         group is simulating a toolbar, set this to "toolbar".
 *
 * Structure:
 *     <div class="rb-c-button-group [modifiers]" role="..."
 *          aria-label="...">
 *      <button class="rb-c-button">...</button>
 *      ...
 *     </div>
 */
.rb-c-button-group {
  @_button-vars: #rb-ns-ui.buttons();

  #ink-ns-ui.deprecated();

  border: @_button-vars[@border];
  border-radius: @_button-vars[@border-radius];
  display: inline-flex;

  /* Make sure there's no space between buttons. */
  font-size: 0;

  &:not(.-is-vertical) {
    /* Default horizontal layouts. */
    #rb-ns-ui.button-groups.for-buttons({
      /*
       * Set these based on a default horizontal orientation. We'll round out
       * the corners of the first and last buttons, and add borders in-between
       * each.
       */
      border-top: 0;
      border-right: 0;
      border-bottom: 0;
      border-left: inherit;
      border-radius: 0;

      &:first-child {
        border-left: 0;
        border-top-left-radius: inherit;
        border-bottom-left-radius: inherit;
      }

      &:last-child {
        border-top-right-radius: inherit;
        border-bottom-right-radius: inherit;
      }
    });
  }

  &.-is-vertical {
    #rb-ns-ui.button-groups.for-buttons({
      border-top: inherit;
      border-left: 0;
      border-right: 0;
      border-bottom: 0;
      border-radius: 0;
      display: block;
      text-align: left;
      width: 100%;

      &:first-child {
        border-top: 0;
        border-top-left-radius: inherit;
        border-top-right-radius: inherit;
      }

      &:last-child {
        border-bottom-right-radius: inherit;
        border-bottom-left-radius: inherit;
      }
    });
  }

}


/**
 * A button that displays a drop-down menu.
 *
 * Menu buttons consist of either a single button or a button group that,
 * when clicked, displays a drop-down (or drop-up) menu.
 *
 * The menu can optionally be a list of buttons, when mixing in a
 * ``.rb-c-button-group`` into the ``.rb-c-menu`` element and ``.rb-c-button``
 * into the ``.rb-c-menu__item`` elements.
 *
 * Deprecated:
 *     7.0:
 *     Use ``.ink-c-menu-button`` instead.
 *
 * DOM Attributes:
 *     role (required):
 *         This should be set to "group".
 *
 * Structure:
 *     <div class="rb-c-menu-button" role="group">
 *      <button class="rb-c-button rb-c-menu-button__toggle"
 *              id="<label ID>"
 *              aria-controls="<menu ID>"
 *              aria-haspopup="true"
 *              aria-label="...">
 *       ...
 *      </button>
 *      <div class="rb-c-menu" role="menu" tabindex="-1" id="<menu ID>"
 *           aria-labelledby="<label ID>">
 *       ...
 *      </div>
 *     </div>
 *
 *     <div class="rb-c-menu-button" role="group">
 *      <div class="rb-c-button-group" role="group">
 *       <button class="rb-c-button rb-c-menu-button__primary">...</button>
 *       <button class="rb-c-button rb-c-menu-button__toggle"
 *               id="<label ID>"
 *               aria-controls="<menu ID>"
 *               aria-haspopup="true"
 *               aria-label="...">
 *        ...
 *       </button>
 *      </div>
 *      <div class="rb-c-menu" role="menu" tabindex="-1" id="<menu ID>"
 *           aria-labelledby="<label ID>">
 *       ...
 *      </ul>
 *     </div>
 */
.rb-c-menu-button {
  @_button-vars: #rb-ns-ui.buttons();
  @_menu-button-vars: #rb-ns-ui.menu-buttons();
  @_border-radius: @_button-vars[@border-radius];

  #ink-ns-ui.deprecated();

  border-radius: @_border-radius @_border-radius 0 0;
  display: inline-block;
  position: relative;
  transition: @_menu-button-vars[@transition];

  &:not(.-opens-up) {
    /* Default drop-down styling. */
    > .rb-c-menu {
      #rb-ns-ui.button-groups.set-flush-side(top);
    }
  }

  &.-opens-up {
    > .rb-c-menu {
      #rb-ns-ui.button-groups.set-flush-side(bottom);
    }
  }

  &.js-open {
    &:not(.-opens-up) {
      /* Default drop-down styling. */
      #rb-ns-ui.button-groups.for-buttons({
        #rb-ns-ui.buttons.set-side-border(@side: bottom,
                                          @border-size: 0,
                                          @border-radius: 0);
      });

      > .rb-c-button-group:not(.rb-c-menu) {
        #rb-ns-ui.button-groups.set-flush-side(bottom);
      }
    }

    &.-opens-up {
      #rb-ns-ui.button-groups.for-buttons({
        #rb-ns-ui.buttons.set-side-border(@side: top,
                                          @border-size: 0,
                                          @border-radius: 0);
      });

      > .rb-c-button-group:not(.rb-c-menu) {
        #rb-ns-ui.button-groups.set-flush-side(top);
      }
    }
  };

  > .rb-c-button,
  > .rb-c-button-group .rb-c-button {
    border-bottom: transparent;
    margin: 0;
  }

  > .rb-c-menu {
    position: absolute;
    white-space: nowrap;

    &.-is-open {
      visibility: visible;
    }
  }

  /**
   * A primary action button, for split menu buttons.
   *
   * Structure:
   *     <button class="rb-c-menu-button__primary rb-c-button"
   *             type="button">
   *      text or icon...
   *     </button>
   */
  &__primary {
  }

  /**
   * The button that toggles the display of a menu.
   *
   * Structure:
   *     <button class="rb-c-menu-button__toggle rb-c-button"
   *             id="<label id>" aria-haspopup="true" type="button">
   *      [text...]
   *      <span class="rb-icon rb-icon-dropdown-arrow"></span>
   *     </button>
   */
  &__toggle {
  }
}


/**
 * Deprecated styling variables for buttons.
 *
 * Deprecated:
 *     4.0:
 *     These have been replaced by definitions in ``#rb-ns-ui.buttons``.
 */
@btn-bg-color1: #rb-ns-ui.buttons[@bg-gradient-color-start];
@btn-bg-color2: #rb-ns-ui.buttons[@bg-gradient-color-end];
@btn-border-bottom-color: #rb-ns-ui.buttons[@border-color];
@btn-border-color: #rb-ns-ui.buttons[@border-color];
@btn-border-radius: #rb-ns-ui.buttons[@border-radius];
@btn-border-size:  #rb-ns-ui.buttons[@border-size];
@btn-box-shadow: none;
@btn-font-size: #rb-ns-ui.buttons[@font-size];
@btn-font-weight: normal;
@btn-padding: #rb-ns-ui.buttons[@padding-vert]
              #rb-ns-ui.buttons[@padding-horiz];
@btn-text-color: #rb-ns-ui.buttons[@text-color];
@btn-text-shadow: none;
@btn-transition: #rb-ns-ui.buttons[@transition];
@btn-active-box-shadow: none;
@btn-disabled-box-shadow: none;
@btn-disabled-opacity: 0.65;
@btn-danger-text-color: #rb-ns-ui.buttons[@danger-text-color];
@btn-danger-bg-color1: #rb-ns-ui.buttons[@danger-bg-gradient-color-start];
@btn-danger-bg-color2: #rb-ns-ui.buttons[@danger-bg-gradient-color-end];
@btn-large-border-radius: 6px;
@btn-large-font-size: 130%;
@btn-large-padding: 9px 14px 9px;
@btn-small-font-size: 11px;
@btn-small-padding: 7px 9px 7px;
@btn-primary-bg-color1: #rb-ns-ui.buttons[@primary-bg-gradient-color-start];
@btn-primary-bg-color2: #rb-ns-ui.buttons[@primary-bg-gradient-color-end];
@btn-primary-color1: @btn-primary-bg-color1;
@btn-primary-color2: @btn-primary-bg-color2;
@btn-primary-text-color: #rb-ns-ui.buttons[@primary-text-color];
@btn-primary-text-shadow: none;
@split-btn-hover-transition-time: 0.25s;


/**
 * Old style for buttons.
 *
 * Deprecated:
 *     4.0:
 *     This has been replaced with ``.rb-c-button``, and will be removed
 *     in a future release.
 */
.btn {
  &:extend(.rb-c-button);
}


/**
 * Old style for segmented button groups.
 *
 * Deprecated:
 *     4.0:
 *     This has been replaced with ``.rb-c-button-group``, and will be
 *     removed in a future release.
 */
.btn-segmented {
  &:extend(.rb-c-button-group);
  &:extend(.rb-c-button-group:not(.-is-vertical));

  /* Override .btn styles. */
  background: none;
  padding: 0;
}


/**
 * Old style for button segments.
 *
 * Deprecated:
 *     4.0:
 *     This has been replaced with ``.rb-c-button`` nested inside of a
 *     ``.rb-c-button-group``, and will be removed in a future release.
 */
.btn-segment {
  &:extend(.rb-c-button);
  &:extend(.rb-c-button-group:not(.-is-vertical) > .rb-c-button all);
}


.split-btn {
  &:extend(.rb-c-menu-button);

  &.drop-down-shown,
  &.drop-up-shown {
    .split-btn-alternatives {
      &:extend(.rb-c-menu-button.js-open > .rb-c-menu);
    }
  }

  &.drop-down-shown {
    .btn-segmented {
      &:extend(.rb-c-menu-button.js-open:not(.-opens-up)
               > .rb-c-button-group all);
    }

    .split-btn-alternatives {
      &:extend(.rb-c-menu-button.js-open:not(.-opens-up) > .rb-c-menu all);
    }
  }

  &.drop-up-shown {
    .btn-segmented {
      &:extend(.rb-c-menu-button.js-open.-opens-up > .rb-c-button-group all);
    }

    .split-btn-alternatives {
      &:extend(.rb-c-menu-button.js-open.-opens-up > .rb-c-menu all);
    }
  }

  .split-btn-alternatives {
    &:extend(.btn-segmented all);
    .rb-c-menu();
    &:extend(.rb-c-menu-button > .rb-c-menu);

    .btn {
      &:extend(.rb-c-button-group.-is-vertical > .rb-c-button all);
    }
  }
}


button:not(.ink-c-button),
input[type="submit"]:not(.ink-c-button),
.btn,
.btn-segment {
  &:hover,
  .hover {
    &:extend(.rb-c-button:hover);
  }

  &:link {
    &:extend(.rb-c-button:link);
  }

  &:visited {
    &:extend(.rb-c-button:visited);
  }

  &.danger {
    &:extend(.rb-c-button.-is-danger);
  }

  &.disabled {
    &:extend(.rb-c-button.-is-disabled);

    .btn-segment {
      &:extend(.rb-c-button.disabled .rb-c-button__segment);
    }
  }

  &.default,
  &.primary {
    &:extend(.rb-c-button.-is-primary);

    .btn-segment {
      &:extend(.rb-c-button.primary .rb-c-button__segment);
    }
  }

  &.large {
    font-size: @btn-large-font-size;
    line-height: normal;
    padding: @btn-large-padding;
    border-radius: @btn-large-border-radius;
  }

  &.small {
    padding: @btn-small-padding;
    font-size: @btn-small-font-size;
  }
}
