improvement: fix item paths, make sidebar much friendlier

This commit is contained in:
Zach Daniel 2022-08-04 13:14:58 -04:00
parent 60a799517d
commit c9f0b23d90
8 changed files with 180 additions and 138 deletions

View file

@ -74,7 +74,7 @@ defmodule AshHq.Docs.Extensions.Search.Transformers.AddSearchStructure do
{AshHq.Docs.Extensions.Search.Changes.SanitizeName,
source: config.name_attribute,
destination: config.sanitized_name_attribute,
use_path_for_name: AshHq.Docs.Extensions.Search.use_path_for_name?(config.resource)}
use_path_for_name?: AshHq.Docs.Extensions.Search.use_path_for_name?(config.resource)}
)
)
end

View file

@ -21,100 +21,131 @@ defmodule AshHqWeb.Components.DocSidebar do
@spec render(any) :: Phoenix.LiveView.Rendered.t()
def render(assigns) do
~F"""
<aside id={@id} class={"grid h-full overflow-y-auto pb-36", @class} aria-label="Sidebar">
<aside id={@id} class={"grid h-full overflow-y-auto pb-36 w-2/12", @class} aria-label="Sidebar">
<div class="py-3 px-3">
<ul class="space-y-2">
{#for library <- @libraries}
<li>
<LivePatch
to={Routes.library_link(library, selected_version_sanitized_name(library, @selected_versions))}
class={
"flex items-center p-2 text-base font-normal text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700",
"dark:bg-gray-600": !@module && !@guide && !@extension && @library && library.id == @library.id
}
>
<Heroicons.Outline.CollectionIcon class="w-6 h-6" />
<span class="ml-3 mr-2">{library.display_name}</span>
<span class="font-light text-gray-500">{selected_version_name(library, @selected_versions)}</span>
</LivePatch>
{#if @library && @library_version && library.id == @library.id}
{#for {category, guides} <- guides_by_category(@library_version.guides)}
<div class="ml-2 text-gray-500">
{category}
</div>
{#for guide <- guides}
<li class="ml-3">
<LivePatch
to={Routes.doc_link(guide, @selected_versions)}
class={
"flex items-center p-1 text-base font-normal text-gray-900 rounded-lg dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700",
"dark:bg-gray-600": @guide && @guide.id == guide.id
}
>
<Heroicons.Outline.BookOpenIcon class="h-4 w-4" />
<span class="ml-3 mr-2">{guide.name}</span>
</LivePatch>
</li>
{/for}
{/for}
<div>
Guides
</div>
{#for {category, guides} <- guides_by_category(@libraries)}
<div class="text-gray-500">
{#if @sidebar_state["guides-#{category}"] == "open" || (@guide && Enum.any?(guides, &(&1.id == @guide.id))) || (@sidebar_state["guides-#{category}"] != "closed" && category == "Tutorials")}
<button :on-click={@collapse_sidebar} phx-value-id={"guides-#{category}"}>
<Heroicons.Outline.ChevronDownIcon class="w-3 h-3" />
</button>
{#else}
<button :on-click={@expand_sidebar} phx-value-id={"guides-#{category}"}>
<Heroicons.Outline.ChevronRightIcon class="w-3 h-3" />
</button>
{/if}
{category}
</div>
{#if @sidebar_state["guides-#{category}"] == "open" || (@guide && Enum.any?(guides, &(&1.id == @guide.id))) || (@sidebar_state["guides-#{category}"] != "closed" && category == "Tutorials")}
{#for guide <- guides}
<li class="ml-3">
<LivePatch
to={Routes.doc_link(guide, @selected_versions)}
class={
"flex items-center p-1 text-base font-normal text-gray-900 rounded-lg dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700",
"bg-gray-300 dark:bg-gray-600": @guide && @guide.id == guide.id
}
>
<Heroicons.Outline.BookOpenIcon class="h-4 w-4" />
<span class="ml-3 mr-2">{guide.name}</span>
</LivePatch>
</li>
{/for}
{/if}
{/for}
<div class="mt-4">
Reference
</div>
<div class="ml-2 space-y-2">
<div class="text-gray-500">
{#if @sidebar_state["extensions"] == "open" || @extension}
<button :on-click={@collapse_sidebar} phx-value-id="extensions">
<Heroicons.Outline.ChevronDownIcon class="w-3 h-3" />
</button>
{#else}
<button :on-click={@expand_sidebar} phx-value-id="extensions">
<Heroicons.Outline.ChevronRightIcon class="w-3 h-3" />
</button>
{/if}
Extensions
</div>
{#if @sidebar_state["extensions"] == "open" || @extension}
{#for extension <- get_extensions(@libraries)}
<li class="ml-3">
<LivePatch
to={Routes.doc_link(extension, @selected_versions)}
class={
"flex items-center p-1 text-base font-normal text-gray-900 rounded-lg dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700",
"dark:bg-gray-600": @extension && @extension.id == extension.id
}
>
<span class="ml-3 mr-2">{extension.name}</span>
{render_icon(assigns, extension.type)}
</LivePatch>
{#if @extension && @extension.id == extension.id && !Enum.empty?(extension.dsls)}
{render_dsls(assigns, extension.dsls, [])}
{/if}
</li>
{/for}
{/if}
{#if !Enum.empty?(@library_version.guides)}
<div class="ml-2 text-gray-500">
Extensions
</div>
{/if}
{#for extension <- get_extensions(library, @selected_versions)}
<li class="ml-3">
<div class="text-gray-500">
{#if @sidebar_state["modules"] == "open" || @module}
<button :on-click={@collapse_sidebar} phx-value-id="modules">
<Heroicons.Outline.ChevronDownIcon class="w-3 h-3" />
</button>
{#else}
<button :on-click={@expand_sidebar} phx-value-id="modules">
<Heroicons.Outline.ChevronRightIcon class="w-3 h-3" />
</button>
{/if}
Modules
</div>
{#if @sidebar_state["modules"] == "open" || @module}
{#for {category, modules} <- modules_and_categories(@libraries)}
<div class="ml-4">
<span class="text-sm text-gray-900 dark:text-gray-500">{category}</span>
</div>
{#for module <- modules}
<li class="ml-4">
<LivePatch
to={Routes.doc_link(extension, @selected_versions)}
to={Routes.doc_link(module, @selected_versions)}
class={
"flex items-center p-1 text-base font-normal text-gray-900 rounded-lg dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700",
"dark:bg-gray-600": @extension && @extension.id == extension.id
"flex items-center pt-1 text-base font-normal text-gray-900 rounded-lg dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700",
"dark:bg-gray-600": @module && @module.id == module.id
}
>
<span class="ml-3 mr-2">{extension.name}</span>
{render_icon(assigns, extension.type)}
<span class="ml-3 mr-2">{module.name}</span>
<Heroicons.Outline.CodeIcon class="h-4 w-4" />
</LivePatch>
{#if @extension && @extension.id == extension.id && !Enum.empty?(extension.dsls)}
{render_dsls(assigns, extension.dsls, [])}
{/if}
</li>
{/for}
{#if !Enum.empty?(@library_version.modules)}
<div class="ml-2 text-gray-500">
Modules
</div>
{#for {category, modules} <- modules_and_categories(@library_version.modules)}
<div class="ml-4">
<span class="text-sm text-gray-900 dark:text-gray-500">{category}</span>
</div>
{#for module <- modules}
<li class="ml-4">
<LivePatch
to={Routes.doc_link(module, @selected_versions)}
class={
"flex items-center pt-1 text-base font-normal text-gray-900 rounded-lg dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700",
"dark:bg-gray-600": @module && @module.id == module.id
}
>
<span class="ml-3 mr-2">{module.name}</span>
<Heroicons.Outline.CodeIcon class="h-4 w-4" />
</LivePatch>
</li>
{/for}
{/for}
{/if}
{/if}
</li>
{/for}
{/for}
{/if}
</div>
</ul>
</div>
</aside>
"""
end
defp modules_and_categories(modules) do
modules
defp modules_and_categories(libraries) do
libraries
|> Enum.flat_map(fn library ->
library.versions
|> Enum.find(&Ash.Resource.Info.loaded?(&1, :modules))
|> case do
nil ->
[]
%{modules: modules} ->
modules
end
end)
|> Enum.group_by(&{&1.category_index, &1.category})
|> Enum.sort_by(fn {{index, _}, _} ->
index
@ -205,8 +236,19 @@ defmodule AshHqWeb.Components.DocSidebar do
"""
end
defp guides_by_category(guides) do
guides
defp guides_by_category(libraries) do
libraries
|> Enum.flat_map(fn library ->
library.versions
|> Enum.find(&Ash.Resource.Info.loaded?(&1, :guides))
|> case do
nil ->
[]
%{guides: guides} ->
guides
end
end)
|> Enum.group_by(& &1.category)
|> Enum.sort_by(fn {category, _guides} ->
Enum.find_index(["Tutorials", "Topics", "How To", "Misc"], &(&1 == category)) || :infinity
@ -216,45 +258,17 @@ defmodule AshHqWeb.Components.DocSidebar do
end)
end
defp selected_version_name(library, selected_versions) do
if selected_versions[library.id] in ["latest", nil, ""] do
"latest"
else
Enum.find_value(library.versions, fn version ->
version.version
if version.id == selected_versions[library.id] do
version.version
end
end) || "latest"
end
end
defp selected_version_sanitized_name(library, selected_versions) do
if selected_versions[library.id] in ["latest", nil, ""] do
"latest"
else
Enum.find_value(library.versions, fn version ->
version.version
if version.id == selected_versions[library.id] do
version.sanitized_version
end
end) || "latest"
end
end
defp get_extensions(library, selected_versions) do
if selected_versions[library.id] in ["latest", nil, ""] do
Enum.at(library.versions, 0).extensions
else
case Enum.find(library.versions, &(&1.id == selected_versions[library.id])) do
defp get_extensions(libraries) do
Enum.flat_map(libraries, fn library ->
library.versions
|> Enum.find(&Ash.Resource.Info.loaded?(&1, :extensions))
|> case do
nil ->
[]
version ->
version.extensions
%{extensions: extensions} ->
extensions
end
end
end)
end
end

View file

@ -110,7 +110,7 @@ defmodule AshHqWeb.Components.Search do
{#for item <- items}
<LiveRedirect to={Routes.doc_link(item, @selected_versions)} opts={id: item.id}>
<div class={
"rounded-lg mb-4 py-2 px-2 hover:bg-gray-400 dark:hover:bg-gray-600",
"rounded-lg mb-4 py-2 px-2 hover:bg-gray-300 dark:hover:bg-gray-700",
"bg-gray-400 dark:bg-gray-600": @selected_item.id == item.id,
"bg-gray-200 dark:bg-gray-800": @selected_item.id != item.id
}>

View file

@ -97,10 +97,20 @@ defmodule AshHqWeb.Pages.Docs do
class="w-full prose prose-xl max-w-6xl dark:bg-primary-black dark:prose-invert overflow-y-auto overflow-x-visible pr-8 mt-14"
phx-hook="Docs"
>
<div id="module-docs" class="w-full nav-anchor text-black dark:text-white">
<div id="module-docs" class="w-full nav-anchor text-black dark:text-white relative">
{#if @module}
<h2>{@module.name}{render_source_code_link(assigns, @module, @library, @library_version)}</h2>
{/if}
{#if @library_version}
<div class="absolute right-2 top-2 border rounded-lg flex flex-row">
<div class="border-r pl-2 pr-2 dark:text-black bg-orange-600 dark:bg-orange-600 rounded-l-lg">
{@library.name}
</div>
<div class="pl-2 pr-2 rounded-r-lg bg-gray-300 dark:bg-inherit">
{@library_version.version}
</div>
</div>
{/if}
{raw(render_replacements(assigns, @docs))}
{#if @dsl}
{#for {category, links} <- @dsl.links || %{}}
@ -316,7 +326,7 @@ defmodule AshHqWeb.Pages.Docs do
{#match functions}
<h1>{header}</h1>
{#for function <- functions}
<div class="rounded-lg bg-slate-700 bg-opacity-50 px-2">
<div class="rounded-lg bg-slate-400 dark:bg-slate-700 bg-opacity-50 px-2">
<p class="">
<div class="">
<div class="flex flex-row items-baseline">

View file

@ -8,23 +8,23 @@ defmodule AshHqWeb.AppViewLive do
alias Phoenix.LiveView.JS
require Ash.Query
data(configured_theme, :string, default: :system)
data(searching, :boolean, default: false)
data(selected_versions, :map, default: %{})
data(libraries, :list, default: [])
data(selected_types, :map, default: %{})
data(sidebar_state, :map, default: %{})
data configured_theme, :string, default: :system
data searching, :boolean, default: false
data selected_versions, :map, default: %{}
data libraries, :list, default: []
data selected_types, :map, default: %{}
data sidebar_state, :map, default: %{}
data(library, :any, default: nil)
data(extension, :any, default: nil)
data(docs, :any, default: nil)
data(library_version, :any, default: nil)
data(guide, :any, default: nil)
data(doc_path, :list, default: [])
data(dsls, :list, default: [])
data(dsl, :any, default: nil)
data(options, :list, default: [])
data(module, :any, default: nil)
data library, :any, default: nil
data extension, :any, default: nil
data docs, :any, default: nil
data library_version, :any, default: nil
data guide, :any, default: nil
data doc_path, :list, default: []
data dsls, :list, default: []
data dsl, :any, default: nil
data options, :list, default: []
data module, :any, default: nil
def render(assigns) do
~F"""
@ -172,7 +172,7 @@ defmodule AshHqWeb.AppViewLive do
new_state = Map.put(socket.assigns.sidebar_state, id, "closed")
{:noreply,
socket |> assign(:sidebar_state, new_state) |> push_event("js:sidebar-state", new_state)}
socket |> assign(:sidebar_state, new_state) |> push_event("sidebar-state", new_state)}
end
def handle_event("expand_sidebar", %{"id" => id}, socket) do

