2022-03-29 11:05:19 +13:00
|
|
|
defmodule AshHqWeb.Components.DocSidebar do
|
2022-08-07 11:22:58 +12:00
|
|
|
@moduledoc "The left sidebar of the docs pages"
|
2022-03-29 11:05:19 +13:00
|
|
|
use Surface.Component
|
|
|
|
|
2022-08-07 11:22:58 +12:00
|
|
|
alias AshHqWeb.DocRoutes
|
2022-09-13 08:47:14 +12:00
|
|
|
alias Surface.Components.LivePatch
|
2022-09-27 12:30:56 +13:00
|
|
|
alias Phoenix.LiveView.JS
|
2022-03-29 11:05:19 +13:00
|
|
|
|
2022-09-21 07:44:00 +12:00
|
|
|
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(add_version, :event, required: true)
|
|
|
|
prop(remove_version, :event, required: true)
|
|
|
|
prop(change_version, :event, required: true)
|
2022-03-29 11:05:19 +13:00
|
|
|
|
2022-07-15 07:09:04 +12:00
|
|
|
@spec render(any) :: Phoenix.LiveView.Rendered.t()
|
2022-03-29 11:05:19 +13:00
|
|
|
def render(assigns) do
|
|
|
|
~F"""
|
2022-08-31 20:11:15 +12:00
|
|
|
<aside
|
|
|
|
id={@id}
|
2022-09-08 15:02:18 +12:00
|
|
|
class={"grid h-full overflow-y-auto pb-36 w-fit z-40 bg-white dark:bg-base-dark-900", @class}
|
2022-08-31 20:11:15 +12:00
|
|
|
aria-label="Sidebar"
|
|
|
|
>
|
|
|
|
<div class="flex flex-col">
|
|
|
|
<div class="text-black dark:text-white font-light w-full px-2 mb-2">
|
|
|
|
Including Packages:
|
|
|
|
</div>
|
|
|
|
<AshHqWeb.Components.VersionPills
|
|
|
|
id={"#{@id}-version-pills"}
|
|
|
|
libraries={@libraries}
|
|
|
|
remove_version={@remove_version}
|
|
|
|
selected_versions={@selected_versions}
|
|
|
|
/>
|
|
|
|
</div>
|
2022-04-01 05:36:44 +13:00
|
|
|
<div class="py-3 px-3">
|
2022-03-29 11:05:19 +13:00
|
|
|
<ul class="space-y-2">
|
2022-08-05 05:14:58 +12:00
|
|
|
<div>
|
|
|
|
Guides
|
|
|
|
</div>
|
2022-08-29 00:42:24 +12:00
|
|
|
{#for {category, guides_by_library} <- guides_by_category_and_library(@libraries, @selected_versions)}
|
2022-09-08 15:51:47 +12:00
|
|
|
<div class="text-base-light-500">
|
2022-09-28 16:18:05 +13:00
|
|
|
<button
|
|
|
|
phx-click={collapse("#{@id}-#{String.replace(category, " ", "-")}")}
|
|
|
|
class="flex flex-row items-center"
|
|
|
|
>
|
2022-09-27 12:30:56 +13:00
|
|
|
<div id={"#{@id}-#{String.replace(category, " ", "-")}-chevron-down"}>
|
|
|
|
<Heroicons.Outline.ChevronDownIcon class="w-3 h-3 mr-1" />
|
|
|
|
</div>
|
2022-09-28 16:18:05 +13:00
|
|
|
<div
|
|
|
|
id={"#{@id}-#{String.replace(category, " ", "-")}-chevron-right"}
|
|
|
|
class="-rotate-90"
|
|
|
|
style="display: none;"
|
|
|
|
>
|
2022-09-27 12:30:56 +13:00
|
|
|
<Heroicons.Outline.ChevronDownIcon class="w-3 h-3 mr-1" />
|
|
|
|
</div>
|
|
|
|
<div>{category}</div>
|
2022-09-13 12:35:59 +12:00
|
|
|
</button>
|
2022-08-05 05:14:58 +12:00
|
|
|
</div>
|
2022-09-27 12:30:56 +13:00
|
|
|
<div id={"#{@id}-#{String.replace(category, " ", "-")}"}>
|
|
|
|
{#for {library, guides} <- guides_by_library}
|
|
|
|
<li class="ml-3 text-base-light-400 p-1">
|
2022-08-31 20:11:15 +12:00
|
|
|
{library}
|
|
|
|
<ul>
|
2022-09-27 12:30:56 +13:00
|
|
|
{#for guide <- guides}
|
2022-08-31 20:11:15 +12:00
|
|
|
<li class="ml-1">
|
2022-09-13 08:47:14 +12:00
|
|
|
<LivePatch
|
2022-09-27 12:30:56 +13:00
|
|
|
to={DocRoutes.doc_link(guide, @selected_versions)}
|
2022-08-31 20:11:15 +12:00
|
|
|
class={
|
2022-09-08 15:51:47 +12:00
|
|
|
"flex items-center p-1 text-base font-normal text-base-light-900 rounded-lg dark:text-base-dark-200 hover:bg-base-light-100 dark:hover:bg-base-dark-700",
|
2022-09-27 12:30:56 +13:00
|
|
|
"bg-base-light-300 dark:bg-base-dark-600": @guide && @guide.id == guide.id
|
2022-08-31 20:11:15 +12:00
|
|
|
}
|
|
|
|
>
|
2022-09-27 12:30:56 +13:00
|
|
|
<Heroicons.Outline.BookOpenIcon class="h-4 w-4" />
|
|
|
|
<span class="ml-3 mr-2">{guide.name}</span>
|
2022-09-13 08:47:14 +12:00
|
|
|
</LivePatch>
|
2022-08-31 20:11:15 +12:00
|
|
|
</li>
|
|
|
|
{/for}
|
|
|
|
</ul>
|
|
|
|
</li>
|
2022-08-05 05:14:58 +12:00
|
|
|
{/for}
|
2022-09-27 12:30:56 +13:00
|
|
|
</div>
|
|
|
|
{/for}
|
|
|
|
<div class="mt-4">
|
|
|
|
Reference
|
|
|
|
</div>
|
|
|
|
<div class="ml-2 space-y-2">
|
|
|
|
{#if !Enum.empty?(get_extensions(@libraries, @selected_versions))}
|
|
|
|
<div class="text-base-light-500">
|
2022-09-28 16:18:05 +13:00
|
|
|
<button phx-click={collapse("#{@id}-extension")} class="flex flex-row items-center">
|
2022-09-27 12:30:56 +13:00
|
|
|
<div id={"#{@id}-extension-chevron-down"}>
|
|
|
|
<Heroicons.Outline.ChevronDownIcon class="w-3 h-3 mr-1" />
|
|
|
|
</div>
|
|
|
|
<div id={"#{@id}-extension-chevron-right"} class="-rotate-90" style="display: none;">
|
|
|
|
<Heroicons.Outline.ChevronDownIcon class="w-3 h-3 mr-1" />
|
|
|
|
</div>
|
|
|
|
Extensions
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
{/if}
|
2022-09-28 16:18:05 +13:00
|
|
|
<div id={"#{@id}-extension"}>
|
|
|
|
{#for {library, extensions} <- get_extensions(@libraries, @selected_versions)}
|
|
|
|
<li class="ml-3 text-base-light-200 p-1">
|
|
|
|
{library}
|
|
|
|
<ul>
|
|
|
|
{#for extension <- extensions}
|
|
|
|
<li class="ml-1">
|
|
|
|
<LivePatch
|
|
|
|
to={DocRoutes.doc_link(extension, @selected_versions)}
|
|
|
|
class="flex items-center p-1 text-base font-normal text-base-light-900 rounded-lg dark:text-base-dark-200 hover:bg-base-light-100 dark:hover:bg-base-dark-700"
|
|
|
|
>
|
|
|
|
{render_icon(assigns, extension.type)}
|
|
|
|
<span class="ml-3 mr-2">{extension.name}</span>
|
|
|
|
</LivePatch>
|
|
|
|
{#if @extension && @extension.id == extension.id && !Enum.empty?(extension.dsls)}
|
|
|
|
{render_dsls(assigns, extension.dsls, [])}
|
|
|
|
{/if}
|
|
|
|
</li>
|
|
|
|
{/for}
|
|
|
|
</ul>
|
|
|
|
</li>
|
|
|
|
{/for}
|
|
|
|
</div>
|
2022-03-29 11:05:19 +13:00
|
|
|
|
2022-09-08 15:51:47 +12:00
|
|
|
<div class="text-base-light-500">
|
2022-09-28 16:18:05 +13:00
|
|
|
<button phx-click={collapse("#{@id}-modules")} class="flex flex-row items-center">
|
|
|
|
<div id={"#{@id}-modules-chevron-down"}>
|
|
|
|
<Heroicons.Outline.ChevronDownIcon class="w-3 h-3 mr-1" />
|
|
|
|
</div>
|
|
|
|
<div id={"#{@id}-modules-chevron-right"} class="-rotate-90" style="display: none;">
|
|
|
|
<Heroicons.Outline.ChevronDownIcon class="w-3 h-3 mr-1" />
|
|
|
|
</div>
|
|
|
|
Modules
|
|
|
|
</button>
|
2022-08-05 05:14:58 +12:00
|
|
|
</div>
|
2022-09-27 12:30:56 +13:00
|
|
|
<div id={"#{@id}-modules"}>
|
2022-08-29 00:42:24 +12:00
|
|
|
{#for {category, modules} <- modules_by_category(@libraries, @selected_versions)}
|
2022-08-05 05:14:58 +12:00
|
|
|
<div class="ml-4">
|
2022-09-08 15:51:47 +12:00
|
|
|
<span class="text-sm text-base-light-900 dark:text-base-dark-100">{category}</span>
|
2022-08-05 05:14:58 +12:00
|
|
|
</div>
|
|
|
|
{#for module <- modules}
|
|
|
|
<li class="ml-4">
|
2022-09-13 08:47:14 +12:00
|
|
|
<LivePatch
|
2022-08-07 11:22:58 +12:00
|
|
|
to={DocRoutes.doc_link(module, @selected_versions)}
|
2022-09-28 16:18:05 +13:00
|
|
|
class="flex items-center space-x-2 pt-1 text-base font-normal text-base-light-900 rounded-lg dark:text-base-dark-100 hover:bg-base-light-100 dark:hover:bg-base-light-700"
|
2022-03-29 11:05:19 +13:00
|
|
|
>
|
2022-08-05 05:14:58 +12:00
|
|
|
<Heroicons.Outline.CodeIcon class="h-4 w-4" />
|
2022-08-31 06:26:32 +12:00
|
|
|
<span class="">{module.name}</span>
|
2022-09-13 08:47:14 +12:00
|
|
|
</LivePatch>
|
2022-03-29 11:05:19 +13:00
|
|
|
</li>
|
|
|
|
{/for}
|
2022-08-05 05:14:58 +12:00
|
|
|
{/for}
|
2022-09-27 12:30:56 +13:00
|
|
|
</div>
|
2022-08-05 05:14:58 +12:00
|
|
|
</div>
|
2022-03-29 11:05:19 +13:00
|
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
</aside>
|
|
|
|
"""
|
|
|
|
end
|
|
|
|
|
2022-03-30 17:40:17 +13:00
|
|
|
defp render_dsls(assigns, dsls, path) do
|
|
|
|
~F"""
|
2022-04-02 08:11:17 +13:00
|
|
|
<ul class="ml-1 flex flex-col">
|
2022-03-30 17:40:17 +13:00
|
|
|
{#for dsl <- Enum.filter(dsls, &(&1.path == path))}
|
2022-09-08 15:02:18 +12:00
|
|
|
<li class="border-l pl-1 border-primary-light-600 border-opacity-30">
|
2022-04-01 10:29:23 +13:00
|
|
|
<div class="flex flex-row items-center">
|
2022-04-02 08:11:17 +13:00
|
|
|
{#if Enum.any?(dsls, &List.starts_with?(&1.path, dsl.path ++ [dsl.name]))}
|
2022-04-01 19:43:09 +13:00
|
|
|
{#if !(@dsl && List.starts_with?(@dsl.path ++ [@dsl.name], path ++ [dsl.name]))}
|
2022-09-13 13:25:03 +12:00
|
|
|
<button>
|
|
|
|
<Heroicons.Outline.ChevronDownIcon class="w-3 h-3" />
|
|
|
|
</button>
|
2022-04-01 10:29:23 +13:00
|
|
|
{/if}
|
|
|
|
{/if}
|
2022-09-13 08:47:14 +12:00
|
|
|
<LivePatch
|
2022-08-07 11:22:58 +12:00
|
|
|
to={DocRoutes.doc_link(dsl, @selected_versions)}
|
2022-04-01 10:29:23 +13:00
|
|
|
class={
|
2022-09-08 15:02:18 +12:00
|
|
|
"flex items-center p-1 text-base font-normal rounded-lg hover:text-primary-light-300",
|
|
|
|
"text-primary-600 dark:text-primary-dark-400 font-bold": @dsl && @dsl.id == dsl.id
|
2022-04-01 10:29:23 +13:00
|
|
|
}
|
|
|
|
>
|
|
|
|
{dsl.name}
|
2022-09-13 08:47:14 +12:00
|
|
|
</LivePatch>
|
2022-04-01 10:29:23 +13:00
|
|
|
</div>
|
2022-09-13 13:25:03 +12:00
|
|
|
{render_dsls(assigns, dsls, path ++ [dsl.name])}
|
2022-03-30 17:40:17 +13:00
|
|
|
</li>
|
|
|
|
{/for}
|
|
|
|
</ul>
|
|
|
|
"""
|
|
|
|
end
|
|
|
|
|
2022-03-29 11:05:19 +13:00
|
|
|
def render_icon(assigns, "Resource") do
|
|
|
|
~F"""
|
|
|
|
<Heroicons.Outline.ServerIcon class="h-4 w-4" />
|
|
|
|
"""
|
|
|
|
end
|
|
|
|
|
|
|
|
def render_icon(assigns, "Api") do
|
|
|
|
~F"""
|
|
|
|
<Heroicons.Outline.SwitchHorizontalIcon class="h-4 w-4" />
|
|
|
|
"""
|
|
|
|
end
|
|
|
|
|
|
|
|
def render_icon(assigns, "DataLayer") do
|
|
|
|
~F"""
|
|
|
|
<Heroicons.Outline.DatabaseIcon class="h-4 w-4" />
|
|
|
|
"""
|
|
|
|
end
|
|
|
|
|
|
|
|
def render_icon(assigns, "Flow") do
|
|
|
|
~F"""
|
|
|
|
<Heroicons.Outline.MapIcon class="h-4 w-4" />
|
|
|
|
"""
|
|
|
|
end
|
|
|
|
|
|
|
|
def render_icon(assigns, "Notifier") do
|
|
|
|
~F"""
|
|
|
|
<Heroicons.Outline.MailIcon class="h-4 w-4" />
|
|
|
|
"""
|
|
|
|
end
|
|
|
|
|
|
|
|
def render_icon(assigns, "Registry") do
|
|
|
|
~F"""
|
|
|
|
<Heroicons.Outline.ViewListIcon class="h-4 w-4" />
|
|
|
|
"""
|
|
|
|
end
|
|
|
|
|
|
|
|
def render_icon(assigns, _) do
|
|
|
|
~F"""
|
|
|
|
<Heroicons.Outline.PuzzleIcon class="h-4 w-4" />
|
|
|
|
"""
|
|
|
|
end
|
|
|
|
|
2022-08-29 00:42:24 +12:00
|
|
|
@start_guides ["Tutorials", "Topics", "How To", "Misc"]
|
|
|
|
|
|
|
|
defp guides_by_category_and_library(libraries, 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)
|
|
|
|
|
2022-08-05 05:14:58 +12:00
|
|
|
libraries
|
|
|
|
|> Enum.flat_map(fn library ->
|
|
|
|
library.versions
|
2022-08-20 04:35:07 +12:00
|
|
|
|> Enum.find(&Ash.Resource.loaded?(&1, :guides))
|
2022-08-05 05:14:58 +12:00
|
|
|
|> case do
|
|
|
|
nil ->
|
|
|
|
[]
|
|
|
|
|
|
|
|
%{guides: guides} ->
|
2022-08-29 00:42:24 +12:00
|
|
|
Enum.map(guides, &{&1, library.display_name})
|
2022-08-05 05:14:58 +12:00
|
|
|
end
|
|
|
|
end)
|
2022-08-29 00:42:24 +12:00
|
|
|
|> Enum.group_by(fn {guide, _} ->
|
|
|
|
guide.category
|
2022-04-05 18:20:36 +12:00
|
|
|
end)
|
|
|
|
|> Enum.map(fn {category, guides} ->
|
2022-08-29 00:42:24 +12:00
|
|
|
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, selected_versions) do
|
|
|
|
libraries
|
|
|
|
|> Enum.filter(fn library ->
|
|
|
|
selected_versions[library.id] && selected_versions[library.id] != ""
|
|
|
|
end)
|
|
|
|
|> Enum.sort_by(& &1.order)
|
|
|
|
|> Enum.flat_map(fn library ->
|
|
|
|
case Enum.find(library.versions, &Ash.Resource.loaded?(&1, :extensions)) do
|
|
|
|
nil ->
|
|
|
|
[]
|
|
|
|
|
|
|
|
version ->
|
|
|
|
case version.extensions do
|
|
|
|
[] ->
|
|
|
|
[]
|
|
|
|
|
|
|
|
extensions ->
|
|
|
|
[{library.display_name, extensions}]
|
|
|
|
end
|
|
|
|
end
|
2022-04-05 18:20:36 +12:00
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
2022-08-29 00:42:24 +12:00
|
|
|
@last_categories ["Errors"]
|
|
|
|
|
|
|
|
defp modules_by_category(libraries, selected_versions) do
|
|
|
|
libraries =
|
|
|
|
Enum.filter(libraries, fn library ->
|
|
|
|
selected_versions[library.id] && selected_versions[library.id] != ""
|
|
|
|
end)
|
|
|
|
|
|
|
|
libraries
|
|
|
|
|> Enum.flat_map(fn library ->
|
2022-08-05 05:14:58 +12:00
|
|
|
library.versions
|
2022-08-29 00:42:24 +12:00
|
|
|
|> Enum.find(&Ash.Resource.loaded?(&1, :modules))
|
2022-08-05 05:14:58 +12:00
|
|
|
|> case do
|
2022-07-28 07:57:39 +12:00
|
|
|
nil ->
|
|
|
|
[]
|
2022-03-29 11:05:19 +13:00
|
|
|
|
2022-08-29 00:42:24 +12:00
|
|
|
%{modules: modules} ->
|
|
|
|
modules
|
2022-07-28 07:57:39 +12:00
|
|
|
end
|
2022-08-05 05:14:58 +12:00
|
|
|
end)
|
2022-08-29 00:42:24 +12:00
|
|
|
|> 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([], @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)
|
|
|
|
)
|
2022-03-29 11:05:19 +13:00
|
|
|
end
|
2022-09-27 12:30:56 +13:00
|
|
|
|
|
|
|
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
|
2022-03-29 11:05:19 +13:00
|
|
|
end
|