defmodule AshHqWeb.Components.DocSidebar do @moduledoc "The left sidebar of the docs pages" use Surface.Component alias AshHqWeb.DocRoutes alias Surface.Components.LivePatch 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 add_version, :event, required: true prop remove_version, :event, required: true prop change_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 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 defp render_dsls(assigns, dsls, path) 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, _) do ~F""" """ 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.filter(libraries, fn library -> selected_versions[library.id] && selected_versions[library.id] != "" end) library_name_to_order = libraries |> Enum.sort_by(& &1.order) |> Enum.map(& &1.display_name) libraries |> Enum.flat_map(fn library -> library |> selected_version(library_version, selected_versions) |> case do nil -> [] %{guides: guides} -> Enum.map(guides, &{&1, library.display_name}) end end) |> Enum.group_by(fn {guide, _} -> guide.category end) |> Enum.map(fn {category, guides} -> guides_by_library = library_name_to_order |> Enum.map(fn name -> {name, Enum.flat_map(guides, fn {guide, guide_lib_name} -> if name == guide_lib_name do [guide] else [] end end)} end) |> Enum.reject(fn {_, guides} -> Enum.empty?(guides) end) {category, guides_by_library} 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 defp collapse(id, js \\ %JS{}) do js |> JS.toggle(to: "##{id}", time: 0) |> JS.toggle(to: "##{id}-chevron-down", time: 0) |> JS.toggle(to: "##{id}-chevron-right", time: 0) end end