15
livebook.livemd Normal file
View file

@ -0,0 +1,15 @@
# AshHq
## Section
<!-- livebook:{"attrs":{"action":"destroy","actions":["destroy","update","read","search","create"],"resource":"AshHq.Docs.Guide","resources":["AshHq.Docs.Dsl","AshHq.Docs.Extension","AshHq.Docs.Function","AshHq.Docs.Guide","AshHq.Docs.Library","AshHq.Docs.LibraryVersion","AshHq.Docs.Module","AshHq.Docs.Option"]},"kind":"Elixir.AshLivebook.Cells.Form","livebook_object":"smart_cell"} -->
```elixir
Ash.Resource.Info.action(AshHq.Docs.Guide, :destroy)
```
## Stuff
```elixir
```

View file

@ -39,7 +39,8 @@ defmodule AshHq.MixProject do
# {:ash_postgres, "~> 0.42.0-rc.5"},
{:ash_postgres, github: "ash-project/ash_postgres"},
# {:ash_postgres, path: "../ash_postgres"},
{:ash_phoenix, github: "ash-project/ash_phoenix"},
{:ash_phoenix, github: "ash-project/ash_phoenix", override: true},
# {:ash_livebook, path: "../ash_livebook", only: [:dev]},
{:earmark, "~> 1.5.0-pre1", override: true},
{:surface, "~> 0.7.3"},
{:surface_heroicons, "~> 0.6.0"},

