defmodule AshHqWeb.Components.DocSidebar do @moduledoc "The left sidebar of the docs pages" use Surface.Component alias AshHqWeb.Components.DocSidebarDslItems alias AshHqWeb.Components.TreeView alias AshHqWeb.DocRoutes alias Phoenix.LiveView.JS prop class, :css_class, default: "" prop libraries, :list, required: true prop extension, :any, default: nil prop guide, :any, default: nil prop library, :any, default: nil prop library_version, :any, default: nil prop selected_versions, :map, default: %{} prop id, :string, required: true prop dsl, :any, required: true prop module, :any, required: true prop mix_task, :any, required: true prop remove_version, :event, required: true data guides_by_category_and_library, :any data extensions, :any data modules_by_category, :any data mix_tasks_by_category, :any @spec render(any) :: Phoenix.LiveView.Rendered.t() def render(assigns) do selected_versions = if assigns[:library_version] do Map.put( assigns[:selected_versions] || %{}, assigns[:library_version].library_id, assigns[:library_version].id ) else assigns[:selected_versions] end assigns = assign(assigns, :selected_versions, selected_versions) assigns = assign( assigns, guides_by_category_and_library: guides_by_category_and_library( assigns[:libraries], assigns[:library_version], assigns[:selected_versions] ), extensions: get_extensions( assigns[:libraries], assigns[:library_versions], assigns[:selected_versions] ), modules_by_category: modules_by_category( assigns[:libraries], assigns[:library_version], assigns[:selected_versions] ), mix_tasks_by_category: mix_tasks_by_category( assigns[:libraries], assigns[:library_version], assigns[:selected_versions] ) ) ~F""" """ end def render_icon(assigns, "Guide") do ~F""" """ end def render_icon(assigns, "Resource") do ~F""" """ end def render_icon(assigns, "Api") do ~F""" """ end def render_icon(assigns, "DataLayer") do ~F""" """ end def render_icon(assigns, "Flow") do ~F""" """ end def render_icon(assigns, "Notifier") do ~F""" """ end def render_icon(assigns, "Registry") do ~F""" """ end def render_icon(assigns, "Mix Task") do ~F""" """ end def render_icon(assigns, _) do ~F""" """ end def hide_sidebar(js \\ %JS{}) do js |> JS.hide( to: "#mobile-sidebar-container", transition: { "transition ease-out duration-75", "opacity-100", "opacity-0" } ) end defp selected_version(library, library_version, selected_versions) do selected_version = selected_versions[library.id] if library_version && library_version.library_id == library.id do library_version else if selected_version == "latest" do AshHqWeb.Helpers.latest_version(library) else if selected_version not in [nil, ""] do Enum.find(library.versions, &(&1.id == selected_version)) end end end end @start_guides ["Tutorials", "Topics", "How To", "Misc"] defp guides_by_category_and_library(libraries, library_version, selected_versions) do libraries |> Enum.map(&{&1, selected_version(&1, library_version, selected_versions)}) |> Enum.filter(fn {_library, version} -> version != nil end) |> Enum.sort_by(fn {library, _version} -> library.order end) |> Enum.flat_map(fn {library, %{guides: guides}} -> guides |> Enum.sort_by(& &1.order) |> Enum.group_by(& &1.category) |> Enum.map(fn {category, guides} -> {category, {library.display_name, guides}} end) end) |> Enum.group_by(fn {category, _} -> category end, fn {_, lib_guides} -> lib_guides end) |> partially_alphabetically_sort(@start_guides, []) end defp get_extensions(libraries, library_version, selected_versions) do libraries |> Enum.sort_by(& &1.order) |> Enum.map(&{&1.display_name, selected_version(&1, library_version, selected_versions)}) |> Enum.filter(&elem(&1, 1)) |> Enum.flat_map(fn {name, version} -> case version.extensions do [] -> [] %Ash.NotLoaded{} -> raise "extensions not selected for #{version.version} | #{version.id} of #{name}" extensions -> [{name, extensions}] end end) end @last_categories ["Errors"] defp modules_by_category(libraries, library_version, selected_versions) do libraries |> Enum.map(&selected_version(&1, library_version, selected_versions)) |> Enum.filter(& &1) |> Enum.flat_map(& &1.modules) |> Enum.group_by(fn module -> module.category end) |> Enum.sort_by(fn {category, _} -> category end) |> Enum.map(fn {category, modules} -> {category, Enum.sort_by(modules, & &1.name)} end) |> partially_alphabetically_sort([], []) end defp mix_tasks_by_category(libraries, library_version, selected_versions) do libraries |> Enum.map(&selected_version(&1, library_version, selected_versions)) |> Enum.filter(& &1) |> Enum.flat_map(& &1.mix_tasks) |> Enum.group_by(fn mix_task -> mix_task.category end) |> Enum.sort_by(fn {category, _} -> category end) |> Enum.map(fn {category, mix_tasks} -> {category, Enum.sort_by(mix_tasks, & &1.name)} end) |> partially_alphabetically_sort([], @last_categories) end defp partially_alphabetically_sort(keyed_list, first, last) do {first_items, rest} = Enum.split_with(keyed_list, fn {key, _} -> key in first end) {last_items, rest} = Enum.split_with(rest, fn {key, _} -> key in last end) first_items |> Enum.sort_by(fn {key, _} -> Enum.find_index(first, &(&1 == key)) end) |> Enum.concat(Enum.sort_by(rest, &elem(&1, 0))) |> Enum.concat( Enum.sort_by(last_items, fn {key, _} -> Enum.find_index(last, &(&1 == key)) end) ) end def slug(string) do string |> String.downcase() |> String.replace(" ", "_") |> String.replace(~r/[^a-z0-9-_]/, "-") end end