lib/layout_blocks.ex

defmodule BlockBox.LayoutBlocks do
  @moduledoc """
  Defines generator functions for all [layout blocks](https://api.slack.com/reference/block-kit/blocks).
  """

  alias BlockBox.CompositionObjects, as: CO
  alias BlockBox.Utils, as: Utils

  @doc """
  Creates a [section block](https://api.slack.com/reference/block-kit/blocks#section).

  ## Options
  Options are not included by default.
  * `:block_id` - string
  * `:fields` - list of `t:BlockBox.CompositionObjects.text_object/0`s
  * `:accessory` - any element from `BlockBox.BlockElements`
  """
  @spec section(String.t() | CO.text_object(), keyword()) :: map()
  def section(text, opts \\ [])

  def section(text, opts) when is_binary(text) do
    CO.text_object(text, :mrkdwn)
    |> section(opts)
  end

  def section(text_object, opts) do
    %{
      type: "section",
      text: text_object
    }
    |> Map.merge(Enum.into(opts, %{}))
  end

  @doc """
  Creates a [divider block](https://api.slack.com/reference/block-kit/blocks#divider).

  ## Options
  Options are not included by default.
  * `:block_id` - String
  """
  @spec divider(keyword()) :: map()
  def divider(opts \\ []) do
    %{type: "divider"}
    |> Map.merge(Enum.into(opts, %{}))
  end

  @doc """
  Creates an [image block](https://api.slack.com/reference/block-kit/blocks#image).

  ## Options
  Options are not included by default.
  * `:title` - `t:BlockBox.CompositionObjects.plain_text_object/0` or String
  * `:block_id` - String
  """
  @spec image_block(String.t(), String.t(), keyword()) :: map()
  def image_block(image_url, alt_text, opts \\ []) do
    opts = Utils.convert_text_opts(opts, [:title])

    %{
      type: "image",
      image_url: image_url,
      alt_text: alt_text
    }
    |> Map.merge(Enum.into(opts, %{}))
  end

  @doc """
  Creates an [actions block](https://api.slack.com/reference/block-kit/blocks#actions).

  ## Options
  Options are not included by default.
  * `:block_id` - String
  """
  @spec actions_block(list(), keyword()) :: map()
  def actions_block(elements, opts \\ []) when is_list(elements) do
    %{
      type: "actions",
      elements: elements
    }
    |> Map.merge(Enum.into(opts, %{}))
  end

  @doc """
  Creates a [context block](https://api.slack.com/reference/block-kit/blocks#context).

  ## Options
  Options are not included by default.
  * `:block_id` - String
  """
  @spec context_block(list(), keyword()) :: map()
  def context_block(elements, opts \\ []) when is_list(elements) do
    %{
      type: "context",
      elements: elements
    }
    |> Map.merge(Enum.into(opts, %{}))
  end

  @doc """
  Creates an [input block](https://api.slack.com/reference/block-kit/blocks#input).

  ## Options
  Options are not included by default.
  * `:block_id` - String
  * `:hint` - `t:BlockBox.CompositionObjects.plain_text_object/0` or String
  * `:optional` - boolean
  """
  @spec input(String.t() | CO.plain_text_object(), map(), keyword()) :: map()
  def input(label, element, opts \\ [])

  def input(label, element, opts) when is_binary(label) do
    CO.text_object(label)
    |> input(element, opts)
  end

  def input(label, element, opts) do
    opts = Utils.convert_text_opts(opts, [:hint])

    %{
      type: "input",
      element: element,
      label: label
    }
    |> Map.merge(Enum.into(opts, %{}))
  end

  @doc """
  Creates a [file block](https://api.slack.com/reference/block-kit/blocks#file).
  ## Options
  Options are not included by default.
  * `:block_id` - String
  """
  def file_block(external_id, source \\ "remote", opts \\ []) do
    %{
      type: "file",
      external_id: external_id,
      source: source
    }
    |> Map.merge(Enum.into(opts, %{}))
  end
end