View file

@ -32,6 +32,7 @@
"html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"},
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
"jason": {:hex, :jason, "1.3.0", "fa6b82a934feb176263ad2df0dbd91bf633d4a46ebfdffea0c8ae82953714946", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "53fc1f51255390e0ec7e50f9cb41e751c260d065dcba2bf0d08dc51a4002c2ac"},
"kino": {:hex, :kino, "0.6.2", "3e8463ea19551f368c3dcbbf39d36b2627a33916598bfe87f51adc9aaab453fb", [:mix], [{:table, "~> 0.1.2", [hex: :table, repo: "hexpm", optional: false]}], "hexpm", "488cd83fa6efcdb4d5289c25daf842c44b33508fea048eb98f58132afc4ed513"},
"makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
"makeup_eex": {:hex, :makeup_eex, "0.1.1", "89352d5da318d97ae27bbcc87201f274504d2b71ede58ca366af6a5fbed9508d", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.16", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_html, "~> 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d111a0994eaaab09ef1a4b3b313ef806513bb4652152c26c0d7ca2be8402a964"},
"makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"},
@ -71,6 +72,7 @@
"surface": {:hex, :surface, "0.7.4", "ce9cf98a11e6572008d82b6dd1dd25fd90966d69cc72a06d69058ef3e7063df8", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.17.4", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:sourceror, "~> 0.9", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "052c2a9a35e260339ec0f9bbc667224993e7e2805c36409736f673ffe7d486ac"},
"surface_heroicons": {:hex, :surface_heroicons, "0.6.0", "04e171843439d2d52c868f8adf5294c49505f504a74a0200179e49c447d6f354", [:mix], [{:surface, ">= 0.5.0", [hex: :surface, repo: "hexpm", optional: false]}], "hexpm", "1136c88a8de44a63c050cec9b0b64f771127dfd96feabab4cd0bde8b6b727ba2"},
"swoosh": {:hex, :swoosh, "1.6.6", "6018c6f4659ac0b4f30684982993b7812b2bb97436d39f76fcfa8c9e3ae74f85", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e92c7206efd442f08484993676ab072afab2f2bb1e87e604230bb1183c5980de"},
"table": {:hex, :table, "0.1.2", "87ad1125f5b70c5dea0307aa633194083eb5182ec537efc94e96af08937e14a8", [:mix], [], "hexpm", "7e99bc7efef806315c7e65640724bf165c3061cdc5d854060f74468367065029"},
"telemetry": {:hex, :telemetry, "1.1.0", "a589817034a27eab11144ad24d5c0f9fab1f58173274b1e9bae7074af9cbee51", [:rebar3], [], "hexpm", "b727b2a1f75614774cff2d7565b64d0dfa5bd52ba517f16543e6fc7efcc0df48"},
"telemetry_metrics": {:hex, :telemetry_metrics, "0.6.1", "315d9163a1d4660aedc3fee73f33f1d355dcc76c5c3ab3d59e76e3edf80eef1f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7be9e0871c41732c233be71e4be11b96e56177bf15dde64a8ac9ce72ac9834c6"},
"telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"},