lib/phoenix/ui/components/table.ex

defmodule Phoenix.UI.Components.Table do
  @moduledoc """
  Provides a table components.
  """
  import Phoenix.UI.Components.Paper

  use Phoenix.UI, :component

  @doc """
  Renders table component.

  ## Examples

      ```
      <.table>
        <.tr>
          <.th>Month</.th>
          <.th>Savings</.th>
        </.tr>
        <.tr>
          <.td>January</.td>
          <.td>$100</.td>
        </.tr>
      </.table>
      ```

  """
  @spec table(Socket.assigns()) :: Rendered.t()
  def table(assigns) do
    assigns
    |> build_paper_assigns()
    |> generate_markup()
  end

  @doc """
  Renders body in table.

  ## Examples

      ```
      <.table>
        <.tbody>
          <.tr>
            <.td>Cell A</.td>
            <.td>Cell B</.td>
          </.tr>
        </.tbody>
      </.table>
      ```

  """
  @spec tbody(Socket.assigns()) :: Rendered.t()
  def tbody(assigns) do
    ~H"""
    <tbody>
      <%= render_slot(@inner_block) %>
    </tbody>
    """
  end

  @doc """
  Renders standard data cell in table.

  ## Examples

      ```
      <.table>
        <.tr>
          <.td>Cell A</.td>
          <.td>Cell B</.td>
        </.tr>
      </.table>
      ```

  """
  @spec td(Socket.assigns()) :: Rendered.t()
  def td(assigns) do
    ~H"""
    <td class="p-3 break-words">
      <%= render_slot(@inner_block) %>
    </td>
    """
  end

  @doc """
  Renders footer in table.

  ## Examples

      ```
      <.table>
        <.tfoot>
          <.tr>
            <.th>Sum</.th>
            <.th>$180</.th>
          </.tr>
        </.tfoot>
      </.table>
      ```

  """
  @spec tfoot(Socket.assigns()) :: Rendered.t()
  def tfoot(assigns) do
    ~H"""
    <tfoot>
      <%= render_slot(@inner_block) %>
    </tfoot>
    """
  end

  @doc """
  Renders header cell in table.

  ## Examples

      ```
      <.table>
        <.thead>
          <.tr>
            <.th>Month</.th>
            <.th>Savings</.th>
          </.tr>
        </.thead>
      </.table>
      ```

  """
  @spec th(Socket.assigns()) :: Rendered.t()
  def th(raw_assigns) do
    attrs = Map.drop(raw_assigns, [:__changed__, :inner_block])
    assigns = Map.put(raw_assigns, :attrs, attrs)

    ~H"""
    <th
      class="p-3 text-left text-xs font-medium text-slate-500 dark:text-white uppercase tracking-wider"
      {@attrs}
    >
      <%= render_slot(@inner_block) %>
    </th>
    """
  end

  @doc """
  Renders header in table.

  ## Examples

      ```
      <.table>
        <.thead>
          ...
        </.thead>
      </.table>
      ```

  """
  @spec thead(Socket.assigns()) :: Rendered.t()
  def thead(assigns) do
    ~H"""
    <thead class="border-b border-slate-200 dark:border-slate-600 text-sm font-medium bg-slate-50 dark:bg-slate-700">
      <%= render_slot(@inner_block) %>
    </thead>
    """
  end

  @doc """
  Renders row in table.

  ## Examples

      ```
      <.table>
        <.tr>
          ...
        </.tr>
      </.table>
      ```

  """
  @spec tr(Socket.assigns()) :: Rendered.t()
  def tr(assigns) do
    ~H"""
    <tr class="even:bg-slate-50 even:dark:bg-slate-700">
      <%= render_slot(@inner_block) %>
    </tr>
    """
  end

  #############################################################################################
  ### Markup
  #############################################################################################

  defp generate_markup(assigns) do
    ~H"""
    <.paper {@paper} extend_class="overflow-x-auto" variant="elevated">
      <table class="w-full text-left text-slate-700 dark:text-slate-300">
        <%= render_slot(@inner_block) %>
      </table>
    </.paper>
    """
  end

  #############################################################################################
  ### Paper Assigns
  #############################################################################################

  defp build_paper_assigns(assigns) do
    paper =
      Map.drop(assigns, [
        :__changed__,
        :inner_block
      ])

    assign(assigns, :paper, paper)
  end
end