Add confirmation for session deletion (#117)

* Update sections panel

* Add deletion confirm and unify buttons

* Align notebook title

* Fix whitespace shrinking in the editor

* Update session deletion route
This commit is contained in:
Jonatan Kłosko 2021-03-25 17:39:18 +01:00 committed by GitHub
parent 9a1fab7b59
commit 0983a9df77
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 127 additions and 58 deletions

View file

@ -1,35 +1,39 @@
/* Buttons */ /* Buttons */
.button { .button {
@apply px-5 py-2 bg-white rounded-lg border border-gray-200 font-medium text-sm text-gray-600; @apply px-5 py-2 rounded-lg border border-transparent font-medium text-sm;
} }
.button:not(:disabled) { .button-blue {
@apply hover:bg-gray-100 focus:bg-gray-100; @apply border-transparent bg-blue-600 text-white hover:bg-blue-700 focus:bg-blue-700;
}
.button-red {
@apply border-transparent bg-red-600 text-white hover:bg-red-700 focus:bg-red-700;
}
.button-gray {
@apply border-gray-200 bg-gray-100 text-gray-600 hover:bg-gray-200 focus:bg-gray-200;
}
.button-outlined-blue {
@apply bg-blue-50 border-blue-600 text-blue-600 hover:bg-blue-100 focus:bg-blue-100;
}
.button-outlined-red {
@apply bg-red-50 border-red-600 text-red-600 hover:bg-red-100 focus:bg-red-100;
}
.button-outlined-gray {
@apply bg-white border-gray-300 text-gray-600 hover:bg-gray-100 focus:bg-gray-100;
} }
.button:disabled { .button:disabled {
@apply cursor-default pointer-events-none border-transparent bg-gray-100 text-gray-400; @apply cursor-default pointer-events-none border-transparent bg-gray-100 text-gray-400;
} }
.button-sm { .button-small {
@apply px-2 py-1; @apply px-2 py-1 bg-gray-50 border-gray-200 text-gray-600 hover:bg-gray-100 focus:bg-gray-100;
}
.button-danger {
@apply bg-red-50 border border-red-600 text-red-600;
}
.button-danger:not(:disabled) {
@apply hover:bg-red-100 focus:bg-red-100;
}
.button-primary {
@apply border-transparent bg-blue-600 text-white;
}
.button-primary:not(:disabled) {
@apply hover:bg-blue-700 focus:bg-blue-700;
} }
.choice-button { .choice-button {

View file

@ -55,7 +55,7 @@ solely client-side operations.
} }
[data-element="section-list-item"][data-js-is-viewed] { [data-element="section-list-item"][data-js-is-viewed] {
@apply text-gray-800; @apply text-gray-900;
} }
[data-element="cell"]:not([data-js-focused]):hover [data-element="cell"]:not([data-js-focused]):hover

View file

@ -64,4 +64,19 @@ monaco.editor.defineTheme("custom", {
}, },
}); });
// See https://github.com/microsoft/monaco-editor/issues/648#issuecomment-564978560
// Without this selecting text with whitespace shrinks the whitespace.
document.fonts.addEventListener("loadingdone", (event) => {
const jetBrainsMonoLoaded = event.fontfaces.some(
(fontFace) => fontFace.family === "JetBrains Mono"
);
if (jetBrainsMonoLoaded) {
// We use JetBrains Mono in all instances of the editor,
// so we wait until it loads and then tell Monaco to remeasure
// fonts and updates its cache.
monaco.editor.remeasureFonts();
}
});
export default monaco; export default monaco;

View file

@ -0,0 +1,36 @@
defmodule LivebookWeb.DeleteSessionComponent do
use LivebookWeb, :live_component
alias Livebook.SessionSupervisor
@impl true
def render(assigns) do
~L"""
<div class="p-6 pb-4 max-w-md w-screen flex flex-col space-y-8">
<h3 class="text-2xl font-semibold text-gray-800">
Delete session
</h3>
<p class="text-gray-700">
Are you sure you want to delete this section -
<span class="font-semibold">
<%= @session_summary.notebook_name %>
</span>?
This won't delete any persisted files.
</p>
<div class="mt-8 flex justify-end space-x-2">
<button class="button button-red" phx-click="delete" phx-target="<%= @myself %>">
<%= remix_icon("delete-bin-6-line", class: "align-middle mr-1") %>
Delete session
</button>
<%= live_patch "Cancel", to: @return_to, class: "button button-outlined-gray" %>
</div>
</div>
"""
end
@impl true
def handle_event("delete", %{}, socket) do
SessionSupervisor.delete_session(socket.assigns.session_summary.session_id)
{:noreply, push_redirect(socket, to: socket.assigns.return_to)}
end
end

