mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-10-06 11:35:54 +08:00
Create LayoutHelpers to encapsulate the whole home layout instead of sidebar (#1352)
This commit is contained in:
parent
1e4ae42e6c
commit
30344b0636
6 changed files with 416 additions and 436 deletions
|
@ -2,9 +2,8 @@ defmodule LivebookWeb.ExploreLive do
|
|||
use LivebookWeb, :live_view
|
||||
|
||||
import LivebookWeb.SessionHelpers
|
||||
import LivebookWeb.UserHelpers
|
||||
|
||||
alias LivebookWeb.{SidebarHelpers, ExploreHelpers, PageHelpers}
|
||||
alias LivebookWeb.{LayoutHelpers, ExploreHelpers, PageHelpers}
|
||||
alias Livebook.Notebook.Explore
|
||||
|
||||
on_mount LivebookWeb.SidebarHook
|
||||
|
@ -24,62 +23,51 @@ defmodule LivebookWeb.ExploreLive do
|
|||
@impl true
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<div class="flex grow h-full">
|
||||
<SidebarHelpers.sidebar
|
||||
socket={@socket}
|
||||
current_page={Routes.explore_path(@socket, :page)}
|
||||
current_user={@current_user}
|
||||
saved_hubs={@saved_hubs}
|
||||
/>
|
||||
<div class="grow overflow-y-auto">
|
||||
<SidebarHelpers.toggle socket={@socket} />
|
||||
<div class="px-4 sm:px-8 md:px-16 pt-4 sm:py-7 max-w-screen-md mx-auto space-y-8">
|
||||
<div>
|
||||
<PageHelpers.title text="Explore" />
|
||||
<p class="mt-4 text-gray-700">
|
||||
Check out a number of examples showcasing various parts of the Elixir ecosystem.
|
||||
Click on any notebook you like and start playing around with it!
|
||||
<LayoutHelpers.layout
|
||||
socket={@socket}
|
||||
current_page={Routes.explore_path(@socket, :page)}
|
||||
current_user={@current_user}
|
||||
saved_hubs={@saved_hubs}
|
||||
>
|
||||
<div class="px-4 sm:px-8 md:px-16 pt-4 sm:py-7 max-w-screen-md mx-auto space-y-8">
|
||||
<div>
|
||||
<PageHelpers.title text="Explore" />
|
||||
<p class="mt-4 text-gray-700">
|
||||
Check out a number of examples showcasing various parts of the Elixir ecosystem.
|
||||
Click on any notebook you like and start playing around with it!
|
||||
</p>
|
||||
</div>
|
||||
<div class="p-8 bg-gray-900 rounded-2xl flex space-x-4 shadow-xl">
|
||||
<div class="self-end max-w-sm">
|
||||
<h3 class="text-xl text-gray-50 font-semibold">
|
||||
<%= @lead_notebook_info.title %>
|
||||
</h3>
|
||||
<p class="mt-2 text-sm text-gray-300">
|
||||
<%= @lead_notebook_info.details.description %>
|
||||
</p>
|
||||
</div>
|
||||
<div class="p-8 bg-gray-900 rounded-2xl flex space-x-4 shadow-xl">
|
||||
<div class="self-end max-w-sm">
|
||||
<h3 class="text-xl text-gray-50 font-semibold">
|
||||
<%= @lead_notebook_info.title %>
|
||||
</h3>
|
||||
<p class="mt-2 text-sm text-gray-300">
|
||||
<%= @lead_notebook_info.details.description %>
|
||||
</p>
|
||||
<div class="mt-4">
|
||||
<%= live_patch("Let's go",
|
||||
to: Routes.explore_path(@socket, :notebook, @lead_notebook_info.slug),
|
||||
class: "button-base button-blue"
|
||||
) %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grow hidden md:flex flex items-center justify-center">
|
||||
<img
|
||||
src={@lead_notebook_info.details.cover_url}
|
||||
height="120"
|
||||
width="120"
|
||||
alt="livebook"
|
||||
/>
|
||||
<div class="mt-4">
|
||||
<%= live_patch("Let's go",
|
||||
to: Routes.explore_path(@socket, :notebook, @lead_notebook_info.slug),
|
||||
class: "button-base button-blue"
|
||||
) %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<% # Note: it's fine to use stateless components in this comprehension,
|
||||
# because @notebook_infos never change %>
|
||||
<%= for info <- @notebook_infos do %>
|
||||
<ExploreHelpers.notebook_card notebook_info={info} socket={@socket} />
|
||||
<% end %>
|
||||
<div class="grow hidden md:flex flex items-center justify-center">
|
||||
<img src={@lead_notebook_info.details.cover_url} height="120" width="120" alt="livebook" />
|
||||
</div>
|
||||
<%= for group_info <- Explore.group_infos() do %>
|
||||
<.notebook_group group_info={group_info} socket={@socket} />
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<% # Note: it's fine to use stateless components in this comprehension,
|
||||
# because @notebook_infos never change %>
|
||||
<%= for info <- @notebook_infos do %>
|
||||
<ExploreHelpers.notebook_card notebook_info={info} socket={@socket} />
|
||||
<% end %>
|
||||
</div>
|
||||
<%= for group_info <- Explore.group_infos() do %>
|
||||
<.notebook_group group_info={group_info} socket={@socket} />
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<.current_user_modal current_user={@current_user} />
|
||||
</LayoutHelpers.layout>
|
||||
"""
|
||||
end
|
||||
|
||||
|
|
|
@ -2,9 +2,8 @@ defmodule LivebookWeb.HomeLive do
|
|||
use LivebookWeb, :live_view
|
||||
|
||||
import LivebookWeb.SessionHelpers
|
||||
import LivebookWeb.UserHelpers
|
||||
|
||||
alias LivebookWeb.{ExploreHelpers, PageHelpers, SidebarHelpers}
|
||||
alias LivebookWeb.{ExploreHelpers, PageHelpers, LayoutHelpers}
|
||||
alias Livebook.{Sessions, Session, LiveMarkdown, Notebook, FileSystem}
|
||||
|
||||
on_mount LivebookWeb.SidebarHook
|
||||
|
@ -37,106 +36,100 @@ defmodule LivebookWeb.HomeLive do
|
|||
@impl true
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<div class="flex grow h-full">
|
||||
<.live_region role="alert" />
|
||||
<SidebarHelpers.sidebar
|
||||
socket={@socket}
|
||||
current_page={Routes.home_path(@socket, :page)}
|
||||
current_user={@current_user}
|
||||
saved_hubs={@saved_hubs}
|
||||
/>
|
||||
<div class="grow overflow-y-auto">
|
||||
<SidebarHelpers.toggle socket={@socket}>
|
||||
<a aria-label="new-notebook" class="flex items-center" phx-click="new">
|
||||
<.remix_icon icon="add-line" />
|
||||
<span class="pl-2">New notebook</span>
|
||||
</a>
|
||||
</SidebarHelpers.toggle>
|
||||
<.update_notification version={@new_version} instructions_url={@update_instructions_url} />
|
||||
<.memory_notification memory={@memory} app_service_url={@app_service_url} />
|
||||
<div class="px-4 sm:px-8 md:px-16 pt-4 sm:py-7 max-w-screen-lg mx-auto space-y-4">
|
||||
<div class="flex flex-row space-y-0 items-center pb-4 justify-between">
|
||||
<PageHelpers.title text="Home" />
|
||||
<div class="hidden sm:flex space-x-2" role="navigation" aria-label="new notebook">
|
||||
<%= live_patch("Import",
|
||||
to: Routes.home_path(@socket, :import, "url"),
|
||||
class: "button-base button-outlined-gray whitespace-nowrap"
|
||||
) %>
|
||||
<button class="button-base button-blue" phx-click="new">
|
||||
New notebook
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="h-80" role="region" aria-label="file system">
|
||||
<.live_component
|
||||
module={LivebookWeb.FileSelectComponent}
|
||||
id="home-file-select"
|
||||
file={@file}
|
||||
extnames={[LiveMarkdown.extension()]}
|
||||
running_files={files(@sessions)}
|
||||
>
|
||||
<div class="flex justify-end space-x-2">
|
||||
<button
|
||||
class="button-base button-outlined-gray whitespace-nowrap"
|
||||
phx-click="fork"
|
||||
disabled={not path_forkable?(@file, @file_info)}
|
||||
>
|
||||
<.remix_icon icon="git-branch-line" class="align-middle mr-1" />
|
||||
<span>Fork</span>
|
||||
</button>
|
||||
<%= if file_running?(@file, @sessions) do %>
|
||||
<%= live_redirect("Join session",
|
||||
to: Routes.session_path(@socket, :page, session_id_by_file(@file, @sessions)),
|
||||
class: "button-base button-blue"
|
||||
) %>
|
||||
<% else %>
|
||||
<span {open_button_tooltip_attrs(@file, @file_info)}>
|
||||
<button
|
||||
class="button-base button-blue"
|
||||
phx-click="open"
|
||||
disabled={not path_openable?(@file, @file_info, @sessions)}
|
||||
>
|
||||
Open
|
||||
</button>
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
</.live_component>
|
||||
</div>
|
||||
|
||||
<div class="py-12" data-el-explore-section role="region" aria-label="explore section">
|
||||
<div class="mb-4 flex justify-between items-center">
|
||||
<h2 class="uppercase font-semibold text-gray-500">
|
||||
Explore
|
||||
</h2>
|
||||
<%= live_redirect to: Routes.explore_path(@socket, :page),
|
||||
class: "flex items-center text-blue-600" do %>
|
||||
<span class="font-semibold">See all</span>
|
||||
<.remix_icon icon="arrow-right-line" class="align-middle ml-1" />
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<% # Note: it's fine to use stateless components in this comprehension,
|
||||
# because @notebook_infos never change %>
|
||||
<%= for info <- @notebook_infos do %>
|
||||
<ExploreHelpers.notebook_card notebook_info={info} socket={@socket} />
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<div id="running-sessions" class="py-12" role="region" aria-label="running sessions">
|
||||
<.live_component
|
||||
module={LivebookWeb.HomeLive.SessionListComponent}
|
||||
id="session-list"
|
||||
sessions={@sessions}
|
||||
memory={@memory}
|
||||
/>
|
||||
<LayoutHelpers.layout
|
||||
socket={@socket}
|
||||
current_page={Routes.home_path(@socket, :page)}
|
||||
current_user={@current_user}
|
||||
saved_hubs={@saved_hubs}
|
||||
>
|
||||
<:topbar_action>
|
||||
<a aria-label="new-notebook" class="flex items-center" phx-click="new">
|
||||
<.remix_icon icon="add-line" />
|
||||
<span class="pl-2">New notebook</span>
|
||||
</a>
|
||||
</:topbar_action>
|
||||
<.update_notification version={@new_version} instructions_url={@update_instructions_url} />
|
||||
<.memory_notification memory={@memory} app_service_url={@app_service_url} />
|
||||
<div class="px-4 sm:px-8 md:px-16 pt-4 sm:py-7 max-w-screen-lg mx-auto space-y-4">
|
||||
<div class="flex flex-row space-y-0 items-center pb-4 justify-between">
|
||||
<PageHelpers.title text="Home" />
|
||||
<div class="hidden sm:flex space-x-2" role="navigation" aria-label="new notebook">
|
||||
<%= live_patch("Import",
|
||||
to: Routes.home_path(@socket, :import, "url"),
|
||||
class: "button-base button-outlined-gray whitespace-nowrap"
|
||||
) %>
|
||||
<button class="button-base button-blue" phx-click="new">
|
||||
New notebook
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<.current_user_modal current_user={@current_user} />
|
||||
<div class="h-80" role="region" aria-label="file system">
|
||||
<.live_component
|
||||
module={LivebookWeb.FileSelectComponent}
|
||||
id="home-file-select"
|
||||
file={@file}
|
||||
extnames={[LiveMarkdown.extension()]}
|
||||
running_files={files(@sessions)}
|
||||
>
|
||||
<div class="flex justify-end space-x-2">
|
||||
<button
|
||||
class="button-base button-outlined-gray whitespace-nowrap"
|
||||
phx-click="fork"
|
||||
disabled={not path_forkable?(@file, @file_info)}
|
||||
>
|
||||
<.remix_icon icon="git-branch-line" class="align-middle mr-1" />
|
||||
<span>Fork</span>
|
||||
</button>
|
||||
<%= if file_running?(@file, @sessions) do %>
|
||||
<%= live_redirect("Join session",
|
||||
to: Routes.session_path(@socket, :page, session_id_by_file(@file, @sessions)),
|
||||
class: "button-base button-blue"
|
||||
) %>
|
||||
<% else %>
|
||||
<span {open_button_tooltip_attrs(@file, @file_info)}>
|
||||
<button
|
||||
class="button-base button-blue"
|
||||
phx-click="open"
|
||||
disabled={not path_openable?(@file, @file_info, @sessions)}
|
||||
>
|
||||
Open
|
||||
</button>
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
</.live_component>
|
||||
</div>
|
||||
|
||||
<div class="py-12" data-el-explore-section role="region" aria-label="explore section">
|
||||
<div class="mb-4 flex justify-between items-center">
|
||||
<h2 class="uppercase font-semibold text-gray-500">
|
||||
Explore
|
||||
</h2>
|
||||
<%= live_redirect to: Routes.explore_path(@socket, :page),
|
||||
class: "flex items-center text-blue-600" do %>
|
||||
<span class="font-semibold">See all</span>
|
||||
<.remix_icon icon="arrow-right-line" class="align-middle ml-1" />
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<% # Note: it's fine to use stateless components in this comprehension,
|
||||
# because @notebook_infos never change %>
|
||||
<%= for info <- @notebook_infos do %>
|
||||
<ExploreHelpers.notebook_card notebook_info={info} socket={@socket} />
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<div id="running-sessions" class="py-12" role="region" aria-label="running sessions">
|
||||
<.live_component
|
||||
module={LivebookWeb.HomeLive.SessionListComponent}
|
||||
id="session-list"
|
||||
sessions={@sessions}
|
||||
memory={@memory}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</LayoutHelpers.layout>
|
||||
|
||||
<%= if @live_action == :close_session do %>
|
||||
<.modal id="close-session-modal" show class="w-full max-w-xl" patch={@self_path}>
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
defmodule LivebookWeb.HubLive do
|
||||
use LivebookWeb, :live_view
|
||||
|
||||
import LivebookWeb.UserHelpers
|
||||
|
||||
alias Livebook.Hubs
|
||||
alias Livebook.Hubs.Provider
|
||||
alias LivebookWeb.{PageHelpers, SidebarHelpers}
|
||||
alias LivebookWeb.{PageHelpers, LayoutHelpers}
|
||||
alias Phoenix.LiveView.JS
|
||||
|
||||
on_mount LivebookWeb.SidebarHook
|
||||
|
@ -23,85 +21,81 @@ defmodule LivebookWeb.HubLive do
|
|||
@impl true
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<div class="flex grow h-full">
|
||||
<.live_region role="alert" />
|
||||
<SidebarHelpers.sidebar
|
||||
socket={@socket}
|
||||
current_user={@current_user}
|
||||
current_page=""
|
||||
saved_hubs={@saved_hubs}
|
||||
/>
|
||||
<LayoutHelpers.layout
|
||||
socket={@socket}
|
||||
current_page={@current_page}
|
||||
current_user={@current_user}
|
||||
saved_hubs={@saved_hubs}
|
||||
>
|
||||
<div class="px-4 sm:px-8 md:px-16 pt-4 sm:py-7 max-w-screen-md mx-auto space-y-8">
|
||||
<div>
|
||||
<PageHelpers.title
|
||||
text={if @operation == :new, do: "Add Hub", else: "Edit Hub"}
|
||||
socket={@socket}
|
||||
/>
|
||||
<p class="mt-4 text-gray-700">
|
||||
Manage your Livebooks in the cloud with Hubs.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="grow px-6 py-8 overflow-y-auto">
|
||||
<div class="max-w-screen-md w-full mx-auto px-4 pb-8 space-y-8">
|
||||
<div>
|
||||
<PageHelpers.title text="Hub" socket={@socket} />
|
||||
<p class="mt-4 text-gray-700">
|
||||
Manage your Livebooks in the cloud with Hubs.
|
||||
</p>
|
||||
<div class="flex flex-col space-y-4">
|
||||
<h2 class="text-xl text-gray-800 font-semibold pb-2 border-b border-gray-200">
|
||||
1. Select your Hub service
|
||||
</h2>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<.card_item id="fly" selected={@selected_provider} title="Fly">
|
||||
<:logo>
|
||||
<%= Phoenix.HTML.raw(File.read!("static/images/fly.svg")) %>
|
||||
</:logo>
|
||||
<:headline>
|
||||
Deploy notebooks to your Fly account.
|
||||
</:headline>
|
||||
</.card_item>
|
||||
|
||||
<.card_item id="enterprise" selected={@selected_provider} title="Livebook Enterprise">
|
||||
<:logo>
|
||||
<img
|
||||
src="/images/enterprise.png"
|
||||
class="max-h-full max-w-[75%]"
|
||||
alt="Livebook Enterprise logo"
|
||||
/>
|
||||
</:logo>
|
||||
<:headline>
|
||||
Control access, manage secrets, and deploy notebooks within your team.
|
||||
</:headline>
|
||||
</.card_item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= if @selected_provider do %>
|
||||
<div class="flex flex-col space-y-4">
|
||||
<h2 class="text-xl text-gray-800 font-semibold pb-2 border-b border-gray-200">
|
||||
1. Select your Hub service
|
||||
2. Configure your Hub
|
||||
</h2>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<.card_item id="fly" selected={@selected_provider} title="Fly">
|
||||
<:logo>
|
||||
<%= Phoenix.HTML.raw(File.read!("static/images/fly.svg")) %>
|
||||
</:logo>
|
||||
<:headline>
|
||||
Deploy notebooks to your Fly account.
|
||||
</:headline>
|
||||
</.card_item>
|
||||
<%= if @selected_provider == "fly" do %>
|
||||
<.live_component
|
||||
module={LivebookWeb.HubLive.FlyComponent}
|
||||
id="fly-form"
|
||||
operation={@operation}
|
||||
hub={@hub}
|
||||
/>
|
||||
<% end %>
|
||||
|
||||
<.card_item id="enterprise" selected={@selected_provider} title="Livebook Enterprise">
|
||||
<:logo>
|
||||
<img
|
||||
src="/images/enterprise.png"
|
||||
class="max-h-full max-w-[75%]"
|
||||
alt="Livebook Enterprise logo"
|
||||
/>
|
||||
</:logo>
|
||||
<:headline>
|
||||
Control access, manage secrets, and deploy notebooks within your team and company.
|
||||
</:headline>
|
||||
</.card_item>
|
||||
</div>
|
||||
<%= if @selected_provider == "enterprise" do %>
|
||||
<div>
|
||||
Livebook Enterprise is currently in closed beta. If you want to learn more, <a
|
||||
href="https://livebook.dev/#livebook-plans"
|
||||
class="pointer-events-auto text-blue-600"
|
||||
target="_blank"
|
||||
>click here</a>.
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<%= if @selected_provider do %>
|
||||
<div class="flex flex-col space-y-4">
|
||||
<h2 class="text-xl text-gray-800 font-semibold pb-2 border-b border-gray-200">
|
||||
2. Configure your Hub
|
||||
</h2>
|
||||
|
||||
<%= if @selected_provider == "fly" do %>
|
||||
<.live_component
|
||||
module={LivebookWeb.HubLive.FlyComponent}
|
||||
id="fly-form"
|
||||
operation={@operation}
|
||||
hub={@hub}
|
||||
/>
|
||||
<% end %>
|
||||
|
||||
<%= if @selected_provider == "enterprise" do %>
|
||||
<div>
|
||||
Livebook Enterprise is currently in closed beta. If you want to learn more, <a
|
||||
href="https://livebook.dev/#livebook-plans"
|
||||
class="pointer-events-auto text-blue-600"
|
||||
target="_blank"
|
||||
>click here</a>.
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<.current_user_modal current_user={@current_user} />
|
||||
</div>
|
||||
</LayoutHelpers.layout>
|
||||
"""
|
||||
end
|
||||
|
||||
|
@ -136,11 +130,17 @@ defmodule LivebookWeb.HubLive do
|
|||
hub = Hubs.fetch_hub!(id)
|
||||
provider = Provider.type(hub)
|
||||
|
||||
{:noreply, assign(socket, operation: :edit, hub: hub, selected_provider: provider)}
|
||||
{:noreply,
|
||||
assign(socket,
|
||||
operation: :edit,
|
||||
hub: hub,
|
||||
selected_provider: provider,
|
||||
current_page: Routes.hub_path(socket, :edit, hub.id)
|
||||
)}
|
||||
end
|
||||
|
||||
def handle_params(_params, _url, socket) do
|
||||
{:noreply, assign(socket, operation: :new)}
|
||||
{:noreply, assign(socket, operation: :new, current_page: Routes.hub_path(socket, :new))}
|
||||
end
|
||||
|
||||
@impl true
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
defmodule LivebookWeb.SidebarHelpers do
|
||||
defmodule LivebookWeb.LayoutHelpers do
|
||||
use Phoenix.Component
|
||||
|
||||
import LivebookWeb.LiveHelpers
|
||||
|
@ -8,58 +8,75 @@ defmodule LivebookWeb.SidebarHelpers do
|
|||
alias LivebookWeb.Router.Helpers, as: Routes
|
||||
|
||||
@doc """
|
||||
Renders the mobile toggle for the sidebar.
|
||||
The layout used in the non-session pages.
|
||||
"""
|
||||
def toggle(assigns) do
|
||||
assigns = assign_new(assigns, :inner_block, fn -> [] end)
|
||||
def layout(assigns) do
|
||||
assigns = assign_new(assigns, :topbar_action, fn -> [] end)
|
||||
|
||||
~H"""
|
||||
<div class="flex sm:hidden items-center justify-between sticky sm:pt-1 px-2 top-0 left-0 right-0 z-[500] bg-white border-b border-gray-200">
|
||||
<div class="my-2 text-2xl text-gray-400 hover:text-gray-600 focus:text-gray-600 rounded-xl h-10 w-10 flex items-center justify-center">
|
||||
<button
|
||||
aria-label="hide sidebar"
|
||||
data-el-toggle-sidebar
|
||||
phx-click={
|
||||
JS.add_class("hidden sm:flex", to: "[data-el-sidebar]")
|
||||
|> JS.toggle(to: "[data-el-toggle-sidebar]", display: "flex")
|
||||
}
|
||||
>
|
||||
<.remix_icon icon="menu-fold-line" />
|
||||
</button>
|
||||
<button
|
||||
class="hidden"
|
||||
aria-label="show sidebar"
|
||||
data-el-toggle-sidebar
|
||||
phx-click={
|
||||
JS.remove_class("hidden sm:flex", to: "[data-el-sidebar]")
|
||||
|> JS.toggle(to: "[data-el-toggle-sidebar]", display: "flex")
|
||||
}
|
||||
>
|
||||
<.remix_icon icon="menu-unfold-line" />
|
||||
</button>
|
||||
<div class="flex grow h-full">
|
||||
<.live_region role="alert" />
|
||||
<.sidebar
|
||||
socket={@socket}
|
||||
current_page={@current_page}
|
||||
current_user={@current_user}
|
||||
saved_hubs={@saved_hubs}
|
||||
/>
|
||||
<div class="grow overflow-y-auto">
|
||||
<div class="flex sm:hidden items-center justify-between sticky sm:pt-1 px-2 top-0 left-0 right-0 z-[500] bg-white border-b border-gray-200">
|
||||
<.topbar_sidebar_toggle />
|
||||
<div
|
||||
class="hidden items-center justify-end p-2 text-gray-400 hover:text-gray-600 focus:text-gray-600"
|
||||
data-el-toggle-sidebar
|
||||
>
|
||||
<% # TODO: Use render_slot(@topbar_action) || default() on LiveView 0.18 %>
|
||||
<%= if @topbar_action == [] do %>
|
||||
<%= live_redirect to: Routes.home_path(@socket, :page), class: "flex items-center", aria: [label: "go to home"] do %>
|
||||
<.remix_icon icon="home-6-line" />
|
||||
<span class="pl-2">Home</span>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= render_slot(@topbar_action) %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<%= render_slot(@inner_block) %>
|
||||
</div>
|
||||
<div
|
||||
class="hidden items-center justify-end p-2 text-gray-400 hover:text-gray-600 focus:text-gray-600"
|
||||
</div>
|
||||
|
||||
<.current_user_modal current_user={@current_user} />
|
||||
"""
|
||||
end
|
||||
|
||||
defp topbar_sidebar_toggle(assigns) do
|
||||
~H"""
|
||||
<div class="my-2 text-2xl text-gray-400 hover:text-gray-600 focus:text-gray-600 rounded-xl h-10 w-10 flex items-center justify-center">
|
||||
<button
|
||||
aria-label="hide sidebar"
|
||||
data-el-toggle-sidebar
|
||||
phx-click={
|
||||
JS.add_class("hidden sm:flex", to: "[data-el-sidebar]")
|
||||
|> JS.toggle(to: "[data-el-toggle-sidebar]", display: "flex")
|
||||
}
|
||||
>
|
||||
<% # TODO: Use render_slot(@inner_block) || default() on LiveView 0.18 %>
|
||||
<%= if @inner_block == [] do %>
|
||||
<%= live_redirect to: Routes.home_path(@socket, :page), class: "flex items-center", aria: [label: "go to home"] do %>
|
||||
<.remix_icon icon="home-6-line" />
|
||||
<span class="pl-2">Home</span>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= render_slot(@inner_block) %>
|
||||
<% end %>
|
||||
</div>
|
||||
<.remix_icon icon="menu-fold-line" />
|
||||
</button>
|
||||
<button
|
||||
class="hidden"
|
||||
aria-label="show sidebar"
|
||||
data-el-toggle-sidebar
|
||||
phx-click={
|
||||
JS.remove_class("hidden sm:flex", to: "[data-el-sidebar]")
|
||||
|> JS.toggle(to: "[data-el-toggle-sidebar]", display: "flex")
|
||||
}
|
||||
>
|
||||
<.remix_icon icon="menu-unfold-line" />
|
||||
</button>
|
||||
</div>
|
||||
"""
|
||||
end
|
||||
|
||||
@doc """
|
||||
Renders sidebar container.
|
||||
"""
|
||||
def sidebar(assigns) do
|
||||
defp sidebar(assigns) do
|
||||
~H"""
|
||||
<nav
|
||||
class="w-[18.75rem] min-w-[14rem] flex flex-col justify-between py-1 sm:py-7 bg-gray-900"
|
||||
|
@ -104,7 +121,7 @@ defmodule LivebookWeb.SidebarHelpers do
|
|||
current={@current_page}
|
||||
/>
|
||||
</div>
|
||||
<.hub_section socket={@socket} hubs={@saved_hubs} />
|
||||
<.hub_section socket={@socket} hubs={@saved_hubs} current_page={@current_page} />
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<%= if Livebook.Config.shutdown_enabled?() do %>
|
||||
|
@ -149,9 +166,15 @@ defmodule LivebookWeb.SidebarHelpers do
|
|||
end
|
||||
|
||||
defp sidebar_link(assigns) do
|
||||
assigns = assign_new(assigns, :icon_style, fn -> nil end)
|
||||
|
||||
~H"""
|
||||
<%= live_redirect to: @to, class: "h-7 flex items-center hover:text-white #{sidebar_link_text_color(@to, @current)} border-l-4 #{sidebar_link_border_color(@to, @current)} hover:border-white" do %>
|
||||
<.remix_icon icon={@icon} class="text-lg leading-6 w-[56px] flex justify-center" />
|
||||
<.remix_icon
|
||||
icon={@icon}
|
||||
class="text-lg leading-6 w-[56px] flex justify-center"
|
||||
style={@icon_style}
|
||||
/>
|
||||
<span class="text-sm font-medium">
|
||||
<%= @title %>
|
||||
</span>
|
||||
|
@ -165,41 +188,25 @@ defmodule LivebookWeb.SidebarHelpers do
|
|||
<div id="hubs" class="flex flex-col mt-12">
|
||||
<div class="space-y-1">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 relative leading-6 mb-2">
|
||||
<div class="flex flex-col">
|
||||
<small class="ml-5 font-medium text-white">HUBS</small>
|
||||
</div>
|
||||
<div class="flex flex-col">
|
||||
<%= live_redirect to: Routes.hub_path(@socket, :page),
|
||||
class: "flex absolute right-5 items-center justify-center
|
||||
text-gray-400 hover:text-white hover:border-white" do %>
|
||||
<.remix_icon icon="add-line" />
|
||||
<% end %>
|
||||
</div>
|
||||
<small class="ml-5 font-medium text-gray-300 cursor-default">HUBS</small>
|
||||
</div>
|
||||
|
||||
<%= for hub <- @hubs do %>
|
||||
<%= live_redirect to: Routes.hub_path(@socket, :edit, hub.id), class: "h-7 flex items-center cursor-pointer text-gray-400 hover:text-white" do %>
|
||||
<.remix_icon
|
||||
class="text-lg leading-6 w-[56px] flex justify-center"
|
||||
icon="checkbox-blank-circle-fill"
|
||||
style={"color: #{hub.color}"}
|
||||
/>
|
||||
|
||||
<span class="text-sm font-medium">
|
||||
<%= hub.name %>
|
||||
</span>
|
||||
<% end %>
|
||||
<.sidebar_link
|
||||
title={hub.name}
|
||||
icon="checkbox-blank-circle-fill"
|
||||
icon_style={"color: #{hub.color}"}
|
||||
to={Routes.hub_path(@socket, :edit, hub.id)}
|
||||
current={@current_page}
|
||||
/>
|
||||
<% end %>
|
||||
|
||||
<div class="h-7 flex items-center cursor-pointer text-gray-400 hover:text-white mt-2">
|
||||
<%= live_redirect to: Routes.hub_path(@socket, :page), class: "h-7 flex items-center cursor-pointer text-gray-400 hover:text-white" do %>
|
||||
<.remix_icon class="text-lg leading-6 w-[56px] flex justify-center" icon="add-line" />
|
||||
|
||||
<span class="text-sm font-medium">
|
||||
Add Hub
|
||||
</span>
|
||||
<% end %>
|
||||
</div>
|
||||
<.sidebar_link
|
||||
title="Add Hub"
|
||||
icon="add-line"
|
||||
to={Routes.hub_path(@socket, :new)}
|
||||
current={@current_page}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
|
@ -1,9 +1,7 @@
|
|||
defmodule LivebookWeb.SettingsLive do
|
||||
use LivebookWeb, :live_view
|
||||
|
||||
import LivebookWeb.UserHelpers
|
||||
|
||||
alias LivebookWeb.{SidebarHelpers, PageHelpers}
|
||||
alias LivebookWeb.{LayoutHelpers, PageHelpers}
|
||||
|
||||
on_mount LivebookWeb.SidebarHook
|
||||
|
||||
|
@ -24,149 +22,143 @@ defmodule LivebookWeb.SettingsLive do
|
|||
@impl true
|
||||
def render(assigns) do
|
||||
~H"""
|
||||
<div class="flex grow h-full">
|
||||
<SidebarHelpers.sidebar
|
||||
socket={@socket}
|
||||
current_page={Routes.settings_path(@socket, :page)}
|
||||
current_user={@current_user}
|
||||
saved_hubs={@saved_hubs}
|
||||
/>
|
||||
<div class="grow overflow-y-auto">
|
||||
<SidebarHelpers.toggle socket={@socket} />
|
||||
<div class="px-4 sm:px-8 md:px-16 pt-4 sm:py-7 max-w-screen-md mx-auto space-y-8">
|
||||
<!-- System settings section -->
|
||||
<div class="flex flex-col space-y-10">
|
||||
<div>
|
||||
<PageHelpers.title text="System settings" />
|
||||
<p class="mt-4 text-gray-700">
|
||||
Here you can change global Livebook configuration. Keep in mind
|
||||
that this configuration gets persisted and will be restored on application
|
||||
launch.
|
||||
</p>
|
||||
</div>
|
||||
<!-- System details -->
|
||||
<div class="flex flex-col space-y-2">
|
||||
<h2 class="text-xl text-gray-800 font-semibold">
|
||||
About
|
||||
</h2>
|
||||
<div class="flex items-center justify-between border border-gray-200 rounded-lg p-4">
|
||||
<div class="flex items-center space-x-12">
|
||||
<%= if app_name = Livebook.Config.app_service_name() do %>
|
||||
<.labeled_text label="Application">
|
||||
<%= if app_url = Livebook.Config.app_service_url() do %>
|
||||
<a href={app_url} class="underline hover:no-underline" target="_blank">
|
||||
<%= app_name %>
|
||||
</a>
|
||||
<% else %>
|
||||
<LayoutHelpers.layout
|
||||
socket={@socket}
|
||||
current_page={Routes.settings_path(@socket, :page)}
|
||||
current_user={@current_user}
|
||||
saved_hubs={@saved_hubs}
|
||||
>
|
||||
<div class="px-4 sm:px-8 md:px-16 pt-4 sm:py-7 max-w-screen-md mx-auto space-y-8">
|
||||
<!-- System settings section -->
|
||||
<div class="flex flex-col space-y-10">
|
||||
<div>
|
||||
<PageHelpers.title text="System settings" />
|
||||
<p class="mt-4 text-gray-700">
|
||||
Here you can change global Livebook configuration. Keep in mind
|
||||
that this configuration gets persisted and will be restored on application
|
||||
launch.
|
||||
</p>
|
||||
</div>
|
||||
<!-- System details -->
|
||||
<div class="flex flex-col space-y-2">
|
||||
<h2 class="text-xl text-gray-800 font-semibold">
|
||||
About
|
||||
</h2>
|
||||
<div class="flex items-center justify-between border border-gray-200 rounded-lg p-4">
|
||||
<div class="flex items-center space-x-12">
|
||||
<%= if app_name = Livebook.Config.app_service_name() do %>
|
||||
<.labeled_text label="Application">
|
||||
<%= if app_url = Livebook.Config.app_service_url() do %>
|
||||
<a href={app_url} class="underline hover:no-underline" target="_blank">
|
||||
<%= app_name %>
|
||||
<% end %>
|
||||
</.labeled_text>
|
||||
<% end %>
|
||||
<.labeled_text label="Livebook">
|
||||
v<%= Application.spec(:livebook, :vsn) %>
|
||||
</a>
|
||||
<% else %>
|
||||
<%= app_name %>
|
||||
<% end %>
|
||||
</.labeled_text>
|
||||
<.labeled_text label="Elixir">
|
||||
v<%= System.version() %>
|
||||
</.labeled_text>
|
||||
</div>
|
||||
|
||||
<%= live_redirect to: Routes.live_dashboard_path(@socket, :home),
|
||||
class: "button-base button-outlined-gray" do %>
|
||||
<.remix_icon icon="dashboard-2-line" class="align-middle mr-1" />
|
||||
<span>Open dashboard</span>
|
||||
<% end %>
|
||||
<.labeled_text label="Livebook">
|
||||
v<%= Application.spec(:livebook, :vsn) %>
|
||||
</.labeled_text>
|
||||
<.labeled_text label="Elixir">
|
||||
v<%= System.version() %>
|
||||
</.labeled_text>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Updates -->
|
||||
<div class="flex flex-col space-y-4">
|
||||
<h2 class="text-xl text-gray-800 font-semibold pb-2 border-b border-gray-200">
|
||||
Updates
|
||||
</h2>
|
||||
<form class="mt-4" phx-change="save" onsubmit="return false;">
|
||||
<.switch_checkbox
|
||||
name="update_check_enabled"
|
||||
label="Show banner when a new Livebook version is available"
|
||||
checked={@update_check_enabled}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
<!-- Autosave path configuration -->
|
||||
<div class="flex flex-col space-y-4">
|
||||
<h2 class="text-xl text-gray-800 font-semibold pb-2 border-b border-gray-200">
|
||||
Autosave
|
||||
</h2>
|
||||
<p class="text-gray-700">
|
||||
The directory to keep unsaved notebooks.
|
||||
</p>
|
||||
<.autosave_path_select state={@autosave_path_state} />
|
||||
</div>
|
||||
<!-- File systems configuration -->
|
||||
<div class="flex flex-col space-y-4">
|
||||
<h2 class="text-xl text-gray-800 font-semibold pb-2 border-b border-gray-200">
|
||||
File systems
|
||||
</h2>
|
||||
<p class="mt-4 text-gray-700">
|
||||
File systems are used to store notebooks. The local disk filesystem
|
||||
is visible only to the current machine, but alternative file systems
|
||||
are available, such as S3-based storages.
|
||||
</p>
|
||||
<LivebookWeb.SettingsLive.FileSystemsComponent.render
|
||||
file_systems={@file_systems}
|
||||
socket={@socket}
|
||||
/>
|
||||
|
||||
<%= live_redirect to: Routes.live_dashboard_path(@socket, :home),
|
||||
class: "button-base button-outlined-gray" do %>
|
||||
<.remix_icon icon="dashboard-2-line" class="align-middle mr-1" />
|
||||
<span>Open dashboard</span>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<!-- User settings section -->
|
||||
<div class="flex flex-col space-y-10">
|
||||
<div>
|
||||
<h1 class="text-3xl text-gray-800 font-semibold">
|
||||
User settings
|
||||
</h1>
|
||||
<p class="mt-4 text-gray-700">
|
||||
The configuration in this section changes only your Livebook
|
||||
experience and is saved in your browser.
|
||||
</p>
|
||||
</div>
|
||||
<!-- Editor configuration -->
|
||||
<div class="flex flex-col space-y-4">
|
||||
<h2 class="text-xl text-gray-800 font-semibold pb-2 border-b border-gray-200">
|
||||
Code editor
|
||||
</h2>
|
||||
<div
|
||||
class="flex flex-col space-y-3"
|
||||
id="editor-settings"
|
||||
phx-hook="EditorSettings"
|
||||
phx-update="ignore"
|
||||
>
|
||||
<.switch_checkbox
|
||||
name="editor_auto_completion"
|
||||
label="Show completion list while typing"
|
||||
checked={false}
|
||||
/>
|
||||
<.switch_checkbox
|
||||
name="editor_auto_signature"
|
||||
label="Show function signature while typing"
|
||||
checked={false}
|
||||
/>
|
||||
<.switch_checkbox name="editor_font_size" label="Increase font size" checked={false} />
|
||||
<.switch_checkbox
|
||||
name="editor_high_contrast"
|
||||
label="Use high contrast theme"
|
||||
checked={false}
|
||||
/>
|
||||
<.switch_checkbox
|
||||
name="editor_markdown_word_wrap"
|
||||
label="Wrap words in Markdown"
|
||||
checked={false}
|
||||
/>
|
||||
</div>
|
||||
<!-- Updates -->
|
||||
<div class="flex flex-col space-y-4">
|
||||
<h2 class="text-xl text-gray-800 font-semibold pb-2 border-b border-gray-200">
|
||||
Updates
|
||||
</h2>
|
||||
<form class="mt-4" phx-change="save" onsubmit="return false;">
|
||||
<.switch_checkbox
|
||||
name="update_check_enabled"
|
||||
label="Show banner when a new Livebook version is available"
|
||||
checked={@update_check_enabled}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
<!-- Autosave path configuration -->
|
||||
<div class="flex flex-col space-y-4">
|
||||
<h2 class="text-xl text-gray-800 font-semibold pb-2 border-b border-gray-200">
|
||||
Autosave
|
||||
</h2>
|
||||
<p class="text-gray-700">
|
||||
The directory to keep unsaved notebooks.
|
||||
</p>
|
||||
<.autosave_path_select state={@autosave_path_state} />
|
||||
</div>
|
||||
<!-- File systems configuration -->
|
||||
<div class="flex flex-col space-y-4">
|
||||
<h2 class="text-xl text-gray-800 font-semibold pb-2 border-b border-gray-200">
|
||||
File systems
|
||||
</h2>
|
||||
<p class="mt-4 text-gray-700">
|
||||
File systems are used to store notebooks. The local disk filesystem
|
||||
is visible only to the current machine, but alternative file systems
|
||||
are available, such as S3-based storages.
|
||||
</p>
|
||||
<LivebookWeb.SettingsLive.FileSystemsComponent.render
|
||||
file_systems={@file_systems}
|
||||
socket={@socket}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- User settings section -->
|
||||
<div class="flex flex-col space-y-10">
|
||||
<div>
|
||||
<h1 class="text-3xl text-gray-800 font-semibold">
|
||||
User settings
|
||||
</h1>
|
||||
<p class="mt-4 text-gray-700">
|
||||
The configuration in this section changes only your Livebook
|
||||
experience and is saved in your browser.
|
||||
</p>
|
||||
</div>
|
||||
<!-- Editor configuration -->
|
||||
<div class="flex flex-col space-y-4">
|
||||
<h2 class="text-xl text-gray-800 font-semibold pb-2 border-b border-gray-200">
|
||||
Code editor
|
||||
</h2>
|
||||
<div
|
||||
class="flex flex-col space-y-3"
|
||||
id="editor-settings"
|
||||
phx-hook="EditorSettings"
|
||||
phx-update="ignore"
|
||||
>
|
||||
<.switch_checkbox
|
||||
name="editor_auto_completion"
|
||||
label="Show completion list while typing"
|
||||
checked={false}
|
||||
/>
|
||||
<.switch_checkbox
|
||||
name="editor_auto_signature"
|
||||
label="Show function signature while typing"
|
||||
checked={false}
|
||||
/>
|
||||
<.switch_checkbox name="editor_font_size" label="Increase font size" checked={false} />
|
||||
<.switch_checkbox
|
||||
name="editor_high_contrast"
|
||||
label="Use high contrast theme"
|
||||
checked={false}
|
||||
/>
|
||||
<.switch_checkbox
|
||||
name="editor_markdown_word_wrap"
|
||||
label="Wrap words in Markdown"
|
||||
checked={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<.current_user_modal current_user={@current_user} />
|
||||
</LayoutHelpers.layout>
|
||||
|
||||
<%= if @live_action == :add_file_system do %>
|
||||
<.modal
|
||||
|
|
|
@ -56,7 +56,7 @@ defmodule LivebookWeb.Router do
|
|||
live "/explore/notebooks/:slug", ExploreLive, :notebook
|
||||
|
||||
if Application.compile_env(:livebook, :feature_flags)[:hub] do
|
||||
live "/hub", HubLive, :page
|
||||
live "/hub", HubLive, :new
|
||||
live "/hub/:id", HubLive, :edit
|
||||
end
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue