# RadioGroup — accessible headless radio group
#
# Exactly one option can be selected. Arrow keys move focus and selection.
# Ships zero CSS.
#
# Usage:
#   RadioGroup value <=> size
#     div $value: "sm", "Small"
#     div $value: "md", "Medium"
#     div $value: "lg", "Large"

export RadioGroup = component
  @value := null
  @disabled := false
  @orientation := 'vertical'
  @name := ''

  _options ~=
    return [] unless @_slot
    Array.from(@_slot.querySelectorAll('[data-value]') or [])

  _select: (val) ->
    return if @disabled
    @value = val
    @emit 'change', @value

  onKeydown: (e) ->
    radios = @_root?.querySelectorAll('[role="radio"]')
    return unless radios?.length
    focused = Array.from(radios).indexOf(document.activeElement)
    return if focused < 0
    len = radios.length
    next = focused
    switch e.key
      when 'ArrowRight', 'ArrowDown'
        e.preventDefault()
        next = (focused + 1) %% len
      when 'ArrowLeft', 'ArrowUp'
        e.preventDefault()
        next = (focused - 1) %% len
      when 'Home'
        e.preventDefault()
        next = 0
      when 'End'
        e.preventDefault()
        next = len - 1
      else return
    radios[next]?.focus()
    @_select(_options[next]?.dataset.value)

  render
    div ref: "_root", role: "radiogroup", aria-orientation: @orientation
      $orientation: @orientation
      $disabled: @disabled?!

      . ref: "_slot", style: "display:none"
        slot

      for opt, idx in _options
        button role: "radio"
          tabindex: (if (opt.dataset.value is @value) or (@value is null and idx is 0) then "0" else "-1")
          aria-checked: opt.dataset.value is @value
          $checked: (opt.dataset.value is @value)?!
          $disabled: @disabled?!
          $value: opt.dataset.value
          @click: (=> @_select(opt.dataset.value))
          = opt.textContent