View file

@ -17,6 +17,13 @@ defmodule LivebookWeb.HomeLive do
@impl true @impl true
def render(assigns) do def render(assigns) do
~L""" ~L"""
<%= if @live_action == :delete_session do %>
<%= live_modal @socket, LivebookWeb.DeleteSessionComponent,
id: :delete_session_modal,
return_to: Routes.home_path(@socket, :page),
session_summary: @session_summary %>
<% end %>
<div class="flex flex-grow h-full"> <div class="flex flex-grow h-full">
<div class="flex flex-col items-center space-y-6 px-3 py-8 bg-gray-900"> <div class="flex flex-col items-center space-y-6 px-3 py-8 bg-gray-900">
<%= live_patch to: Routes.home_path(@socket, :page) do %> <%= live_patch to: Routes.home_path(@socket, :page) do %>
@ -29,7 +36,7 @@ defmodule LivebookWeb.HomeLive do
<div class="text-2xl text-gray-800 font-semibold"> <div class="text-2xl text-gray-800 font-semibold">
Livebook Livebook
</div> </div>
<button class="button button-primary" <button class="button button-blue"
phx-click="new"> phx-click="new">
New Notebook New Notebook
</button> </button>
@ -43,7 +50,7 @@ defmodule LivebookWeb.HomeLive do
target: nil do %> target: nil do %>
<div class="flex justify-end space-x-2"> <div class="flex justify-end space-x-2">
<%= content_tag :button, <%= content_tag :button,
class: "button", class: "button button-outlined-gray",
phx_click: "fork", phx_click: "fork",
disabled: not path_forkable?(@path) do %> disabled: not path_forkable?(@path) do %>
<%= remix_icon("git-branch-line", class: "align-middle mr-1") %> <%= remix_icon("git-branch-line", class: "align-middle mr-1") %>
@ -51,10 +58,10 @@ defmodule LivebookWeb.HomeLive do
<% end %> <% end %>
<%= if path_running?(@path, @session_summaries) do %> <%= if path_running?(@path, @session_summaries) do %>
<%= live_patch "Join session", to: Routes.session_path(@socket, :page, session_id_by_path(@path, @session_summaries)), <%= live_patch "Join session", to: Routes.session_path(@socket, :page, session_id_by_path(@path, @session_summaries)),
class: "button button-primary" %> class: "button button-blue" %>
<% else %> <% else %>
<%= content_tag :button, "Open", <%= content_tag :button, "Open",
class: "button button-primary", class: "button button-blue",
phx_click: "open", phx_click: "open",
disabled: not path_openable?(@path, @session_summaries) %> disabled: not path_openable?(@path, @session_summaries) %>
<% end %> <% end %>
@ -89,6 +96,11 @@ defmodule LivebookWeb.HomeLive do
end end
@impl true @impl true
def handle_params(%{"session_id" => session_id}, _url, socket) do
session_summary = Enum.find(socket.assigns.session_summaries, &(&1.session_id == session_id))
{:noreply, assign(socket, session_summary: session_summary)}
end
def handle_params(_params, _url, socket), do: {:noreply, socket} def handle_params(_params, _url, socket), do: {:noreply, socket}
@impl true @impl true

View file

@ -5,20 +5,20 @@ defmodule LivebookWeb.InsertButtonsComponent do
~L""" ~L"""
<div class="relative top-0.5 m-0 flex justify-center"> <div class="relative top-0.5 m-0 flex justify-center">
<div class="absolute z-10 <%= if(@persistent, do: "opacity-100", else: "opacity-0") %> hover:opacity-100 focus-within:opacity-100 flex space-x-2 justify-center items-center"> <div class="absolute z-10 <%= if(@persistent, do: "opacity-100", else: "opacity-0") %> hover:opacity-100 focus-within:opacity-100 flex space-x-2 justify-center items-center">
<button class="button button-sm" <button class="button button-small"
phx-click="insert_cell" phx-click="insert_cell"
phx-value-type="markdown" phx-value-type="markdown"
phx-value-section_id="<%= @section_id %>" phx-value-section_id="<%= @section_id %>"
phx-value-index="<%= @insert_cell_index %>" phx-value-index="<%= @insert_cell_index %>"
>+ Markdown</button> >+ Markdown</button>
<button class="button button-sm" <button class="button button-small"
phx-click="insert_cell" phx-click="insert_cell"
phx-value-type="elixir" phx-value-type="elixir"
phx-value-section_id="<%= @section_id %>" phx-value-section_id="<%= @section_id %>"
phx-value-index="<%= @insert_cell_index %>" phx-value-index="<%= @insert_cell_index %>"
>+ Elixir</button> >+ Elixir</button>
<%= if @insert_section_index do %> <%= if @insert_section_index do %>
<button class="button button-sm" <button class="button button-small"
phx-click="insert_section" phx-click="insert_section"
phx-value-index="<%= @insert_section_index %>" phx-value-index="<%= @insert_section_index %>"
>+ Section</button> >+ Section</button>

