lib/bitstyles_phoenix/component/heading.ex

defmodule BitstylesPhoenix.Component.Heading do
  use BitstylesPhoenix.Component

  @moduledoc """
  Components for building headings.
  """

  @doc ~s"""
  Render a page header with a `<h1>` title and an optional `title_extra` and some `action` slots
  for that page (usually combined with buttons and dropdowns).

  ## Attributes
  - `class` - Set CSS classes on the outer div.
  - All other attributes are passed to the outer `div` tag.

  ## Attributes - `title_extra` slot
  - `class` - Set CSS classes on the surrounding div.
  - All other attributes are passed to the outer `div` tag.

  ## Attributes - `action` slots
  - `class` - Set CSS classes on the surrounding `li`.
  - `show` - If the action should be rendered. Defaults to `true`.
  - All other attributes are passed to the outer `li` tag.

  See [the page header docs](https://bitcrowd.github.io/bitstyles/?path=/docs/ui-content-page-header--page-header)
  for more examples and details.
  """

  story(
    "Page title",
    '''
        iex> assigns = %{}
        ...> render ~H"""
        ...> <.ui_page_title>
        ...>   Title
        ...> </.ui_page_title>
        ...> """
    ''',
    '''
        """
        <div class="u-flex u-justify-between u-flex-wrap u-items-center">
          <div class="u-flex u-flex-wrap u-items-center">
            <h1 class="u-margin-m-right u-break-text">
              Title
            </h1>
          </div>
        </div>
        """
    ''',
    width: "100%"
  )

  story(
    "Page title with actions and title extra",
    '''
        iex> assigns = %{}
        ...> render ~H"""
        ...> <.ui_page_title>
        ...>   Title
        ...>   <:title_extra>
        ...>     <.ui_badge>Published</.ui_badge>
        ...>   </:title_extra>
        ...>   <:action>
        ...>     <.ui_button>Edit</.ui_button>
        ...>   </:action>
        ...>   <:action>
        ...>     <.ui_button color="danger">Delete</.ui_button>
        ...>   </:action>
        ...>   <:action show={false}>
        ...>     <.ui_button color="danger">Hide me</.ui_button>
        ...>   </:action>
        ...> </.ui_page_title>
        ...> """
    ''',
    '''
        """
        <div class="u-flex u-justify-between u-flex-wrap u-items-center">
          <div class="u-flex u-flex-wrap u-items-center">
            <h1 class="u-margin-m-right u-break-text">
              Title
            </h1>
            <div class="u-flex-shrink-0 u-margin-m-bottom">
              <span class="a-badge u-h6 u-font-medium" data-theme="grayscale">
                Published
              </span>
            </div>
          </div>
          <ul class="u-list-none u-flex u-flex-wrap">
            <li class="u-margin-s2-right u-margin-m-bottom">
              <button type="button" class="a-button">
                Edit
              </button>
            </li>
            <li class="u-margin-s2-right u-margin-m-bottom">
              <button type="button" class="a-button a-button--danger">
                Delete
              </button>
            </li>
          </ul>
        </div>
        """
    ''',
    width: "100%",
    extra_html: """
    <svg xmlns="http://www.w3.org/2000/svg" hidden aria-hidden="true">
      <symbol id="icon-caret-down" viewBox="0 0 100 100">
        <path d="M6.64,34.23a5.57,5.57,0,0,1,7.87-7.89L49.92,61.91,85.49,26.34a5.57,5.57,0,0,1,7.87,7.89L53.94,73.66a5.58,5.58,0,0,1-7.88,0Z" fill-rule="evenodd"/>
      </symbol>
    </svg>
    """
  )

  def ui_page_title(assigns) do
    class = classnames(["u-flex u-justify-between u-flex-wrap u-items-center", assigns[:class]])
    extra = assigns_to_attributes(assigns, [:class, :action, :title_extra])

    assigns = assign(assigns, class: class, extra: extra)

    ~H"""
    <div class={@class} {@extra}>
      <div class={classnames("u-flex u-flex-wrap u-items-center")}>
        <h1 class={classnames("u-margin-m-right u-break-text")}><%= render_slot(@inner_block) %></h1>
        <%= if assigns[:title_extra] do %>
          <div class={classnames(["u-flex-shrink-0 u-margin-m-bottom", @title_extra[:class]])} {assigns_to_attributes(@title_extra, [:class])}>
             <%= render_slot(@title_extra) %>
          </div>
        <% end %>
      </div>
      <%= if assigns[:action] do %>
        <.ui_action_buttons action={@action}/>
      <% end %>
    </div>
    """
  end

  @section_default_border_color "gray-light"

  @doc ~s"""
  Render a section header with optional `action`s and `title_extra`.

  ## Attributes
  - `class` - Set CSS classes on the outer div.
  - `border` - Controls the bottom border and padding (default: true, boolean)
  - `border_color` - The border color, defaults to `gray-light` resulting in `u-border-gray-light-bottom`.
  - `tag` - the heading tag (defaults to h3)
  - `heading_class` - Extra classes on the heading
  - All other attributes are passed to the outer `div` tag.

  ## Attributes - `title_extra` slot
  - `class` - Set CSS classes on the surrounding div.
  - All other attributes are passed to the outer `div` tag.

  ## Attributes - `action` slots
  - `class` - Set CSS classes on the surrounding `li`.
  - `show` - If the action should be rendered. Defaults to `true`.
  - All other attributes are passed to the outer `li` tag.
  """

  story(
    "Section title with default border",
    '''
        iex> assigns = %{}
        ...> render ~H"""
        ...> <.ui_section_title>
        ...>   Section title
        ...> </.ui_section_title>
        ...> """
    ''',
    '''
        """
        <div class="u-flex u-flex-wrap u-items-center u-justify-between u-padding-m-bottom u-border-gray-light-bottom">
          <div class="u-flex u-items-center">
            <h3 class="u-margin-0 u-margin-m-right u-break-text">
              Section title
            </h3>
          </div>
        </div>
        """
    ''',
    width: "100%"
  )

  story(
    "Section title without border and h2",
    '''
        iex> assigns = %{}
        ...> render ~H"""
        ...> <.ui_section_title border={false} tag={:h2}>
        ...>   Section title
        ...> </.ui_section_title>
        ...> """
    ''',
    '''
        """
        <div class="u-flex u-flex-wrap u-items-center u-justify-between">
          <div class="u-flex u-items-center">
            <h2 class="u-margin-0 u-margin-m-right u-break-text">
              Section title
            </h2>
          </div>
        </div>
        """
    ''',
    width: "100%"
  )

  story(
    "Section title with actions and title extra and different border",
    '''
        iex> assigns = %{}
        ...> render ~H"""
        ...> <.ui_section_title border_color="gray-light" heading_class="extra">
        ...>   Section title
        ...>   <:title_extra>
        ...>     <.ui_badge>Published</.ui_badge>
        ...>   </:title_extra>
        ...>   <:action>
        ...>     <.ui_button>Edit</.ui_button>
        ...>   </:action>
        ...>   <:action>
        ...>     <.ui_button color="danger">Delete</.ui_button>
        ...>   </:action>
        ...>   <:action show={false}>
        ...>     <.ui_button color="danger">Hide me</.ui_button>
        ...>   </:action>
        ...> </.ui_section_title>
        ...> """
    ''',
    '''
        """
        <div class="u-flex u-flex-wrap u-items-center u-justify-between u-padding-m-bottom u-border-gray-light-bottom">
          <div class="u-flex u-items-center">
            <h3 class="u-margin-0 u-margin-m-right u-break-text extra">
              Section title
            </h3>
            <span class="a-badge u-h6 u-font-medium" data-theme="grayscale">
              Published
            </span>
          </div>
          <ul class="u-list-none u-flex u-flex-wrap">
            <li class="u-margin-s2-right u-margin-m-bottom">
              <button type="button" class="a-button">
                Edit
              </button>
            </li>
            <li class="u-margin-s2-right u-margin-m-bottom">
              <button type="button" class="a-button a-button--danger">
                Delete
              </button>
            </li>
          </ul>
        </div>
        """
    ''',
    width: "100%"
  )

  def ui_section_title(assigns) do
    border_color = Map.get(assigns, :border_color, @section_default_border_color)
    border = Map.get(assigns, :border, true)

    class =
      classnames([
        "u-flex u-flex-wrap u-items-center u-justify-between",
        {"u-padding-m-bottom u-border-#{border_color}-bottom", border},
        assigns[:class]
      ])

    extra =
      assigns_to_attributes(assigns, [
        :class,
        :action,
        :title_extra,
        :tag,
        :border,
        :border_color,
        :heading_class
      ])

    assigns = assigns |> assign(class: class, extra: extra) |> assign_new(:tag, fn -> :h3 end)

    ~H"""
      <div class={@class} {@extra}>
        <div class={classnames("u-flex u-items-center")}>
          <.dynamic_tag name={@tag} class={classnames(["u-margin-0 u-margin-m-right u-break-text", assigns[:heading_class]])}>
            <%= render_slot(@inner_block) %>
          </.dynamic_tag>
          <%= assigns[:title_extra] && render_slot(@title_extra) %>
        </div>
        <%= if assigns[:action] do %>
          <.ui_action_buttons action={@action}/>
        <% end %>
      </div>
    """
  end

  defp ui_action_buttons(assigns) do
    ~H"""
    <ul class={classnames("u-list-none u-flex u-flex-wrap")}>
      <%= for action <- @action do %>
        <%= if Map.get(action, :show, true) do %>
          <li class={classnames(["u-margin-s2-right u-margin-m-bottom", action[:class]])} {assigns_to_attributes(action, [:class, :show])}>
            <%= render_slot(action) %>
          </li>
        <% end %>
      <% end %>
    </ul>
    """
  end
end