////
/// @group button
////

/// This is the core button module. It handles the styling, sizes and other styling attributes of buttons. You can include the "component" button to print the button styles/sizes or you can use the mixins from the core module.

@use "sass:map";
@use "sass:meta";
@use "sass:string";

@use "color";
@use "element";
@use "typography";
@use "utils";

// Used for function fallback
$-fallbacks: (
  "box-shadow": meta.get-function("get", false, "element")
);

/// Module Settings
/// @type Map
/// @prop {String} active-selector ["&.is-active"] The selector that determines if a button is active
/// @prop {CssValue} box-shadow [true] Specify box shadow for default button. Default true will fallback to element "box-shadow"
/// @prop {Number} line-height [1.1] Line height for button
/// @prop {Dimension} letter-spacing [0.02em]
/// @prop {Dimension} margin [(0.45em 0.5em 0.45em 0)] Margin for buttons, usually left margin is omitted so buttons can flow inline and make space between them and the next element inline
/// @prop {Dimension} min-width [9rem] The smallest width for all buttons
/// @prop {Dimension} padding [(0.75em 1.5em)] Button inner padding value, pass space separated list
/// @prop {CssValue} white-space [nowrap] Adjust button line wrapping, by default line's are not wrapped
/// @prop {Dimension} gap [0.5em] Gap to use between items within the button
/// @prop {String} border-color ["control-border"] The border color for the button, usually if there is no border we set this to match the background color so if a button with no borders is adjacent a style that has borders the heights will match.
/// @prop {String} border-color-hover ["control-border-hover"] Color of border when button is hovered
/// @prop {String} border-color-active ["control-border-active"] Color of border when a button has active class
/// @prop {Dimension} border-radius [2rem] Border Radius for button
/// @prop {Dimension} border-width [1px] Width of button border
/// @prop {String} background-color ["control-background"] Background color of button
/// @prop {String} background-color-hover ["control-background-hover"] Background color of button when hovered
/// @prop {String} background-color-active ["control-background-active"] Background color of button when active
/// @prop {String} color ["control"] Text color of button
/// @prop {String} color-hover ["control-hover"] Text color of button when hovered
/// @prop {String} color-active ["control-active"] Text color of button when active
/// @prop {CssValue} font-family [inherit] Font family for button
/// @prop {CssValue} font-weight [bold] Font weight for button
/// @prop {String} font-size ["base"] Font size for button, can be omitted if it should inherit, sizes can also work with utility type size classes
/// @prop {Dimension} icon-size [2.5rem] The size of a button when used with an icon
/// @prop {Dimension} icon-font-size [1.38rem] The font size for the icon when a button is an icon button
/// @prop {Dimension} icon-border-radius [50%] The border radius of a icon button (defaults to 50% circle)
/// @prop {CssValue} text-shadow [none] Text shadow for button
/// @prop {CssValue} text-transform [none] Text transform for button
/// @prop {CssValue} text-decoration [none] Text decoration of button
/// @prop {Boolean} transition-enabled [true] Whether or not to enable transitions on button properties like background-color, color, border color as they change state
/// @prop {Time} transition-duration [200ms] The duration of the button's transition if enabled
/// @prop {List} transition-properties [(border-color, background-color, color, box-shadow)] The properties to transition if `transition-enabled` 

$config: (
  "active-selector":         "&.is-active",
  "box-shadow":              true,
  "line-height":             1.1,
  "letter-spacing":          0.02em,
  "margin":                  (0.45em 0.5em 0.45em 0),
  "min-width":               9rem,
  "padding":                 (0.75em 1.5em),
  "white-space":             nowrap,
  "border-color":            "control-border",
  "border-color-active":     "control-border-active",
  "border-color-hover":      "control-border-hover",
  "border-radius":           2rem,
  "border-width":            1px,
  "background-color":        "control-background",
  "background-color-hover":  "control-background-hover",
  "background-color-active": "control-background-active",
  "color":                   "control",
  "color-hover":             "control-hover",
  "color-active":            "control-active",
  "font-family":             inherit,
  "font-weight":             bold,
  "font-size":               "base",
  "icon-size":               2.5rem,
  "icon-font-size":          1.38rem,
  "icon-border-radius":      50%,
  "text-shadow":             none,
  "text-transform":          none,
  "text-decoration":         none,
  "transition-enabled":      true,
  "transition-duration":     200ms,
  "transition-properties":   (border-color, background-color, color, box-shadow),
  "gap":                     0.5em,
) !default;
  