View file

@ -95,26 +95,29 @@ defmodule LivebookWeb.SessionLive do
<% end %> <% end %>
</span> </span>
</div> </div>
<div class="flex flex-col w-1/5 bg-gray-50 border-r border-gray-100" data-element="sections-panel"> <div class="flex flex-col w-1/5 bg-gray-50 border-r border-gray-100 px-6 py-10" data-element="sections-panel">
<div class="flex-grow flex flex-col space-y-2 pl-4 pt-4" <div class="flex-grow flex flex-col">
data-element="section-list"> <h3 class="font-semibold text-gray-800 text-lg">
Sections
</h3>
<div class="mt-4 flex flex-col space-y-4" data-element="section-list">
<%= for section <- @data.notebook.sections do %> <%= for section <- @data.notebook.sections do %>
<button class="py-2 px-4 text-left hover:text-gray-800 text-gray-500" <button class="text-left hover:text-gray-900 text-gray-500"
data-element="section-list-item" data-element="section-list-item"
data-section-id="<%= section.id %>"> data-section-id="<%= section.id %>">
<%= section.name %> <%= section.name %>
</button> </button>
<% end %> <% end %>
<button phx-click="add_section" class="py-2 px-4 rounded-l-md cursor-pointer text-gray-300 hover:text-gray-400">
<div class="flex items-center space-x-2">
<%= remix_icon("add-line", class: "text-2xl") %>
<span>New section</span>
</div> </div>
<button class="mt-8 p-8 py-1 text-gray-500 text-sm font-medium rounded-xl border border-gray-400 border-dashed hover:bg-gray-100 inline-flex items-center justify-center space-x-2"
phx-click="add_section" >
<%= remix_icon("add-line", class: "text-lg align-center") %>
<span>New section</span>
</button> </button>
</div> </div>
</div> </div>
<div class="flex-grow overflow-y-auto" data-element="notebook"> <div class="flex-grow overflow-y-auto" data-element="notebook">
<div class="py-8 px-16 max-w-screen-lg w-full mx-auto"> <div class="py-7 px-16 max-w-screen-lg w-full mx-auto">
<div class="pb-4 mb-6 border-b border-gray-200"> <div class="pb-4 mb-6 border-b border-gray-200">
<h1 class="text-gray-800 font-semibold text-3xl p-1 -ml-1 rounded-lg border border-transparent hover:border-blue-200 focus:border-blue-300" <h1 class="text-gray-800 font-semibold text-3xl p-1 -ml-1 rounded-lg border border-transparent hover:border-blue-200 focus:border-blue-300"
id="notebook-name" id="notebook-name"
@ -127,7 +130,7 @@ defmodule LivebookWeb.SessionLive do
<div class="flex flex-col w-full space-y-16"> <div class="flex flex-col w-full space-y-16">
<%= if @data.notebook.sections == [] do %> <%= if @data.notebook.sections == [] do %>
<div class="flex justify-center"> <div class="flex justify-center">
<button class="button button-sm" <button class="button button-small"
phx-click="insert_section" phx-click="insert_section"
phx-value-index="0" phx-value-index="0"
>+ Section</button> >+ Section</button>

View file

@ -43,7 +43,7 @@ defmodule LivebookWeb.SessionLive.AttachedLive do
<%= text_input f, :name, value: @name, class: "input", <%= text_input f, :name, value: @name, class: "input",
placeholder: if(Livebook.Config.shortnames?, do: "test", else: "test@127.0.0.1") %> placeholder: if(Livebook.Config.shortnames?, do: "test", else: "test@127.0.0.1") %>
<%= submit "Connect", class: "mt-3 button button-primary" %> <%= submit "Connect", class: "mt-3 button button-blue" %>
</form> </form>
</div> </div>
""" """

