2022-03-28 10:26:35 +13:00
|
|
|
defmodule AshHqWeb.Pages.Docs do
|
2022-08-07 11:22:58 +12:00
|
|
|
@moduledoc "The page for showing documentation"
|
2022-09-13 08:47:14 +12:00
|
|
|
use Surface.LiveComponent
|
2022-03-28 10:26:35 +13:00
|
|
|
|
2022-09-13 11:36:28 +12:00
|
|
|
import AshHqWeb.Helpers
|
|
|
|
|
2022-04-01 05:36:44 +13:00
|
|
|
alias AshHqWeb.Components.{CalloutText, DocSidebar, RightNav, Tag}
|
2022-09-13 11:36:28 +12:00
|
|
|
alias AshHqWeb.Components.Docs.{DocPath, Functions, SourceLink}
|
2022-08-07 11:22:58 +12:00
|
|
|
alias AshHqWeb.DocRoutes
|
|
|
|
alias Phoenix.LiveView.JS
|
2022-11-17 08:36:05 +13:00
|
|
|
alias Surface.Components.LivePatch
|
2022-06-06 06:03:45 +12:00
|
|
|
require Logger
|
2023-01-19 11:56:07 +13:00
|
|
|
require Ash.Query
|
2022-03-28 10:26:35 +13:00
|
|
|
|
2023-02-10 10:52:58 +13:00
|
|
|
prop(change_versions, :event, required: true)
|
|
|
|
prop(selected_versions, :map, required: true)
|
|
|
|
prop(libraries, :list, default: [])
|
|
|
|
prop(uri, :string)
|
|
|
|
prop(remove_version, :event)
|
|
|
|
prop(add_version, :event)
|
|
|
|
prop(change_version, :event)
|
|
|
|
prop(params, :map, required: true)
|
|
|
|
prop(show_catalogue_call_to_action, :boolean)
|
|
|
|
|
|
|
|
data(library, :any)
|
|
|
|
data(extension, :any)
|
|
|
|
data(docs, :any)
|
|
|
|
data(library_version, :any)
|
|
|
|
data(guide, :any)
|
|
|
|
data(doc_path, :list, default: [])
|
|
|
|
data(dsls, :list, default: [])
|
|
|
|
data(dsl, :any)
|
|
|
|
data(options, :list, default: [])
|
|
|
|
data(module, :any)
|
|
|
|
data(mix_task, :any)
|
|
|
|
data(positional_options, :list)
|
|
|
|
data(description, :string)
|
|
|
|
data(title, :string)
|
|
|
|
data(sidebar_data, :any)
|
|
|
|
data(not_found, :boolean, default: false)
|
2022-08-29 00:42:24 +12:00
|
|
|
|
2022-03-28 10:26:35 +13:00
|
|
|
@spec render(any) :: Phoenix.LiveView.Rendered.t()
|
|
|
|
def render(assigns) do
|
|
|
|
~F"""
|
2022-11-16 14:27:24 +13:00
|
|
|
<div class="flex flex-col xl:flex-row justify-center">
|
2022-12-27 22:31:13 +13:00
|
|
|
<head>
|
|
|
|
<meta property="og:title" content={@title}>
|
|
|
|
<meta property="og:description" content={@description}>
|
|
|
|
</head>
|
2023-02-07 17:29:23 +13:00
|
|
|
<div class="xl:hidden sticky top-20 z-40 h-14 bg-white dark:bg-base-dark-850 flex flex-row justify-start w-full space-x-6 items-center border-b border-base-light-300 dark:border-base-dark-700 py-3">
|
|
|
|
<button phx-click={show_sidebar()}>
|
2022-03-29 11:05:19 +13:00
|
|
|
<Heroicons.Outline.MenuIcon class="w-8 h-8 ml-4" />
|
|
|
|
</button>
|
2022-10-26 17:53:18 +13:00
|
|
|
<button id={"#{@id}-hide"} class="hidden" phx-click={hide_sidebar()} />
|
2022-03-29 11:05:19 +13:00
|
|
|
{#if @doc_path && @doc_path != []}
|
2022-09-13 12:35:59 +12:00
|
|
|
<DocPath doc_path={@doc_path} />
|
2022-03-29 11:05:19 +13:00
|
|
|
{/if}
|
|
|
|
</div>
|
2022-11-16 14:27:24 +13:00
|
|
|
<span class="grid xl:hidden z-40">
|
|
|
|
<div
|
|
|
|
id="mobile-sidebar-container"
|
2023-02-07 16:54:59 +13:00
|
|
|
class="hidden fixed transition sidebar-container overflow-y-auto z-40 border-r border-b border-base-light-300 dark:border-base-dark-700"
|
|
|
|
:on-click-away={hide_sidebar()}
|
2022-11-16 14:27:24 +13:00
|
|
|
>
|
2022-03-30 05:12:28 +13:00
|
|
|
<DocSidebar
|
|
|
|
id="mobile-sidebar"
|
2023-02-07 16:54:59 +13:00
|
|
|
class="max-w-sm p-2 pr-4"
|
2022-03-30 05:12:28 +13:00
|
|
|
libraries={@libraries}
|
2023-02-07 06:35:52 +13:00
|
|
|
remove_version={@remove_version}
|
2022-03-30 05:12:28 +13:00
|
|
|
selected_versions={@selected_versions}
|
2023-02-07 06:35:52 +13:00
|
|
|
sidebar_data={@sidebar_data}
|
2022-03-30 05:12:28 +13:00
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</span>
|
2022-11-16 14:27:24 +13:00
|
|
|
<div class="grow w-full flex flex-row max-w-[1800px] justify-between md:space-x-12">
|
2022-11-16 18:29:29 +13:00
|
|
|
<div class="sidebar-container sticky overflow-y-auto overflow-x-hidden shrink-0 top-20 xl:border-r xl:border-b xl:border-base-light-300 xl:dark:border-base-dark-700 lg:pr-2 lg:pt-4">
|
2022-09-08 02:48:10 +12:00
|
|
|
<DocSidebar
|
|
|
|
id="sidebar"
|
2022-11-16 14:27:24 +13:00
|
|
|
class="hidden xl:block w-80"
|
2022-09-08 02:48:10 +12:00
|
|
|
libraries={@libraries}
|
2023-02-07 06:35:52 +13:00
|
|
|
remove_version={@remove_version}
|
2022-09-08 02:48:10 +12:00
|
|
|
selected_versions={@selected_versions}
|
2023-02-07 06:35:52 +13:00
|
|
|
sidebar_data={@sidebar_data}
|
2022-09-08 02:48:10 +12:00
|
|
|
/>
|
|
|
|
</div>
|
2022-03-30 05:12:28 +13:00
|
|
|
<div
|
|
|
|
id="docs-window"
|
2023-02-08 04:04:54 +13:00
|
|
|
:if={@not_found}
|
|
|
|
class="w-full shrink prose prose-td:pl-0 max-w-6xl bg-white dark:bg-base-dark-850 dark:prose-invert md:pr-8 md:mt-4 px-4 md:px-auto mx-auto overflow-x-auto overflow-y-hidden"
|
|
|
|
>
|
|
|
|
<div class="w-full nav-anchor text-black dark:text-white relative py-4 md:py-auto">
|
|
|
|
We couldn't find that page.
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div
|
|
|
|
id="docs-window"
|
|
|
|
:if={!@not_found}
|
2023-01-25 03:02:46 +13:00
|
|
|
class="w-full shrink prose prose-td:pl-0 max-w-6xl bg-white dark:bg-base-dark-850 dark:prose-invert md:pr-8 md:mt-4 px-4 md:px-auto mx-auto overflow-x-auto overflow-y-hidden"
|
2022-03-30 05:12:28 +13:00
|
|
|
>
|
2022-09-09 15:23:57 +12:00
|
|
|
<div
|
|
|
|
id="module-docs"
|
|
|
|
class="w-full nav-anchor text-black dark:text-white relative py-4 md:py-auto"
|
|
|
|
>
|
2023-02-01 05:13:42 +13:00
|
|
|
<.catalogue_call_to_action :if={@show_catalogue_call_to_action} />
|
2022-07-15 07:09:04 +12:00
|
|
|
{#if @module}
|
2022-09-13 12:35:59 +12:00
|
|
|
<h2>{@module.name} <SourceLink module_or_function={@module} library={@library} library_version={@library_version} /></h2>
|
2022-07-15 07:09:04 +12:00
|
|
|
{/if}
|
2022-09-30 05:56:07 +13:00
|
|
|
{#if @mix_task}
|
|
|
|
<h2>{@mix_task.name} <SourceLink
|
|
|
|
module_or_function={@mix_task}
|
|
|
|
library={@library}
|
|
|
|
library_version={@library_version}
|
|
|
|
/></h2>
|
|
|
|
{/if}
|
2023-02-15 15:20:09 +13:00
|
|
|
<.github_guide_link
|
|
|
|
:if={@guide}
|
|
|
|
guide={@guide}
|
|
|
|
library={@library}
|
|
|
|
library_version={@library_version}
|
|
|
|
/>
|
2023-01-19 14:23:09 +13:00
|
|
|
<.docs doc_path={@doc_path} docs={@docs} />
|
2022-10-26 17:28:25 +13:00
|
|
|
{#if @extension && !@dsl && !@guide}
|
|
|
|
{#case Enum.count_until(Stream.filter(@extension.dsls, &(&1.type == :section)), 2)}
|
|
|
|
{#match 0}
|
2022-10-26 17:53:18 +13:00
|
|
|
<div />
|
2022-10-26 17:28:25 +13:00
|
|
|
{#match 1}
|
2022-10-26 17:53:18 +13:00
|
|
|
<h3>
|
|
|
|
DSL
|
|
|
|
</h3>
|
2022-10-26 17:28:25 +13:00
|
|
|
{#match 2}
|
2022-10-26 17:53:18 +13:00
|
|
|
<h3>
|
|
|
|
DSL Sections
|
|
|
|
</h3>
|
2022-10-26 17:28:25 +13:00
|
|
|
{/case}
|
|
|
|
<ul>
|
|
|
|
{#for section <- Enum.filter(@extension.dsls, &(&1.type == :section))}
|
|
|
|
<li>
|
2022-11-17 08:36:05 +13:00
|
|
|
<LivePatch to={DocRoutes.doc_link(section, @selected_versions)}>{section.name}</LivePatch>
|
2022-10-26 17:28:25 +13:00
|
|
|
</li>
|
|
|
|
{/for}
|
|
|
|
</ul>
|
|
|
|
{/if}
|
2022-04-01 09:59:53 +13:00
|
|
|
</div>
|
2022-03-30 17:40:17 +13:00
|
|
|
{#if @module}
|
2022-09-16 10:35:33 +12:00
|
|
|
<Functions
|
|
|
|
header="Types"
|
|
|
|
type={:type}
|
|
|
|
functions={@module.functions}
|
|
|
|
library={@library}
|
|
|
|
library_version={@library_version}
|
|
|
|
libraries={@libraries}
|
|
|
|
selected_versions={@selected_versions}
|
|
|
|
/>
|
2022-09-13 12:35:59 +12:00
|
|
|
<Functions
|
|
|
|
header="Callbacks"
|
|
|
|
type={:callback}
|
|
|
|
functions={@module.functions}
|
|
|
|
library={@library}
|
|
|
|
library_version={@library_version}
|
|
|
|
libraries={@libraries}
|
|
|
|
selected_versions={@selected_versions}
|
|
|
|
/>
|
|
|
|
<Functions
|
|
|
|
header="Functions"
|
|
|
|
type={:function}
|
|
|
|
functions={@module.functions}
|
|
|
|
library={@library}
|
|
|
|
library_version={@library_version}
|
|
|
|
libraries={@libraries}
|
|
|
|
selected_versions={@selected_versions}
|
|
|
|
/>
|
|
|
|
<Functions
|
|
|
|
header="Macros"
|
|
|
|
type={:macro}
|
|
|
|
functions={@module.functions}
|
|
|
|
library={@library}
|
|
|
|
library_version={@library_version}
|
|
|
|
libraries={@libraries}
|
|
|
|
selected_versions={@selected_versions}
|
|
|
|
/>
|
2022-03-30 17:40:17 +13:00
|
|
|
{/if}
|
2022-07-15 07:09:04 +12:00
|
|
|
{#case modules_in_scope(@dsl, @extension, @libraries, @selected_versions)}
|
|
|
|
{#match []}
|
|
|
|
{#match imports}
|
|
|
|
<h3>Imported Modules</h3>
|
|
|
|
{#for mod <- imports}
|
|
|
|
<ul>
|
|
|
|
<li>
|
2022-11-17 08:36:05 +13:00
|
|
|
<LivePatch to={DocRoutes.doc_link(mod, @selected_versions)}>{mod.name}</LivePatch>
|
2022-07-15 07:09:04 +12:00
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
{/for}
|
|
|
|
{/case}
|
|
|
|
{#case child_dsls(@extension, @dsl)}
|
|
|
|
{#match []}
|
|
|
|
{#match children}
|
2022-07-28 07:57:39 +12:00
|
|
|
<h3>
|
|
|
|
Nested DSLs
|
|
|
|
</h3>
|
|
|
|
<ul>
|
|
|
|
{#for child <- children}
|
|
|
|
<li>
|
2022-11-17 08:36:05 +13:00
|
|
|
<LivePatch to={DocRoutes.doc_link(child, @selected_versions)}>{child.name}</LivePatch>
|
2022-07-28 07:57:39 +12:00
|
|
|
</li>
|
|
|
|
{/for}
|
|
|
|
</ul>
|
2022-07-15 07:09:04 +12:00
|
|
|
{/case}
|
|
|
|
<div class="ml-2">
|
2022-07-28 07:57:39 +12:00
|
|
|
<table>
|
|
|
|
{#if !Enum.empty?(@options)}
|
|
|
|
{#if Enum.any?(@options, & &1.argument_index)}
|
|
|
|
<td colspan="100%">
|
|
|
|
<h3>
|
|
|
|
Arguments
|
|
|
|
</h3>
|
|
|
|
</td>
|
2022-07-26 13:24:43 +12:00
|
|
|
<tr>
|
|
|
|
<th>Name</th>
|
|
|
|
<th>Type</th>
|
2023-01-15 19:48:24 +13:00
|
|
|
{#if @dsl.arg_defaults not in [%{}, nil]}
|
|
|
|
<th>Default</th>
|
|
|
|
{/if}
|
2022-07-26 13:24:43 +12:00
|
|
|
<th>Doc</th>
|
|
|
|
</tr>
|
2022-07-15 07:09:04 +12:00
|
|
|
{#for option <- positional_options(@options)}
|
2022-10-11 19:19:11 +13:00
|
|
|
<tr id={"#{option.sanitized_path}/#{option.name}"}>
|
2022-07-15 07:09:04 +12:00
|
|
|
<td>
|
|
|
|
<div class="flex flex-row items-baseline">
|
2022-11-17 08:36:05 +13:00
|
|
|
<LivePatch to={"##{DocRoutes.sanitize_name(option.name)}"}>
|
2023-01-25 03:02:46 +13:00
|
|
|
<Heroicons.Outline.LinkIcon class="h-3 w-3 mr-2" />
|
2022-11-17 08:36:05 +13:00
|
|
|
</LivePatch>
|
2022-07-28 07:57:39 +12:00
|
|
|
<div class="flex flex-row space-x-2">
|
2022-09-13 12:35:59 +12:00
|
|
|
<CalloutText text={option.name} />
|
2023-01-18 15:33:49 +13:00
|
|
|
{render_tags(assigns, %{
|
|
|
|
option
|
|
|
|
| required: option.required && !Map.has_key?(@dsl.arg_defaults || %{}, option.name)
|
|
|
|
})}
|
2022-07-28 07:57:39 +12:00
|
|
|
</div>
|
2022-07-15 07:09:04 +12:00
|
|
|
</div>
|
|
|
|
</td>
|
|
|
|
<td>
|
|
|
|
{option.type}
|
|
|
|
</td>
|
2023-01-15 19:48:24 +13:00
|
|
|
{#if @dsl.arg_defaults not in [%{}, nil]}
|
2023-01-18 15:33:49 +13:00
|
|
|
<th>{Map.get(@dsl.arg_defaults || %{}, option.name)}</th>
|
2023-01-15 19:48:24 +13:00
|
|
|
{/if}
|
2022-07-15 07:09:04 +12:00
|
|
|
<td>
|
2023-01-19 14:23:09 +13:00
|
|
|
{raw(option.doc_html)}
|
2022-07-15 07:09:04 +12:00
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
{/for}
|
2022-07-28 07:57:39 +12:00
|
|
|
{/if}
|
|
|
|
<tr>
|
|
|
|
<td colspan="100%">
|
|
|
|
<h3>
|
|
|
|
Options
|
|
|
|
</h3>
|
|
|
|
</td>
|
|
|
|
</tr>
|
2022-07-26 14:17:39 +12:00
|
|
|
<tr>
|
|
|
|
<th>Name</th>
|
|
|
|
<th>Type</th>
|
2022-12-01 12:53:11 +13:00
|
|
|
<th>Default</th>
|
2022-07-26 14:17:39 +12:00
|
|
|
<th>Doc</th>
|
|
|
|
</tr>
|
2022-07-15 07:09:04 +12:00
|
|
|
{#for %{argument_index: nil} = option <- @options}
|
2022-10-11 19:19:11 +13:00
|
|
|
<tr id={"#{option.sanitized_path}/#{option.name}"}>
|
2022-09-13 05:44:52 +12:00
|
|
|
<td id={DocRoutes.sanitize_name(option.name)}>
|
2022-03-30 17:40:17 +13:00
|
|
|
<div class="flex flex-row items-baseline">
|
2022-11-17 08:36:05 +13:00
|
|
|
<LivePatch to={"##{DocRoutes.sanitize_name(option.name)}"}>
|
2023-01-25 03:02:46 +13:00
|
|
|
<Heroicons.Outline.LinkIcon class="h-3 w-3 mr-2" />
|
2022-11-17 08:36:05 +13:00
|
|
|
</LivePatch>
|
2022-09-13 12:35:59 +12:00
|
|
|
<CalloutText text={option.name} />
|
2022-07-26 18:47:03 +12:00
|
|
|
{render_tags(assigns, option)}
|
2022-03-30 17:40:17 +13:00
|
|
|
</div>
|
|
|
|
</td>
|
|
|
|
<td>
|
|
|
|
{option.type}
|
|
|
|
</td>
|
2022-12-01 12:53:11 +13:00
|
|
|
<td>
|
|
|
|
{if option.default == "nil", do: nil, else: option.default}
|
|
|
|
</td>
|
2022-03-30 17:40:17 +13:00
|
|
|
<td>
|
2023-01-19 14:23:09 +13:00
|
|
|
{raw(option.doc_html)}
|
2022-03-30 17:40:17 +13:00
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
{/for}
|
2022-07-28 07:57:39 +12:00
|
|
|
{/if}
|
|
|
|
</table>
|
2022-07-15 07:09:04 +12:00
|
|
|
</div>
|
2022-10-23 00:51:22 +13:00
|
|
|
|
2022-11-01 20:20:09 +13:00
|
|
|
<footer class="p-2 sm:justify-center">
|
2022-10-23 00:51:22 +13:00
|
|
|
<div class="md:flex md:justify-around items-center">
|
2022-11-17 08:36:05 +13:00
|
|
|
<LivePatch to="/">
|
2022-10-23 00:51:22 +13:00
|
|
|
<img class="h-6 md:h-10 hidden dark:block" src="/images/ash-framework-dark.png">
|
|
|
|
<img class="h-6 md:h-10 dark:hidden" src="/images/ash-framework-light.png">
|
2022-11-17 08:36:05 +13:00
|
|
|
</LivePatch>
|
2022-10-23 00:51:22 +13:00
|
|
|
<a href="https://github.com/ash-project" class="hover:underline">Source</a>
|
|
|
|
<a href="https://github.com/ash-project/ash_hq/issues/new/choose" class="hover:underline">Report an issue</a>
|
|
|
|
</div>
|
|
|
|
</footer>
|
2022-03-28 10:26:35 +13:00
|
|
|
</div>
|
2022-04-01 05:36:44 +13:00
|
|
|
{#if @module}
|
2022-11-16 14:27:24 +13:00
|
|
|
<div class="sidebar-container hidden lg:block lg:w-80 sticky top-36 xl:top-20 shrink-0 overflow-y-auto overflow-x-hidden dark:bg-base-dark-850 bg-opacity-70 mt-4">
|
2022-04-02 08:11:17 +13:00
|
|
|
<RightNav functions={@module.functions} module={@module.name} />
|
2022-04-01 05:36:44 +13:00
|
|
|
</div>
|
2022-07-28 07:57:39 +12:00
|
|
|
{#else}
|
2022-11-16 14:27:24 +13:00
|
|
|
<!-- empty div to preserve flex row spacing -->
|
2022-07-28 07:57:39 +12:00
|
|
|
<div />
|
2022-04-01 05:36:44 +13:00
|
|
|
{/if}
|
2022-03-28 10:26:35 +13:00
|
|
|
</div>
|
2022-03-30 05:12:28 +13:00
|
|
|
</div>
|
|
|
|
"""
|
|
|
|
end
|
|
|
|
|
2023-02-15 15:20:09 +13:00
|
|
|
def github_guide_link(assigns) do
|
|
|
|
~F"""
|
|
|
|
<a
|
|
|
|
href={source_link(@guide, @library, @library_version)}
|
|
|
|
target="_blank"
|
|
|
|
class="float-right no-underline mt-1 ml-2 hidden lg:block"
|
|
|
|
>
|
|
|
|
<svg
|
|
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
|
|
class="w-6 h-6 inline-block mr-1 -mt-1 dark:fill-white fill-base-light-600"
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
>
|
|
|
|
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
|
|
|
|
</svg>
|
|
|
|
<span class="underline">View this guide on GitHub</span>
|
|
|
|
<Heroicons.Outline.ExternalLinkIcon class="w-6 h-6 inline-block -mt-1" />
|
|
|
|
</a>
|
|
|
|
"""
|
|
|
|
end
|
|
|
|
|
2023-01-19 14:23:09 +13:00
|
|
|
def docs(assigns) do
|
|
|
|
~F"""
|
|
|
|
<div id={docs_container_id(@doc_path)}>
|
2023-01-19 16:14:58 +13:00
|
|
|
{raw(@docs)}
|
2023-01-19 14:23:09 +13:00
|
|
|
</div>
|
|
|
|
"""
|
|
|
|
end
|
|
|
|
|
2023-02-01 05:13:42 +13:00
|
|
|
def catalogue_call_to_action(assigns) do
|
|
|
|
~F"""
|
|
|
|
<div
|
|
|
|
class="bg-blue-500 text-white px-4 py-2 mb-8 cursor-pointer flex justify-between items-center"
|
|
|
|
id="catalogue-call-to-action"
|
|
|
|
:on-click={AshHqWeb.AppViewLive.toggle_catalogue()}
|
|
|
|
>
|
|
|
|
<span>View the full range of Ash libraries for authentication, soft deletion, GraphQL and more</span>
|
|
|
|
<button
|
|
|
|
id="close-search"
|
|
|
|
class="h-6 w-6 cursor-pointer"
|
|
|
|
:on-click="dismiss_catalogue_call_to_action"
|
|
|
|
>
|
|
|
|
<Heroicons.Outline.XIcon class="h-6 w-6" />
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
"""
|
|
|
|
end
|
|
|
|
|
2022-09-13 08:47:14 +12:00
|
|
|
def update(assigns, socket) do
|
2023-01-19 11:56:07 +13:00
|
|
|
if socket.assigns[:loaded_once?] &&
|
|
|
|
assigns[:selected_versions] == socket.assigns[:selected_versions] do
|
|
|
|
{:ok, socket |> assign(Map.delete(assigns, :libraries)) |> load_docs()}
|
2022-09-30 05:56:07 +13:00
|
|
|
else
|
|
|
|
{:ok,
|
|
|
|
socket
|
2023-01-19 11:56:07 +13:00
|
|
|
|> assign(assigns)
|
|
|
|
|> assign_libraries()
|
|
|
|
|> load_docs()
|
2023-02-07 06:35:52 +13:00
|
|
|
|> assign_sidebar_content()
|
2023-01-19 11:56:07 +13:00
|
|
|
|> assign(:loaded_once?, true)}
|
2022-09-30 05:56:07 +13:00
|
|
|
end
|
2022-09-13 08:47:14 +12:00
|
|
|
end
|
|
|
|
|
2022-07-15 07:09:04 +12:00
|
|
|
defp modules_in_scope(nil, _, _, _), do: []
|
|
|
|
defp modules_in_scope(_, nil, _, _), do: []
|
|
|
|
|
|
|
|
defp modules_in_scope(dsl, %{dsls: dsls}, libraries, selected_versions) do
|
|
|
|
dsl_path = dsl.path ++ [dsl.name]
|
|
|
|
|
|
|
|
dsls
|
|
|
|
|> Enum.filter(fn potential_parent ->
|
|
|
|
List.starts_with?(dsl_path, potential_parent.path ++ [potential_parent.name])
|
|
|
|
end)
|
|
|
|
|> Enum.flat_map(fn dsl ->
|
|
|
|
dsl.imports || []
|
|
|
|
end)
|
|
|
|
|> Enum.flat_map(fn mod_name ->
|
2022-08-07 11:22:58 +12:00
|
|
|
case find_module(libraries, selected_versions, mod_name) do
|
2022-07-15 07:09:04 +12:00
|
|
|
nil ->
|
|
|
|
Logger.warn("No such module found called #{inspect(mod_name)}")
|
|
|
|
[]
|
|
|
|
|
|
|
|
module ->
|
|
|
|
[module]
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
2022-08-07 11:22:58 +12:00
|
|
|
defp find_module(libraries, selected_versions, mod_name) do
|
|
|
|
Enum.find_value(libraries, fn library ->
|
2022-10-11 19:24:13 +13:00
|
|
|
version = selected_version(library, selected_versions[library.id])
|
|
|
|
|
|
|
|
if version do
|
|
|
|
Enum.find(version.modules, &(&1.name == mod_name))
|
|
|
|
end
|
2022-08-07 11:22:58 +12:00
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
2022-10-11 19:24:13 +13:00
|
|
|
defp selected_version(library, selected_version) do
|
|
|
|
if selected_version == "latest" do
|
|
|
|
latest_version(library)
|
|
|
|
else
|
|
|
|
Enum.find(library.versions, &(&1.id == selected_version))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-07-15 07:09:04 +12:00
|
|
|
defp child_dsls(_, nil), do: []
|
|
|
|
defp child_dsls(nil, _), do: []
|
|
|
|
|
|
|
|
defp child_dsls(%{dsls: dsls}, dsl) do
|
|
|
|
dsl_path = dsl.path ++ [dsl.name]
|
|
|
|
dsl_path_count = Enum.count(dsl_path)
|
|
|
|
|
|
|
|
Enum.filter(dsls, fn potential_child ->
|
|
|
|
potential_child_path = potential_child.path ++ [potential_child.name]
|
|
|
|
|
|
|
|
List.starts_with?(potential_child_path, dsl_path) &&
|
|
|
|
Enum.count(potential_child_path) - dsl_path_count == 1
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
defp positional_options(options) do
|
|
|
|
options
|
|
|
|
|> Enum.filter(& &1.argument_index)
|
|
|
|
|> Enum.sort_by(& &1.argument_index)
|
|
|
|
end
|
|
|
|
|
2022-03-30 05:12:28 +13:00
|
|
|
def path_to_name(path, name) do
|
2022-08-07 11:22:58 +12:00
|
|
|
Enum.map_join(path ++ [name], "-", &DocRoutes.sanitize_name/1)
|
2022-03-30 05:12:28 +13:00
|
|
|
end
|
|
|
|
|
|
|
|
defp render_tags(assigns, option) do
|
|
|
|
~F"""
|
|
|
|
{#if option.required}
|
|
|
|
<Tag color={:red}>
|
|
|
|
Required
|
|
|
|
</Tag>
|
|
|
|
{/if}
|
|
|
|
"""
|
|
|
|
end
|
|
|
|
|
2022-08-07 11:22:58 +12:00
|
|
|
def show_sidebar(js \\ %JS{}) do
|
|
|
|
js
|
2022-03-29 11:05:19 +13:00
|
|
|
|> JS.toggle(
|
|
|
|
to: "#mobile-sidebar-container",
|
2022-03-30 05:12:28 +13:00
|
|
|
in: {
|
|
|
|
"transition ease-in duration-100",
|
|
|
|
"opacity-0",
|
|
|
|
"opacity-100"
|
|
|
|
},
|
|
|
|
out: {
|
|
|
|
"transition ease-out duration-75",
|
|
|
|
"opacity-100",
|
|
|
|
"opacity-0"
|
|
|
|
}
|
2022-03-29 11:05:19 +13:00
|
|
|
)
|
2022-03-28 10:26:35 +13:00
|
|
|
end
|
|
|
|
|
2022-10-26 17:28:25 +13:00
|
|
|
def hide_sidebar(js \\ %JS{}) do
|
|
|
|
js
|
|
|
|
|> JS.hide(
|
|
|
|
to: "#mobile-sidebar-container",
|
|
|
|
transition: {
|
|
|
|
"transition ease-out duration-75",
|
|
|
|
"opacity-100",
|
|
|
|
"opacity-0"
|
|
|
|
}
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
2023-01-19 11:56:07 +13:00
|
|
|
def assign_libraries(socket) do
|
2022-09-13 12:35:59 +12:00
|
|
|
socket = assign_library(socket)
|
2022-07-15 07:09:04 +12:00
|
|
|
|
2022-09-30 05:56:07 +13:00
|
|
|
guides_query =
|
|
|
|
AshHq.Docs.Guide
|
|
|
|
|> Ash.Query.new()
|
2023-01-19 11:56:07 +13:00
|
|
|
|> load_for_search()
|
2022-09-30 05:56:07 +13:00
|
|
|
|
|
|
|
modules_query =
|
|
|
|
AshHq.Docs.Module
|
|
|
|
|> Ash.Query.sort(order: :asc)
|
2023-01-19 11:56:07 +13:00
|
|
|
|> load_for_search()
|
2022-09-30 05:56:07 +13:00
|
|
|
|
|
|
|
mix_tasks_query =
|
|
|
|
AshHq.Docs.MixTask
|
|
|
|
|> Ash.Query.sort(order: :asc)
|
2023-01-19 11:56:07 +13:00
|
|
|
|> load_for_search()
|
2022-09-30 05:56:07 +13:00
|
|
|
|
|
|
|
extensions_query =
|
|
|
|
AshHq.Docs.Extension
|
|
|
|
|> Ash.Query.sort(order: :asc)
|
2023-01-19 11:56:07 +13:00
|
|
|
|> load_for_search()
|
2022-09-30 05:56:07 +13:00
|
|
|
|
2022-09-13 08:47:14 +12:00
|
|
|
new_libraries =
|
|
|
|
socket.assigns.libraries
|
2022-09-30 05:56:07 +13:00
|
|
|
|> Enum.flat_map(fn library ->
|
|
|
|
latest_version = AshHqWeb.Helpers.latest_version(library)
|
|
|
|
|
|
|
|
Enum.filter(library.versions, fn version ->
|
|
|
|
(latest_version && version.id == latest_version.id) ||
|
|
|
|
version.id == socket.assigns[:selected_versions][library.id] ||
|
|
|
|
(socket.assigns[:library_version] &&
|
|
|
|
socket.assigns[:library_version].id == version.id) ||
|
|
|
|
(socket.assigns.params["version"] &&
|
|
|
|
socket.assigns.params["version"] ==
|
|
|
|
version.version)
|
|
|
|
end)
|
|
|
|
end)
|
|
|
|
|> AshHq.Docs.load!(
|
|
|
|
[
|
|
|
|
extensions: extensions_query,
|
|
|
|
guides: guides_query,
|
|
|
|
modules: modules_query,
|
|
|
|
mix_tasks: mix_tasks_query
|
|
|
|
],
|
|
|
|
lazy?: true
|
|
|
|
)
|
|
|
|
|> Enum.reduce(socket.assigns.libraries, fn library_version, libraries ->
|
|
|
|
Enum.map(libraries, fn library ->
|
|
|
|
if library.id == library_version.library_id do
|
|
|
|
Map.update!(library, :versions, fn versions ->
|
|
|
|
Enum.map(versions, fn current_version ->
|
|
|
|
if current_version.id == library_version.id do
|
|
|
|
library_version
|
|
|
|
else
|
|
|
|
current_version
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
end)
|
|
|
|
else
|
|
|
|
library
|
|
|
|
end
|
2022-09-13 08:47:14 +12:00
|
|
|
end)
|
|
|
|
end)
|
|
|
|
|
2023-01-19 11:56:07 +13:00
|
|
|
assign(socket, :libraries, new_libraries)
|
|
|
|
end
|
|
|
|
|
|
|
|
def load_docs(socket) do
|
2022-09-13 08:47:14 +12:00
|
|
|
socket
|
|
|
|
|> assign_library()
|
|
|
|
|> assign_extension()
|
|
|
|
|> assign_guide()
|
|
|
|
|> assign_module()
|
2022-09-30 05:56:07 +13:00
|
|
|
|> assign_mix_task()
|
2022-09-13 08:47:14 +12:00
|
|
|
|> assign_dsl()
|
2022-11-04 05:20:51 +13:00
|
|
|
|> assign_fallback_guide()
|
2022-09-13 08:47:14 +12:00
|
|
|
|> assign_docs()
|
|
|
|
end
|
|
|
|
|
2023-02-07 06:35:52 +13:00
|
|
|
defp assign_sidebar_content(socket) do
|
|
|
|
sidebar_data = [
|
|
|
|
%{
|
|
|
|
name: "Guides",
|
|
|
|
id: "guides",
|
|
|
|
categories:
|
|
|
|
guides_by_category_and_library(
|
|
|
|
socket.assigns[:libraries],
|
|
|
|
socket.assigns[:library_version],
|
|
|
|
socket.assigns[:selected_versions],
|
|
|
|
socket.assigns[:guide]
|
|
|
|
)
|
|
|
|
},
|
|
|
|
%{
|
|
|
|
name: "DSLs & Extensions",
|
|
|
|
id: "dsls",
|
|
|
|
categories:
|
|
|
|
get_extensions(
|
|
|
|
socket.assigns[:libraries],
|
|
|
|
socket.assigns[:library_version],
|
|
|
|
socket.assigns[:selected_versions],
|
|
|
|
socket.assigns[:extension]
|
|
|
|
)
|
|
|
|
},
|
|
|
|
%{
|
|
|
|
name: "Code",
|
|
|
|
id: "code",
|
|
|
|
categories:
|
|
|
|
modules_by_category_and_library(
|
|
|
|
socket.assigns[:libraries],
|
|
|
|
socket.assigns[:library_version],
|
|
|
|
socket.assigns[:selected_versions],
|
|
|
|
socket.assigns[:module]
|
|
|
|
)
|
|
|
|
},
|
|
|
|
%{
|
|
|
|
name: "Mix Tasks",
|
|
|
|
id: "mix-tasks",
|
|
|
|
categories:
|
|
|
|
mix_tasks_by_category_and_library(
|
|
|
|
socket.assigns[:libraries],
|
|
|
|
socket.assigns[:library_version],
|
|
|
|
socket.assigns[:selected_versions],
|
|
|
|
socket.assigns[:mix_task]
|
|
|
|
)
|
|
|
|
}
|
|
|
|
]
|
|
|
|
|
|
|
|
assign(socket, sidebar_libraries: socket.assigns.libraries, sidebar_data: sidebar_data)
|
|
|
|
end
|
|
|
|
|
|
|
|
@start_guides ["Tutorials", "Topics", "How To", "Misc"]
|
|
|
|
|
|
|
|
defp guides_by_category_and_library(libraries, library_version, selected_versions, active_guide) 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, fn guide ->
|
|
|
|
%{
|
|
|
|
id: guide.id,
|
|
|
|
name: guide.name,
|
|
|
|
to: DocRoutes.doc_link(guide, selected_versions),
|
|
|
|
active?: active_guide && active_guide.id == guide.id
|
|
|
|
}
|
|
|
|
end)
|
|
|
|
|> Enum.map(fn {category, guides} -> {category, {library.display_name, guides}} end)
|
|
|
|
end)
|
|
|
|
|> Enum.group_by(&elem(&1, 0), &elem(&1, 1))
|
|
|
|
|> partially_alphabetically_sort(@start_guides, [])
|
|
|
|
end
|
|
|
|
|
|
|
|
@last_categories ["Errors"]
|
|
|
|
|
|
|
|
defp modules_by_category_and_library(
|
|
|
|
libraries,
|
|
|
|
library_version,
|
|
|
|
selected_versions,
|
|
|
|
active_module
|
|
|
|
) 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, %{modules: modules}} ->
|
|
|
|
modules
|
|
|
|
|> Enum.sort_by(& &1.order)
|
|
|
|
|> Enum.group_by(& &1.category, fn module ->
|
|
|
|
%{
|
|
|
|
id: module.id,
|
|
|
|
name: module.name,
|
|
|
|
to: DocRoutes.doc_link(module, selected_versions),
|
|
|
|
active?: active_module && active_module.id == module.id
|
|
|
|
}
|
|
|
|
end)
|
|
|
|
|> Enum.map(fn {category, modules} -> {category, {library.display_name, modules}} end)
|
|
|
|
end)
|
|
|
|
|> Enum.group_by(&elem(&1, 0), &elem(&1, 1))
|
|
|
|
|> partially_alphabetically_sort([], @last_categories)
|
|
|
|
end
|
|
|
|
|
|
|
|
defp mix_tasks_by_category_and_library(
|
|
|
|
libraries,
|
|
|
|
library_version,
|
|
|
|
selected_versions,
|
|
|
|
active_mix_task
|
|
|
|
) 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, %{mix_tasks: mix_tasks}} ->
|
|
|
|
mix_tasks
|
|
|
|
|> Enum.sort_by(& &1.order)
|
|
|
|
|> Enum.group_by(& &1.category, fn mix_task ->
|
|
|
|
%{
|
|
|
|
id: mix_task.id,
|
|
|
|
name: mix_task.name,
|
|
|
|
to: DocRoutes.doc_link(mix_task, selected_versions),
|
|
|
|
active?: active_mix_task && active_mix_task.id == mix_task.id
|
|
|
|
}
|
|
|
|
end)
|
|
|
|
|> Enum.map(fn {category, mix_tasks} -> {category, {library.display_name, mix_tasks}} end)
|
|
|
|
end)
|
|
|
|
|> Enum.group_by(&elem(&1, 0), &elem(&1, 1))
|
|
|
|
|> partially_alphabetically_sort([], @last_categories)
|
|
|
|
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
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
@target_order ["Ash.Resource", "Ash.Api", "Ash.Flow", "Ash.Registry"]
|
|
|
|
|
|
|
|
defp get_extensions(libraries, library_version, selected_versions, active_extension) 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, %{extensions: extensions}} ->
|
|
|
|
extensions
|
|
|
|
|> Enum.sort_by(& &1.order)
|
|
|
|
|> Enum.group_by(& &1.target, fn extension ->
|
|
|
|
%{
|
|
|
|
id: extension.id,
|
|
|
|
name: extension.module || extension.name,
|
|
|
|
to: DocRoutes.doc_link(extension, selected_versions),
|
|
|
|
active?: active_extension && active_extension.id == extension.id
|
|
|
|
}
|
|
|
|
end)
|
|
|
|
|> Enum.map(fn {target, extensions} -> {target, {library.display_name, extensions}} end)
|
|
|
|
end)
|
|
|
|
|> Enum.group_by(&elem(&1, 0), &elem(&1, 1))
|
|
|
|
|> partially_alphabetically_sort(@target_order, [])
|
|
|
|
end
|
|
|
|
|
2022-11-04 05:20:51 +13:00
|
|
|
defp assign_fallback_guide(socket) do
|
|
|
|
if socket.assigns[:library_version] &&
|
|
|
|
!(socket.assigns[:dsl] || socket.assigns[:mix_task] || socket.assigns[:guide] ||
|
|
|
|
socket.assigns[:extension] || socket.assigns[:module]) do
|
|
|
|
guide =
|
|
|
|
Enum.find(socket.assigns.library_version.guides, fn guide ->
|
|
|
|
guide.default
|
|
|
|
end) ||
|
|
|
|
Enum.find(socket.assigns.library_version.guides, fn guide ->
|
|
|
|
String.contains?(guide.sanitized_name, "started")
|
|
|
|
end) || Enum.at(socket.assigns.library_version.guides, 0)
|
|
|
|
|
2023-01-19 11:56:07 +13:00
|
|
|
guide = guide |> reselect!(:text_html)
|
2022-11-04 05:20:51 +13:00
|
|
|
|
|
|
|
assign(socket, guide: guide)
|
|
|
|
else
|
|
|
|
socket
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-01-19 11:56:07 +13:00
|
|
|
defp reselect!(%resource{} = record, field) do
|
|
|
|
if Ash.Resource.selected?(record, field) do
|
|
|
|
record
|
|
|
|
else
|
|
|
|
# will blow up if pkey is not an id or if its not a docs resource
|
|
|
|
# but w/e
|
|
|
|
value =
|
|
|
|
resource
|
|
|
|
|> Ash.Query.select(field)
|
|
|
|
|> Ash.Query.filter(id == ^record.id)
|
|
|
|
|> AshHq.Docs.read_one!()
|
|
|
|
|> Map.get(field)
|
|
|
|
|
|
|
|
Map.put(record, field, value)
|
|
|
|
end
|
2022-09-13 08:47:14 +12:00
|
|
|
end
|
|
|
|
|
2023-01-19 11:56:07 +13:00
|
|
|
defp reselect_and_get!(record, field) do
|
|
|
|
record
|
|
|
|
|> reselect!(field)
|
|
|
|
|> Map.get(field)
|
2022-09-13 08:47:14 +12:00
|
|
|
end
|
|
|
|
|
2023-01-19 11:56:07 +13:00
|
|
|
defp load_for_search(query) do
|
2022-09-16 10:35:33 +12:00
|
|
|
query
|
2023-01-19 11:56:07 +13:00
|
|
|
|> Ash.Query.load(AshHq.Docs.Extensions.Search.load_for_search(query.resource))
|
|
|
|
|> deselect_doc_attributes()
|
2022-09-13 08:47:14 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
defp deselect_doc_attributes(query) do
|
|
|
|
query.resource
|
|
|
|
|> AshHq.Docs.Extensions.RenderMarkdown.render_attributes()
|
|
|
|
|> Enum.reduce(query, fn {source, target}, query ->
|
|
|
|
Ash.Query.deselect(query, [source, target])
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
defp assign_library(socket) do
|
|
|
|
case Enum.find(
|
|
|
|
socket.assigns.libraries,
|
|
|
|
&(&1.name == socket.assigns.params["library"])
|
|
|
|
) do
|
|
|
|
nil ->
|
2023-02-08 04:04:54 +13:00
|
|
|
assign(socket,
|
|
|
|
not_found: true,
|
|
|
|
library: Enum.find(socket.assigns.libraries, &(&1.name == "ash"))
|
|
|
|
)
|
2022-09-13 08:47:14 +12:00
|
|
|
|
|
|
|
library ->
|
|
|
|
socket =
|
|
|
|
if socket.assigns[:params]["version"] do
|
|
|
|
library_version =
|
|
|
|
case socket.assigns[:params]["version"] do
|
|
|
|
"latest" ->
|
|
|
|
AshHqWeb.Helpers.latest_version(library)
|
|
|
|
|
|
|
|
version ->
|
|
|
|
Enum.find(
|
|
|
|
library.versions,
|
|
|
|
&(&1.version == version)
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
2023-02-08 04:04:54 +13:00
|
|
|
if is_nil(library_version) do
|
|
|
|
assign(socket,
|
|
|
|
not_found: true,
|
|
|
|
library_version: AshHqWeb.Helpers.latest_version(library)
|
|
|
|
)
|
|
|
|
else
|
2022-09-13 08:47:14 +12:00
|
|
|
socket =
|
|
|
|
assign(
|
|
|
|
socket,
|
|
|
|
library_version: library_version
|
|
|
|
)
|
|
|
|
|
|
|
|
if socket.assigns.params["version"] != "latest" &&
|
|
|
|
(!socket.assigns[:library] ||
|
|
|
|
socket.assigns.params["library"] !=
|
|
|
|
socket.assigns.library.name) do
|
|
|
|
new_selected_versions =
|
|
|
|
Map.put(socket.assigns.selected_versions, library.id, library_version.id)
|
|
|
|
|
|
|
|
socket
|
|
|
|
|> assign(selected_versions: new_selected_versions)
|
|
|
|
|> push_event("selected-versions", new_selected_versions)
|
|
|
|
else
|
2023-02-08 08:18:13 +13:00
|
|
|
socket
|
2022-09-13 08:47:14 +12:00
|
|
|
end
|
|
|
|
end
|
|
|
|
else
|
|
|
|
assign(socket, :library_version, nil)
|
|
|
|
end
|
|
|
|
|
|
|
|
assign(socket, :library, library)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
defp assign_extension(socket) do
|
|
|
|
if socket.assigns.library_version && socket.assigns[:params]["extension"] do
|
2023-02-07 08:22:49 +13:00
|
|
|
extension =
|
|
|
|
Enum.find(socket.assigns.library_version.extensions, fn extension ->
|
|
|
|
extension.sanitized_name == socket.assigns[:params]["extension"] ||
|
2023-02-12 11:15:36 +13:00
|
|
|
AshHqWeb.DocRoutes.sanitize_name(extension.module) ==
|
2023-02-07 08:22:49 +13:00
|
|
|
socket.assigns[:params]["extension"]
|
2023-02-10 10:52:58 +13:00
|
|
|
end)
|
2023-02-07 08:22:49 +13:00
|
|
|
|
2023-02-10 10:52:58 +13:00
|
|
|
if is_nil(extension) do
|
|
|
|
assign(socket, extension: nil, not_found: true)
|
|
|
|
else
|
|
|
|
dsls_query =
|
|
|
|
AshHq.Docs.Dsl
|
|
|
|
|> Ash.Query.sort(order: :asc)
|
|
|
|
|> load_for_search()
|
2023-02-08 04:04:54 +13:00
|
|
|
|
2023-02-10 10:52:58 +13:00
|
|
|
options_query =
|
|
|
|
AshHq.Docs.Option
|
|
|
|
|> Ash.Query.sort(order: :asc)
|
|
|
|
|> load_for_search()
|
2023-02-08 04:04:54 +13:00
|
|
|
|
2023-02-10 10:52:58 +13:00
|
|
|
extension = AshHq.Docs.load!(extension, dsls: dsls_query, options: options_query)
|
2022-09-13 08:47:14 +12:00
|
|
|
|
2023-02-10 10:52:58 +13:00
|
|
|
assign(socket,
|
|
|
|
extension: extension
|
|
|
|
)
|
|
|
|
end
|
2022-09-13 08:47:14 +12:00
|
|
|
else
|
2023-01-18 15:33:49 +13:00
|
|
|
if socket.assigns.library_version && socket.assigns[:params]["module"] do
|
2023-02-07 08:22:49 +13:00
|
|
|
extension =
|
|
|
|
Enum.find(socket.assigns.library_version.extensions, fn extension ->
|
|
|
|
extension.sanitized_name == socket.assigns[:params]["module"] ||
|
|
|
|
AshHqWeb.DocRoutes.sanitize_name(extension.target) ==
|
|
|
|
socket.assigns[:params]["module"]
|
|
|
|
end)
|
|
|
|
|
|
|
|
extension =
|
|
|
|
if extension do
|
|
|
|
dsls_query =
|
|
|
|
AshHq.Docs.Dsl
|
|
|
|
|> Ash.Query.sort(order: :asc)
|
|
|
|
|> load_for_search()
|
|
|
|
|
|
|
|
options_query =
|
|
|
|
AshHq.Docs.Option
|
|
|
|
|> Ash.Query.sort(order: :asc)
|
|
|
|
|> load_for_search()
|
|
|
|
|
|
|
|
AshHq.Docs.load!(extension, dsls: dsls_query, options: options_query)
|
|
|
|
end
|
2023-01-18 15:33:49 +13:00
|
|
|
|
|
|
|
assign(socket,
|
2023-02-07 08:22:49 +13:00
|
|
|
extension: extension
|
2023-01-18 15:33:49 +13:00
|
|
|
)
|
|
|
|
else
|
|
|
|
assign(socket, :extension, nil)
|
|
|
|
end
|
2022-09-13 08:47:14 +12:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
defp assign_guide(socket) do
|
2022-11-03 20:58:14 +13:00
|
|
|
guide_route = socket.assigns[:params]["guide"]
|
|
|
|
|
2022-09-13 08:47:14 +12:00
|
|
|
guide =
|
2022-11-03 20:58:14 +13:00
|
|
|
if guide_route && socket.assigns.library_version do
|
|
|
|
guide_route = Enum.map(List.wrap(guide_route), &String.trim_trailing(&1, ".md"))
|
|
|
|
|
2022-09-13 08:47:14 +12:00
|
|
|
Enum.find(socket.assigns.library_version.guides, fn guide ->
|
2022-11-03 20:58:14 +13:00
|
|
|
matches_path?(guide, guide_route) || matches_name?(guide, guide_route)
|
2022-09-13 08:47:14 +12:00
|
|
|
end)
|
|
|
|
end
|
|
|
|
|
|
|
|
assign(socket, :guide, guide)
|
|
|
|
end
|
|
|
|
|
2022-11-03 20:58:14 +13:00
|
|
|
defp matches_path?(guide, guide_route) do
|
2022-12-15 21:28:03 +13:00
|
|
|
guide.sanitized_route == DocRoutes.sanitize_name(Enum.join(guide_route, "/"), true)
|
2022-11-03 20:58:14 +13:00
|
|
|
end
|
|
|
|
|
2022-12-15 21:28:03 +13:00
|
|
|
defp matches_name?(guide, list) do
|
|
|
|
guide_name = List.last(list)
|
|
|
|
DocRoutes.sanitize_name(guide.name) == DocRoutes.sanitize_name(guide_name)
|
2022-11-03 20:58:14 +13:00
|
|
|
end
|
|
|
|
|
2022-09-13 08:47:14 +12:00
|
|
|
defp assign_dsl(socket) do
|
|
|
|
case socket.assigns[:params]["dsl_path"] do
|
|
|
|
nil ->
|
|
|
|
assign(socket, :dsl, nil)
|
|
|
|
|
|
|
|
path ->
|
2023-02-10 10:52:58 +13:00
|
|
|
if socket.assigns.extension do
|
|
|
|
path = Enum.join(path, "/")
|
|
|
|
|
|
|
|
dsl =
|
|
|
|
Enum.find(
|
|
|
|
socket.assigns.extension.dsls,
|
|
|
|
&(&1.sanitized_path == path)
|
|
|
|
)
|
|
|
|
|
|
|
|
if is_nil(dsl) do
|
|
|
|
assign(socket, dsl: nil, not_found: true)
|
|
|
|
else
|
|
|
|
socket
|
|
|
|
|> assign(
|
|
|
|
:dsl,
|
|
|
|
dsl
|
|
|
|
)
|
|
|
|
end
|
|
|
|
else
|
|
|
|
assign(socket, dsl: nil, not_found: true)
|
|
|
|
end
|
2022-09-13 08:47:14 +12:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
defp assign_module(socket) do
|
2023-01-18 15:33:49 +13:00
|
|
|
if !socket.assigns.extension && socket.assigns.library && socket.assigns.library_version &&
|
2022-09-13 08:47:14 +12:00
|
|
|
socket.assigns[:params]["module"] do
|
|
|
|
module =
|
|
|
|
Enum.find(
|
|
|
|
socket.assigns.library_version.modules,
|
|
|
|
&(&1.sanitized_name == socket.assigns[:params]["module"])
|
2023-02-10 10:52:58 +13:00
|
|
|
)
|
|
|
|
|
|
|
|
if is_nil(module) do
|
|
|
|
assign(socket, module: nil, not_found: true)
|
|
|
|
else
|
|
|
|
functions_query =
|
|
|
|
AshHq.Docs.Function
|
|
|
|
|> Ash.Query.sort(name: :asc, arity: :asc)
|
|
|
|
|> load_for_search()
|
|
|
|
|> Ash.Query.load(:doc_html)
|
|
|
|
|
|
|
|
assign(socket,
|
|
|
|
module: AshHq.Docs.load!(module, [functions: functions_query], lazy?: true)
|
|
|
|
)
|
|
|
|
end
|
2022-09-13 08:47:14 +12:00
|
|
|
else
|
|
|
|
assign(socket, :module, nil)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-09-30 05:56:07 +13:00
|
|
|
defp assign_mix_task(socket) do
|
|
|
|
if socket.assigns.library && socket.assigns.library_version &&
|
|
|
|
socket.assigns[:params]["mix_task"] do
|
|
|
|
mix_task =
|
|
|
|
Enum.find(
|
|
|
|
socket.assigns.library_version.mix_tasks,
|
|
|
|
&(&1.sanitized_name == socket.assigns[:params]["mix_task"])
|
2023-02-10 10:52:58 +13:00
|
|
|
)
|
2022-09-30 05:56:07 +13:00
|
|
|
|
2023-02-10 10:52:58 +13:00
|
|
|
if mix_task do
|
|
|
|
assign(socket,
|
|
|
|
mix_task: mix_task
|
|
|
|
)
|
|
|
|
else
|
|
|
|
assign(socket, mix_task: nil, not_found: true)
|
|
|
|
end
|
2022-09-30 05:56:07 +13:00
|
|
|
else
|
|
|
|
assign(socket, :mix_task, nil)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-09-13 08:47:14 +12:00
|
|
|
defp assign_docs(socket) do
|
|
|
|
cond do
|
|
|
|
socket.assigns.module ->
|
2023-01-18 15:33:49 +13:00
|
|
|
send(self(), {:page_title, socket.assigns.module.name})
|
|
|
|
|
2022-09-13 08:47:14 +12:00
|
|
|
assign(socket,
|
2023-01-19 11:56:07 +13:00
|
|
|
docs: socket.assigns.module |> reselect_and_get!(:doc_html),
|
2022-12-27 22:31:13 +13:00
|
|
|
title: "Module: #{socket.assigns.module.name}",
|
|
|
|
description: "View the documentation for #{socket.assigns.module.name} on Ash HQ.",
|
2022-09-13 08:47:14 +12:00
|
|
|
doc_path: [socket.assigns.library.name, socket.assigns.module.name],
|
|
|
|
options: []
|
|
|
|
)
|
2022-09-30 05:56:07 +13:00
|
|
|
|
|
|
|
socket.assigns.mix_task ->
|
2023-01-18 17:53:21 +13:00
|
|
|
send(self(), {:page_title, socket.assigns.mix_task.name})
|
2023-01-18 15:33:49 +13:00
|
|
|
|
2022-09-30 05:56:07 +13:00
|
|
|
assign(socket,
|
2023-01-19 11:56:07 +13:00
|
|
|
docs: socket.assigns.mix_task |> reselect_and_get!(:doc_html),
|
2022-12-27 22:31:13 +13:00
|
|
|
title: "Mix Task: #{socket.assigns.mix_task.name}",
|
|
|
|
description: "View the documentation for #{socket.assigns.mix_task.name} on Ash HQ.",
|
2022-09-30 05:56:07 +13:00
|
|
|
doc_path: [socket.assigns.library.name, socket.assigns.mix_task.name],
|
|
|
|
options: []
|
|
|
|
)
|
2022-09-13 08:47:14 +12:00
|
|
|
|
|
|
|
socket.assigns.dsl ->
|
2023-01-18 17:53:21 +13:00
|
|
|
send(self(), {:page_title, socket.assigns.dsl.name})
|
2023-01-18 15:33:49 +13:00
|
|
|
|
2022-12-27 22:31:13 +13:00
|
|
|
meta_name =
|
|
|
|
Enum.join(
|
|
|
|
[
|
|
|
|
socket.assigns.library.name,
|
|
|
|
socket.assigns.extension.name
|
|
|
|
] ++ socket.assigns.dsl.path ++ [socket.assigns.dsl.name],
|
|
|
|
" > "
|
|
|
|
)
|
|
|
|
|
|
|
|
meta_type = String.capitalize(to_string(socket.assigns.dsl.type))
|
|
|
|
|
2023-01-27 16:19:00 +13:00
|
|
|
dsl = AshHq.Docs.load!(socket.assigns.dsl, [:doc_html], lazy?: true)
|
|
|
|
|
|
|
|
options =
|
|
|
|
Enum.filter(
|
|
|
|
socket.assigns.extension.options,
|
|
|
|
&(&1.path == socket.assigns.dsl.path ++ [socket.assigns.dsl.name])
|
|
|
|
)
|
|
|
|
|> AshHq.Docs.load!(:doc_html, lazy?: true)
|
|
|
|
|
2022-09-13 08:47:14 +12:00
|
|
|
assign(socket,
|
2023-01-27 16:19:00 +13:00
|
|
|
docs: dsl.doc_html,
|
2022-12-27 22:31:13 +13:00
|
|
|
title: "DSL #{meta_type}: #{meta_name}",
|
|
|
|
description: "View the documentation for DSL #{meta_type}: #{meta_name} on Ash HQ.",
|
2022-09-13 08:47:14 +12:00
|
|
|
doc_path:
|
|
|
|
[
|
|
|
|
socket.assigns.library.name,
|
|
|
|
socket.assigns.extension.name
|
|
|
|
] ++ socket.assigns.dsl.path ++ [socket.assigns.dsl.name],
|
2023-01-27 16:19:00 +13:00
|
|
|
options: options
|
2022-09-13 08:47:14 +12:00
|
|
|
)
|
|
|
|
|
|
|
|
socket.assigns.extension ->
|
2023-01-18 17:53:21 +13:00
|
|
|
send(self(), {:page_title, socket.assigns.extension.name})
|
|
|
|
|
2022-09-13 08:47:14 +12:00
|
|
|
assign(socket,
|
2023-01-19 11:56:07 +13:00
|
|
|
docs: socket.assigns.extension |> reselect_and_get!(:doc_html),
|
2022-12-27 22:31:13 +13:00
|
|
|
title: "Extension: #{socket.assigns.extension.name}",
|
|
|
|
description: "View the documentation for #{socket.assigns.extension.name} on Ash HQ.",
|
2022-09-13 08:47:14 +12:00
|
|
|
doc_path: [socket.assigns.library.name, socket.assigns.extension.name],
|
|
|
|
options: []
|
|
|
|
)
|
|
|
|
|
|
|
|
socket.assigns.guide ->
|
2023-01-18 17:53:21 +13:00
|
|
|
send(self(), {:page_title, socket.assigns.guide.name})
|
|
|
|
|
2022-09-13 08:47:14 +12:00
|
|
|
assign(socket,
|
2022-12-27 22:31:13 +13:00
|
|
|
title: "Guide: #{socket.assigns.guide.name}",
|
2023-01-19 11:56:07 +13:00
|
|
|
docs: socket.assigns.guide |> reselect_and_get!(:text_html),
|
2022-12-27 22:31:13 +13:00
|
|
|
description: "Read the \"#{socket.assigns.guide.name}\" guide on Ash HQ",
|
2022-09-13 08:47:14 +12:00
|
|
|
doc_path: [socket.assigns.library.name, socket.assigns.guide.name],
|
|
|
|
options: []
|
|
|
|
)
|
|
|
|
|
|
|
|
true ->
|
2022-12-27 22:31:13 +13:00
|
|
|
assign(socket,
|
|
|
|
docs: "",
|
|
|
|
title: "Ash Framework",
|
|
|
|
description: default_description(),
|
|
|
|
doc_path: [],
|
|
|
|
dsls: [],
|
|
|
|
options: []
|
|
|
|
)
|
2022-09-13 08:47:14 +12:00
|
|
|
end
|
|
|
|
end
|
2022-11-02 18:29:57 +13:00
|
|
|
|
2022-12-27 22:31:13 +13:00
|
|
|
defp default_description do
|
|
|
|
"A declarative foundation for ambitious Elixir applications. Model your domain, derive the rest."
|
|
|
|
end
|
|
|
|
|
2022-11-02 18:29:57 +13:00
|
|
|
# workaround for https://github.com/patrick-steele-idem/morphdom/pull/231
|
|
|
|
# Adding a unique ID on the container for the rendered docs prevents morphdom
|
|
|
|
# merging them incorrectly.
|
|
|
|
defp docs_container_id(doc_path) do
|
|
|
|
["docs-container" | doc_path]
|
|
|
|
|> Enum.join("-")
|
|
|
|
|> String.replace(~r/[^A-Za-z0-9_]/, "-")
|
|
|
|
|> String.downcase()
|
|
|
|
end
|
2022-03-28 10:26:35 +13:00
|
|
|
end
|