/// Button sizes
/// - A map that holds to the sizes for buttons in the theme
/// - Use set-sizes() to adjust the sizes for the theme
/// - Don't edit sizes variable directly
/// @type Map
  
$sizes: (
  "small" : (
    "padding":        (0.35em 1em),
    "min-width":      0,
    "icon-size":      2rem,
    "icon-font-size": 1rem
  ),
  "large" : (
    "padding":   (1em 2em),
    "min-width": 11rem,
    "icon-size": 3.5rem
  )
) !default;

/// Button styles
/// - A map of styles for each button in the theme. Us set-styles() to overwrite or merge into these styles
/// - Don't edit styles variable directly
/// @type Map

$styles: (
  "transparent" : (
    "background-color" : transparent,
    "color" : "type",
    "border-color" : transparent,
    "box-shadow" : none,
    "hover" : (
      "background-color" : "control-background",
      "color" : "control",
      "border-color" : "control-border",
    )
  ),
  "outline" : (
    "background-color" : transparent,
    "color" : "type",
    "border-color" : "rule-light",
    "box-shadow" : none,
    "hover" : (
      "background-color" : "control-background",
      "color" : "control",
      "border-color" : "control-border",
    )
  ),
) !default;

/// Change modules $config
/// @param {Map} $changes Map of changes
/// @example scss - General example
///   @include ulu.button-set(( "property" : value ));

@mixin set($changes) {
  $config: map.merge($config, $changes) !global;
}

/// Get a config option
/// @param {Map} $name Name of property
/// @example scss - General example
///   $shadow: ulu.button-get("box-shadow");
/// @return {*} Resolved value 

@function get($name) {
  $value: utils.require-map-get($config, $name, "button [config]");
  @return utils.function-fallback($name, $value, $-fallbacks);
}

/// Set button styles 
/// - See $styles for example of structure of map
/// @param {Map} $changes Map of changes
/// @param {String} $merge-mode Merge mode see utils.map-merge() [null|"deep"|"overwrite"]

@mixin set-styles($changes, $merge-mode: null) {
  $styles: utils.map-merge($styles, $changes, $merge-mode) !global;
}

/// Set Button Sizes
/// - See $sizes for example of structure of map
/// @param {Map} $changes Map of changes
/// @param {String} $merge-mode Merge mode see utils.map-merge() [null|"deep"|"overwrite"]

@mixin set-sizes($changes, $merge-mode: null) {
  $sizes: utils.map-merge($sizes, $changes, $merge-mode) !global;
}

/// Reset CSS for button (to change browser defaults for button styling)

@mixin reset() {
  text-transform: none;
  text-align: inherit;
  background-color: transparent;
  border: transparent;
  border-radius: 0;
  margin: 0;
  padding: 0;
  appearance: none; 
  color: inherit; 
  cursor: pointer;
}

/// Output the default button styles
/// @param {Boolean} $with-reset [false] Include button.reset()

@mixin default($with-reset: false) {
  $font-size: get("font-size");
  @if ($with-reset) {
    @include reset();
  }
  appearance: none; // If used with select for example
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: get("gap");
  text-transform: get("text-transform");
  text-shadow: get("text-shadow");
  vertical-align: middle;
  font-family: get("font-family");
  font-weight: get("font-weight");
  @if ($font-size) {
    @include typography.size($font-size, null, true);
  }
  border-radius: get("border-radius");
  // Removed not sure why it was needed, effects buttons with 
  // borders (rendering between bg and border)
  // background-clip: padding-box; 
  padding: get("padding");
  line-height: get("line-height");
  letter-spacing: get("letter-spacing");
  box-shadow: get("box-shadow");
  margin: get("margin");
  min-width: get("min-width");
  white-space: get("white-space");
  max-width: 100%; // Questionable, not sure if it's needed
  flex-shrink: 0; 
  // Should override link visited coloring
  
  @if get("transition-enabled") {
    transition-duration: get("transition-duration");
    transition-property: get("transition-properties");
  }
  &,
  &:visited {
    color: color.get(get("color"));
    border: get("border-width") solid color.get(get("border-color"));
    background-color: color.get(get("background-color"));
    text-decoration: get("text-decoration");
  }
  &:last-child {
    margin-right: 0;
  }
  &:hover,
  &:focus {
    color: color.get(get("color-hover"));
    background-color: color.get(get("background-color-hover"));
    border-color: color.get(get("border-color-hover"));
    text-decoration: get("text-decoration");
  }
  @include when-active() {
    color: color.get(get("color-active"));
    background-color: color.get(get("background-color-active"));
    border-color: color.get(get("border-color-active"));
  }
}