View file

@ -31,8 +31,8 @@ defmodule LivebookWeb.SessionLive.CellSettingsComponent do
</div> </div>
</div> </div>
<div class="mt-8 flex justify-end space-x-2"> <div class="mt-8 flex justify-end space-x-2">
<%= live_patch "Cancel", to: @return_to, class: "button" %> <%= live_patch "Cancel", to: @return_to, class: "button button-outlined-gray" %>
<button class="button button-primary" type="submit"> <button class="button button-blue" type="submit">
Save Save
</button> </button>
</div> </div>

View file

@ -21,7 +21,7 @@ defmodule LivebookWeb.SessionLive.ElixirStandaloneLive do
This is the default runtime and is started automatically This is the default runtime and is started automatically
as soon as you evaluate the first cell. as soon as you evaluate the first cell.
</p> </p>
<button class="button button-primary" phx-click="init"> <button class="button button-blue" phx-click="init">
<%= if(matching_runtime?(@current_runtime), do: "Reconnect", else: "Connect") %> <%= if(matching_runtime?(@current_runtime), do: "Reconnect", else: "Connect") %>
</button> </button>
<%= if @output do %> <%= if @output do %>

View file

@ -41,7 +41,7 @@ defmodule LivebookWeb.SessionLive.MixStandaloneLive do
target: nil %> target: nil %>
</div> </div>
<%= content_tag :button, if(matching_runtime?(@current_runtime, @path), do: "Reconnect", else: "Connect"), <%= content_tag :button, if(matching_runtime?(@current_runtime, @path), do: "Reconnect", else: "Connect"),
class: "button button-primary", class: "button button-blue",
phx_click: "init", phx_click: "init",
disabled: not mix_project_root?(@path) %> disabled: not mix_project_root?(@path) %>
<% end %> <% end %>

View file

@ -46,7 +46,7 @@ defmodule LivebookWeb.SessionLive.PersistenceComponent do
<% end %> <% end %>
<div> <div>
<%= content_tag :button, "Save", <%= content_tag :button, "Save",
class: "button button-primary", class: "button button-blue",
phx_click: "save", phx_click: "save",
phx_target: @myself, phx_target: @myself,
disabled: not path_savable?(normalize_path(@path), @session_summaries) or normalize_path(@path) == @current_path %> disabled: not path_savable?(normalize_path(@path), @session_summaries) or normalize_path(@path) == @current_path %>

View file

@ -53,7 +53,7 @@ defmodule LivebookWeb.SessionLive.RuntimeComponent do
<%= @runtime.node %> <%= @runtime.node %>
</span> </span>
</div> </div>
<button class="button text-sm button-danger" <button class="button button-outlined-red"
type="button" type="button"
phx-click="disconnect" phx-click="disconnect"
phx-target="<%= @myself %>"> phx-target="<%= @myself %>">

View file

@ -28,13 +28,11 @@ defmodule LivebookWeb.SessionsComponent do
<%= remix_icon("git-branch-line") %> <%= remix_icon("git-branch-line") %>
<span class="font-medium">Fork</span> <span class="font-medium">Fork</span>
</button> </button>
<button class="flex space-x-3 px-5 py-2 items-center text-red-600 hover:bg-gray-50" <%= live_patch to: Routes.home_path(@socket, :delete_session, summary.session_id),
phx-click="delete_session" class: "flex space-x-3 px-5 py-2 items-center text-red-600 hover:bg-gray-50" do %>
phx-value-id="<%= summary.session_id %>"
phx-target="<%= @myself %>">
<%= remix_icon("delete-bin-6-line") %> <%= remix_icon("delete-bin-6-line") %>
<span class="font-medium">Delete</span> <span class="font-medium">Delete</span>
</button> <% end %>
</div> </div>
</div> </div>
</div> </div>

View file

@ -18,6 +18,7 @@ defmodule LivebookWeb.Router do
pipe_through :browser pipe_through :browser
live "/", HomeLive, :page live "/", HomeLive, :page
live "/home/sessions/:session_id/delete", HomeLive, :delete_session
live "/sessions/:id", SessionLive, :page live "/sessions/:id", SessionLive, :page
live "/sessions/:id/shortcuts", SessionLive, :shortcuts live "/sessions/:id/shortcuts", SessionLive, :shortcuts
live "/sessions/:id/settings/:tab", SessionLive, :settings live "/sessions/:id/settings/:tab", SessionLive, :settings