lib/selection_list/selection_list_item.ex

defmodule FloUI.SelectionListItem do
  @moduledoc """
  ## Usage in SnapFramework

  A selection list item used by SelectionList.

  data is a tuple in the form of
  ``` elixir
  {label, value, id}
  ```

  ``` elixir
  <%= graph font_size: 20 %>

  <%= component FloUI.SelectionListItem,
      {@label, @value, @key}
  %>
  ```
  """

  @default_theme FloUI.Theme.preset(:primary)

  use SnapFramework.Component,
    name: :selection_list_item,
    template: "lib/selection_list/selection_list_item.eex",
    controller: FloUI.SelectionListItemController,
    assigns: [
      hovered: false,
      width: 500,
      height: 50
    ],
    opts: []

  defcomponent(:selection_list_item, :tuple)

  watch([:hovered])

  use_effect([assigns: [selected: :any]],
    run: [:on_selected_change]
  )

  @impl true
  def setup(%{assigns: %{data: {label, value, key}, hovered: hovered, opts: opts}} = scene) do
    request_input(scene, [:cursor_pos])

    assign(scene,
      label: label,
      value: value,
      key: key,
      width: opts[:width] || 500,
      selected: opts[:selected] || false,
      hovered: hovered,
      theme: get_theme(opts)
    )
  end

  @impl true
  def bounds(_data, opts) do
    {0.0, 0.0, opts[:width] || 500, 50}
  end

  @impl true
  def process_input({:cursor_pos, _}, :box, scene) do
    {:noreply, assign(scene, hovered: true)}
  end

  def process_input({:cursor_pos, _}, _, scene) do
    {:noreply, assign(scene, hovered: false)}
  end

  def process_input(
        {:cursor_button, {:btn_left, 1, _, _}},
        :box,
        %{assigns: %{key: key, label: label, value: value, selected: false}} = scene
      ) do
    send_parent_event(scene, {:select, {label, value, key}})
    {:noreply, assign(scene, selected: true)}
  end

  def process_input(
        {:cursor_button, {:btn_left, 1, _, _}},
        :box,
        %{assigns: %{selected: true}} = scene
      ) do
    send_parent_event(scene, :deselect)
    {:noreply, assign(scene, selected: false)}
  end

  def process_input(_, _, scene) do
    {:noreply, scene}
  end

  @impl true
  def process_call(:deselect, _, scene) do
    {:reply, :ok, assign(scene, selected: false)}
  end

  defp get_theme(opts) do
    case opts[:theme] do
      nil -> @default_theme
      :dark -> @default_theme
      :light -> @default_theme
      theme -> theme
    end
    |> FloUI.Theme.normalize()
  end
end