lib/phx_live_storybook/stories/index.ex

defmodule PhxLiveStorybook.Index do
  @moduledoc """
  An index is an optional file you can create in every folder of your storybook content tree to
  improve rendering of the storybook sidebar.

  The index files can be used:
    - to customize the folder itself: name, icon and opening status.
    - to customize folder direct children (only stories): name and icon.

  Indexes must be created as `index.exs` files.

  Read the [icons](guides/icons.md) guide for more information on custom icon usage.

  ## Usage

  ```elixir
  # storybook/_components.index.exs
  defmodule MyAppWeb.Storybook.Components do
    use PhxLiveStorybook.Index

    def folder_name, do: "My Components"
    def folder_icon, do: {:fa, "icon"}
    def folder_open?, do: true

    def entry("a_component"), do: [name: "My Component"]
    def entry("other_component"), do: [name: "Another Component", icon: {:fa, "icon", :thin}]
  end
  ```
  """

  defmodule IndexBehaviour do
    @moduledoc false

    @type icon_provider :: :fa | :hero
    @type icon ::
            {icon_provider(), String.t()}
            | {icon_provider(), String.t(), atom}
            | {icon_provider(), String.t(), atom, String.t()}

    @callback folder_name() :: nil | String.t()
    @callback folder_icon() :: nil | icon()
    @callback folder_open?() :: boolean()
    @callback entry(String.t()) :: [key: String.t() | icon()]
  end

  @doc """
  Convenience helper for using the functions above.
  """
  defmacro __using__(_) do
    quote do
      @behaviour IndexBehaviour

      @on_definition {PhxLiveStorybook.Stories.IndexValidator, :on_definition}

      @impl IndexBehaviour
      def folder_name, do: nil

      @impl IndexBehaviour
      def folder_icon, do: nil

      @impl IndexBehaviour
      def folder_open?, do: false

      @impl IndexBehaviour
      def entry(_), do: []

      defoverridable folder_name: 0, folder_icon: 0, folder_open?: 0, entry: 1
    end
  end
end