///|
pub fn toggle_controlled(
  id~ : String,
  pressed~ : @minimoon.Val[Bool],
  on_change~ : @minimoon.Emit[Bool],
  content~ : @minimoon.Val[@minimoon.Node],
  disabled? : Bool = false,
  variant? : ButtonVariant = Ghost,
  size? : Size = Medium,
  invalid? : Bool = false,
) -> @minimoon.Val[@minimoon.Node] {
  let tap = form_dispatch(pressed, (current, _ : Unit) => {
    if disabled {
      @minimoon.none
    } else {
      on_change(!current)
    }
  })
  @minimoon.Val::map2(pressed, content, (selected, node) => {
    @minimoon.button_children(
      id~,
      event_key=id + "/toggle",
      disabled~,
      class="mmui-toggle " +
        size_class(size) +
        (if invalid { " mmui-invalid " } else { " " }) +
        (if selected { "mmui-selected" } else { "" }) +
        (if variant == Outline { " mmui-outline" } else { "" }),
      semantics=@minimoon.semantics(
        role=@minimoon.ButtonRole,
        selected~,
        disabled~,
        invalid~,
      ),
      on_tap=tap(()),
      [node],
    )
  })
}

///|
pub fn toggle(
  id~ : String,
  content~ : @minimoon.Val[@minimoon.Node],
  initially_pressed? : Bool = false,
  disabled? : Bool = false,
  variant? : ButtonVariant = Ghost,
  size? : Size = Medium,
  invalid? : Bool = false,
) -> @minimoon.Val[@minimoon.Node] {
  let (pressed, update) = @minimoon.create_variable(initially_pressed)
  toggle_controlled(
    id~,
    pressed~,
    on_change=update.map(value => _ => value),
    content~,
    disabled~,
    variant~,
    size~,
    invalid~,
  )
}

///|
pub fn toggle_group_controlled(
  id~ : String,
  options~ : @minimoon.Val[Array[ChoiceOption]],
  value~ : @minimoon.Val[Array[String]],
  on_change~ : @minimoon.Emit[Array[String]],
  multiple? : Bool = false,
  disabled? : Bool = false,
  orientation? : Orientation = Horizontal,
  variant? : ButtonVariant = Ghost,
  size? : Size = Medium,
  spacing? : Int = 0,
  render? : (ChoiceOption, Bool) -> @minimoon.Node,
) -> @minimoon.Val[@minimoon.Node] {
  let tap = form_dispatch(
    @minimoon.Val::map2(options, value, (options, value) => (options, value)),
    (current, key : String) => {
      let (options, selected) = current
      validate_choices(options)
      if disabled ||
        !options.any(option => option.key == key && !option.disabled) {
        return @minimoon.none
      }
      let selected = normalize_toggle_keys(options, selected, multiple)
      let next = if selected.contains(key) {
        selected.filter(value => value != key)
      } else if multiple {
        let next = selected.copy()
        next.push(key)
        next
      } else {
        [key]
      }
      on_change(next)
    },
  )
  @minimoon.Val::map2(options, value, (options, selected) => {
    validate_choices(options)
    let selected = normalize_toggle_keys(options, selected, multiple)
    ui_container(
      "toggle-group",
      options.map(option => {
        let active = selected.contains(option.key)
        @minimoon.button_children(
          id=id + "-" + option.key,
          class="mmui-toggle " +
            size_class(size) +
            (if variant == Outline { " mmui-outline" } else { "" }) +
            (if active { " mmui-selected" } else { "" }),
          event_key=id + "/" + option.key,
          disabled=disabled || option.disabled,
          semantics=@minimoon.semantics(
            role=@minimoon.ButtonRole,
            selected=active,
            disabled=disabled || option.disabled,
          ),
          on_tap=if disabled || option.disabled {
            @minimoon.none
          } else {
            tap(option.key)
          },
          match render {
            Some(render) => render(option, active)
            None => @minimoon.text(option.label)
          },
        )
      }),
      id~,
      class=orientation_class(orientation),
      style="gap:" + spacing.max(0).to_string() + "px;",
      semantics=@minimoon.semantics(role=@minimoon.GroupRole),
    )
  })
}

///|
pub fn toggle_group(
  id~ : String,
  options~ : @minimoon.Val[Array[ChoiceOption]],
  initial_value? : Array[String] = [],
  multiple? : Bool = false,
  disabled? : Bool = false,
  orientation? : Orientation = Horizontal,
  variant? : ButtonVariant = Ghost,
  size? : Size = Medium,
  spacing? : Int = 0,
  render? : (ChoiceOption, Bool) -> @minimoon.Node,
) -> @minimoon.Val[@minimoon.Node] {
  let (value, update) = @minimoon.create_variable(initial_value)
  toggle_group_controlled(
    id~,
    options~,
    value~,
    on_change=update.map(value => _ => value),
    multiple~,
    disabled~,
    orientation~,
    variant~,
    size~,
    spacing~,
    render?,
  )
}

///|
pub fn[C : @minimoon.IsChildren] toggle_group_item(
  on_tap~ : @minimoon.Cmd,
  pressed? : Bool = false,
  disabled? : Bool = false,
  id? : String = "",
  children : C,
) -> @minimoon.Node {
  @minimoon.button_children(
    on_tap=if disabled { @minimoon.none } else { on_tap },
    id~,
    disabled~,
    class="mmui-toggle " + (if pressed { "mmui-selected" } else { "" }),
    semantics=@minimoon.semantics(
      role=@minimoon.ButtonRole,
      selected=pressed,
      disabled~,
    ),
    children,
  )
}