/// Mixin to wrap in active selectors
/// - Use to match the button's active selector
/// @example scss
///   // Site specific styling for active button
///   .button {
///     @include when-active() {
///       background: linear-gradient(0.25turn, #3f87a6, #ebf8e1, #f69d3c);
///     }
///   }

@mixin when-active() {
  #{ get("active-selector") } {
    @content;
  }
}

/// Print button size styles for a specific size
/// @param {String} $name Name of size from $sizes
/// @see $sizes
/// @see set-sizes

@mixin size($name) {
  $size: utils.require-map-get($sizes, $name, 'button [size]');
  $font-size: map.get($size, "font-size");
  padding: map.get($size, "padding");
  border-radius: map.get($size, "border-radius");
  border-width: map.get($size, "border-width");
  min-width: map.get($size, "min-width");
  @if $font-size {
    @include typography.size($font-size);
  }
}

/// Get a value from a button style
/// @param {String} $name Name of style from $styles
/// @see {variable} $styles
/// @see set-styles
/// @return {*} The property you were trying to get

@function get-style-value($name, $prop, $state: null) {
  $style: utils.require-map-get($styles, $name, 'button [color value]');
  $state-style: ();
  // If a specific state [hover, active] grab that map
  @if ($state) {
    $state-style: map.get($style, $state);
    $state-style: utils.when($state-style, $state-style, ());
  }
  // From is the map to grab styles from
  $from: utils.when($state, $state-style, $style);
  $value: map.get($from, $prop);

  // Fallback to parent (if hover)
  @if ($state == "hover") {
    $value: utils.when($value, $value, map.get($style, $prop)); 
  }

  @if (meta.type-of($value) == "string" and string.index($prop, "color")) {
    @return color.get($value);
  } @else {
    @return $value;
  }
}

/// Print a button style's base styles (not hover)
/// - In most cases you want the style() mixin
/// - This is used mainly for trying to match a buttons base styles without including the other state (hover, etc) styles
/// @param {String} $name Name of style from $styles
/// @see {variable} $styles
/// @see set-styles

@mixin style-styles($name) {
  background-color: get-style-value($name, "background-color");
  color: get-style-value($name, "color");
  border-color: get-style-value($name, "border-color");
  border-width: get-style-value($name, "border-width");
  box-shadow: get-style-value($name, "box-shadow");
  text-decoration: get-style-value($name, "text-decoration");
  font-weight: get-style-value($name, "font-weight");
  @if get-style-value($name, "transition-duration") {
    transition-duration: get-style-value($name, "transition-duration");
  }
}

/// Print a button style's base styles (hover styles only)
/// - In most cases you want the style() mixin
/// - This is used mainly for trying to match a buttons hover styles without including the base styling
/// @param {String} $name Name of style from $styles
/// @see {variable} $styles
/// @see set-styles

@mixin style-styles-hover($name) {
  background-color: get-style-value($name, "background-color", "hover");
  color: get-style-value($name, "color", "hover");
  border-color: get-style-value($name, "border-color", "hover");
  box-shadow: get-style-value($name, "box-shadow", "hover");
  text-decoration: get-style-value($name, "text-decoration", "hover");
}

@mixin style-styles-active($name) {
  @include when-active() {
    background-color: get-style-value($name, "background-color", "active");
    color: get-style-value($name, "color", "active");
    border-color: get-style-value($name, "border-color", "active");
  }
}

/// Print a button style
/// - Includes base/visited styling, and hover/focus state styles
/// - To print only one of those states, use style-styles or style-styles-hover
/// - By default this mixin prints the buttons base styles along with :visited state. 
///   This is to avoid link visited states effecting the button styles (if used in editor areas 
///   or other areas that apply automatic links styling for example. (param below to override behavior)
/// @param {String} $name Name of style from $styles
/// @param {String} $no-visited [false] Do not include :visited selector for button base styles
/// @see {variable} $styles
/// @see set-styles

@mixin style($name, $no-visited: false) {
  @if ($no-visited) {
    @include style-styles($name);
  } @else {
    &,
    &:visited {
      @include style-styles($name);
    }
  }
  &:hover,
  &:focus {
    @include style-styles-hover($name);
  }
  @include when-active() {
    @include style-styles-active($name);
  }
}