mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-11-10 06:01:44 +08:00
Insert section (#114)
* Add section insertion button * Add shortcut for inserting section * Pass buttons to the component * Add mix alias for running both formatter * Adjustments * Make the buttons group component stateless
This commit is contained in:
parent
50db813092
commit
fe5dfe3b86
8 changed files with 77 additions and 42 deletions
|
|
@ -5,7 +5,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.button:not(:disabled) {
|
.button:not(:disabled) {
|
||||||
@apply hover:bg-gray-50 focus:bg-gray-50;
|
@apply hover:bg-gray-100 focus:bg-gray-100;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button:disabled {
|
.button:disabled {
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-sm {
|
.button-sm {
|
||||||
@apply px-3 py-1;
|
@apply px-2 py-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-danger {
|
.button-danger {
|
||||||
|
|
|
||||||
|
|
@ -169,6 +169,8 @@ function handleDocumentKeyDown(hook, event) {
|
||||||
insertCellBelowFocused(hook, "markdown");
|
insertCellBelowFocused(hook, "markdown");
|
||||||
} else if (keyBuffer.tryMatch(["M"])) {
|
} else if (keyBuffer.tryMatch(["M"])) {
|
||||||
insertCellAboveFocused(hook, "markdown");
|
insertCellAboveFocused(hook, "markdown");
|
||||||
|
} else if (keyBuffer.tryMatch(["S"])) {
|
||||||
|
addSection(hook);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -380,6 +382,10 @@ function insertCellAboveFocused(hook, type) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addSection(hook) {
|
||||||
|
hook.pushEvent("add_section", {});
|
||||||
|
}
|
||||||
|
|
||||||
function insertFirstCell(hook, type) {
|
function insertFirstCell(hook, type) {
|
||||||
const sectionIds = getSectionIds();
|
const sectionIds = getSectionIds();
|
||||||
|
|
||||||
|
|
|
||||||
13
lib/livebook_web/live/insert_buttons_component.ex
Normal file
13
lib/livebook_web/live/insert_buttons_component.ex
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
defmodule LivebookWeb.InsertButtonsComponent do
|
||||||
|
use LivebookWeb, :live_component
|
||||||
|
|
||||||
|
def render(assigns) do
|
||||||
|
~L"""
|
||||||
|
<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">
|
||||||
|
<%= render_block(@inner_block) %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
defmodule LivebookWeb.InsertCellComponent do
|
|
||||||
use LivebookWeb, :live_component
|
|
||||||
|
|
||||||
def render(assigns) do
|
|
||||||
~L"""
|
|
||||||
<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">
|
|
||||||
<button class="py-1 px-2 text-sm text-gray-600 font-medium rounded-lg border border-gray-200 bg-gray-50 hover:bg-gray-100 focus:bg-gray-100"
|
|
||||||
phx-click="insert_cell"
|
|
||||||
phx-value-type="markdown"
|
|
||||||
phx-value-section_id="<%= @section_id %>"
|
|
||||||
phx-value-index="<%= @index %>">
|
|
||||||
+ Markdown
|
|
||||||
</button>
|
|
||||||
<button class="py-1 px-2 text-sm text-gray-600 font-medium rounded-lg border border-gray-200 bg-gray-50 hover:bg-gray-100 focus:bg-gray-100"
|
|
||||||
phx-click="insert_cell"
|
|
||||||
phx-value-type="elixir"
|
|
||||||
phx-value-section_id="<%= @section_id %>"
|
|
||||||
phx-value-index="<%= @index %>">
|
|
||||||
+ Elixir
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
"""
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -26,22 +26,46 @@ defmodule LivebookWeb.SectionComponent do
|
||||||
</div>
|
</div>
|
||||||
<div class="container py-2">
|
<div class="container py-2">
|
||||||
<div class="flex flex-col space-y-1">
|
<div class="flex flex-col space-y-1">
|
||||||
<%= live_component @socket, LivebookWeb.InsertCellComponent,
|
|
||||||
id: "#{@section.id}:0",
|
|
||||||
section_id: @section.id,
|
|
||||||
index: 0,
|
|
||||||
persistent: @section.cells == [] %>
|
|
||||||
<%= for {cell, index} <- Enum.with_index(@section.cells) do %>
|
<%= for {cell, index} <- Enum.with_index(@section.cells) do %>
|
||||||
|
<%= live_component @socket, LivebookWeb.InsertButtonsComponent,
|
||||||
|
persistent: false do %>
|
||||||
|
<button class="button button-sm"
|
||||||
|
phx-click="insert_cell"
|
||||||
|
phx-value-type="markdown"
|
||||||
|
phx-value-section_id="<%= @section.id %>"
|
||||||
|
phx-value-index="<%= index %>"
|
||||||
|
>+ Markdown</button>
|
||||||
|
<button class="button button-sm"
|
||||||
|
phx-click="insert_cell"
|
||||||
|
phx-value-type="elixir"
|
||||||
|
phx-value-section_id="<%= @section.id %>"
|
||||||
|
phx-value-index="<%= index %>"
|
||||||
|
>+ Elixir</button>
|
||||||
|
<% end %>
|
||||||
<%= live_component @socket, LivebookWeb.CellComponent,
|
<%= live_component @socket, LivebookWeb.CellComponent,
|
||||||
id: cell.id,
|
id: cell.id,
|
||||||
session_id: @session_id,
|
session_id: @session_id,
|
||||||
cell: cell,
|
cell: cell,
|
||||||
cell_info: @cell_infos[cell.id] %>
|
cell_info: @cell_infos[cell.id] %>
|
||||||
<%= live_component @socket, LivebookWeb.InsertCellComponent,
|
<% end %>
|
||||||
id: "#{@section.id}:#{index + 1}",
|
<%= live_component @socket, LivebookWeb.InsertButtonsComponent,
|
||||||
section_id: @section.id,
|
persistent: @section.cells == [] do %>
|
||||||
index: index + 1,
|
<button class="button button-sm"
|
||||||
persistent: false %>
|
phx-click="insert_cell"
|
||||||
|
phx-value-type="markdown"
|
||||||
|
phx-value-section_id="<%= @section.id %>"
|
||||||
|
phx-value-index="<%= length(@section.cells) %>"
|
||||||
|
>+ Markdown</button>
|
||||||
|
<button class="button button-sm"
|
||||||
|
phx-click="insert_cell"
|
||||||
|
phx-value-type="elixir"
|
||||||
|
phx-value-section_id="<%= @section.id %>"
|
||||||
|
phx-value-index="<%= length(@section.cells) %>"
|
||||||
|
>+ Elixir</button>
|
||||||
|
<button class="button button-sm"
|
||||||
|
phx-click="insert_section"
|
||||||
|
phx-value-index="<%= @index + 1 %>"
|
||||||
|
>+ Section</button>
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -83,14 +83,14 @@ defmodule LivebookWeb.SessionLive do
|
||||||
</span>
|
</span>
|
||||||
<span class="tooltip right distant" aria-label="Notebook settings (sn)">
|
<span class="tooltip right distant" aria-label="Notebook settings (sn)">
|
||||||
<%= live_patch to: Routes.session_path(@socket, :settings, @session_id, "file"),
|
<%= live_patch to: Routes.session_path(@socket, :settings, @session_id, "file"),
|
||||||
class: "text-gray-600 hover:text-gray-50 focus:text-gray-50 #{if(@live_action == :settings, do: "text-gray-50")}" do %>
|
class: "text-gray-600 hover:text-gray-50 focus:text-gray-50 #{if(@live_action == :settings, do: "text-gray-50")}" do %>
|
||||||
<%= remix_icon("settings-4-fill", class: "text-2xl") %>
|
<%= remix_icon("settings-4-fill", class: "text-2xl") %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</span>
|
</span>
|
||||||
<div class="flex-grow"></div>
|
<div class="flex-grow"></div>
|
||||||
<span class="tooltip right distant" aria-label="Keyboard shortcuts (?)">
|
<span class="tooltip right distant" aria-label="Keyboard shortcuts (?)">
|
||||||
<%= live_patch to: Routes.session_path(@socket, :shortcuts, @session_id),
|
<%= live_patch to: Routes.session_path(@socket, :shortcuts, @session_id),
|
||||||
class: "text-gray-600 hover:text-gray-50 focus:text-gray-50 #{if(@live_action == :shortcuts, do: "text-gray-50")}" do %>
|
class: "text-gray-600 hover:text-gray-50 focus:text-gray-50 #{if(@live_action == :shortcuts, do: "text-gray-50")}" do %>
|
||||||
<%= remix_icon("keyboard-box-fill", class: "text-2xl") %>
|
<%= remix_icon("keyboard-box-fill", class: "text-2xl") %>
|
||||||
<% end %>
|
<% end %>
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -125,9 +125,18 @@ defmodule LivebookWeb.SessionLive do
|
||||||
data-update-attribute="phx-value-name"><%= @data.notebook.name %></h1>
|
data-update-attribute="phx-value-name"><%= @data.notebook.name %></h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col w-full space-y-16">
|
<div class="flex flex-col w-full space-y-16">
|
||||||
<%= for section <- @data.notebook.sections do %>
|
<%= if @data.notebook.sections == [] do %>
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<button class="button button-sm"
|
||||||
|
phx-click="insert_section"
|
||||||
|
phx-value-index="0"
|
||||||
|
>+ Section</button>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<%= for {section, index} <- Enum.with_index(@data.notebook.sections) do %>
|
||||||
<%= live_component @socket, LivebookWeb.SectionComponent,
|
<%= live_component @socket, LivebookWeb.SectionComponent,
|
||||||
id: section.id,
|
id: section.id,
|
||||||
|
index: index,
|
||||||
session_id: @session_id,
|
session_id: @session_id,
|
||||||
section: section,
|
section: section,
|
||||||
cell_infos: @data.cell_infos %>
|
cell_infos: @data.cell_infos %>
|
||||||
|
|
@ -183,6 +192,13 @@ defmodule LivebookWeb.SessionLive do
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_event("insert_section", %{"index" => index}, socket) do
|
||||||
|
index = ensure_integer(index) |> max(0)
|
||||||
|
Session.insert_section(socket.assigns.session_id, index)
|
||||||
|
|
||||||
|
{:noreply, socket}
|
||||||
|
end
|
||||||
|
|
||||||
def handle_event("delete_section", %{"section_id" => section_id}, socket) do
|
def handle_event("delete_section", %{"section_id" => section_id}, socket) do
|
||||||
Session.delete_section(socket.assigns.session_id, section_id)
|
Session.delete_section(socket.assigns.session_id, section_id)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ defmodule LivebookWeb.SessionLive.ShortcutsComponent do
|
||||||
%{seq: ["N"], desc: "Insert Elixir cell above"},
|
%{seq: ["N"], desc: "Insert Elixir cell above"},
|
||||||
%{seq: ["M"], desc: "Insert Markdown cell above"},
|
%{seq: ["M"], desc: "Insert Markdown cell above"},
|
||||||
%{seq: ["dd"], desc: "Delete cell"},
|
%{seq: ["dd"], desc: "Delete cell"},
|
||||||
|
%{seq: ["S"], desc: "Add section"},
|
||||||
%{seq: ["ee"], desc: "Evaluate cell"},
|
%{seq: ["ee"], desc: "Evaluate cell"},
|
||||||
%{seq: ["es"], desc: "Evaluate section"},
|
%{seq: ["es"], desc: "Evaluate section"},
|
||||||
%{seq: ["ea"], desc: "Evaluate all stale/new cells"},
|
%{seq: ["ea"], desc: "Evaluate all stale/new cells"},
|
||||||
|
|
|
||||||
3
mix.exs
3
mix.exs
|
|
@ -59,7 +59,8 @@ defmodule Livebook.MixProject do
|
||||||
setup: ["deps.get", "cmd npm install --prefix assets"],
|
setup: ["deps.get", "cmd npm install --prefix assets"],
|
||||||
# Update the assets bundle to be committed into the repository
|
# Update the assets bundle to be committed into the repository
|
||||||
# and also builds the Escript.
|
# and also builds the Escript.
|
||||||
build: ["cmd npm run deploy --prefix ./assets", "escript.build"]
|
build: ["cmd npm run deploy --prefix ./assets", "escript.build"],
|
||||||
|
"format.all": ["format", "cmd npm run format --prefix ./assets"]
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue