Packages
livebook
0.2.2
0.19.8
0.19.7
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.6
0.18.5
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
0.17.3
0.17.2
0.17.1
0.17.0
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.7
0.14.6
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.14.0
0.14.0-rc.1
0.14.0-rc.0
0.13.3
0.13.2
0.13.1
0.13.0
0.12.1
0.12.0
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.3
0.9.2
0.9.1
0.9.0
0.8.2
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.3
0.6.2
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.1
0.4.0
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.2
0.1.1
0.1.0
Automate code & data workflows with interactive notebooks
Current section
Files
Jump to
Current section
Files
lib/livebook_web/live/session_live/bin_component.ex
defmodule LivebookWeb.SessionLive.BinComponent do
use LivebookWeb, :live_component
alias Livebook.Notebook.Cell
@initial_limit 10
@limit_step 10
@impl true
def mount(socket) do
{:ok, assign(socket, search: "", limit: @initial_limit)}
end
@impl true
def update(assigns, socket) do
{bin_entries, assigns} = Map.pop(assigns, :bin_entries)
# Only show text cells, as they have an actual content
bin_entries =
Enum.filter(bin_entries, fn entry ->
Cell.type(entry.cell) in [:markdown, :elixir]
end)
{:ok,
socket
|> assign(:bin_entries, bin_entries)
|> assign(assigns)
|> assign_matching_entries()}
end
@impl true
def render(assigns) do
~L"""
<div class="p-6 max-w-4xl flex flex-col space-y-3">
<h3 class="text-2xl font-semibold text-gray-800">
Bin
</h3>
<div class="w-full flex-col space-y-5">
<p class="text-gray-700">
Here you can find all the deleted cells from this notebook session.
</p>
<%= if @bin_entries == [] do %>
<div class="p-5 flex space-x-4 items-center border border-gray-200 rounded-lg">
<div>
<%= remix_icon("windy-line", class: "text-gray-400 text-xl") %>
</div>
<div class="text-gray-600">
There are currently no cells in the bin.
</div>
</div>
<% else %>
<form phx-change="search" onsubmit="return false" phx-target="<%= @myself %>">
<input class="input"
type="text"
name="search"
value="<%= @search %>"
placeholder="Search"
autocomplete="off"
spellcheck="false"
autofocus />
</form>
<div class="flex flex-col space-y-8 min-h-[30rem] pt-3">
<%= for {%{cell: cell} = entry, index} <- Enum.take(@matching_entries, @limit) |> Enum.with_index(1) do %>
<div class="flex flex-col space-y-1">
<div class="flex justify-between items-center">
<p class="text-sm text-gray-700">
<%= index %>.
<span class="font-semibold"><%= Cell.type(cell) |> Atom.to_string() |> String.capitalize() %></span> cell
deleted from <span class="font-semibold">“<%= entry.section_name %>”</span> section
<span class="font-semibold">
<%= entry.deleted_at |> DateTime.to_naive() |> Livebook.Utils.Time.time_ago_in_words() %> ago
</span>
</p>
<div class="flex justify-end space-x-2">
<span class="tooltip left" aria-label="Copy source">
<button class="icon-button"
id="bin-cell-<%= cell.id %>-clipcopy"
phx-hook="ClipCopy"
data-target-id="bin-cell-<%= cell.id %>-source">
<%= remix_icon("clipboard-line", class: "text-lg") %>
</button>
</span>
<span class="tooltip left" aria-label="Restore">
<button class="icon-button"
phx-click="restore"
phx-value-cell_id="<%= entry.cell.id %>"
phx-target="<%= @myself %>">
<%= remix_icon("arrow-go-back-line", class: "text-lg") %>
</button>
</span>
</div>
</div>
<div class="markdown">
<pre><code
id="bin-cell-<%= cell.id %>-source"
phx-hook="Highlight"
data-language="<%= Cell.type(cell) %>"><%= cell.source %></code></pre>
</div>
</div>
<% end %>
<%= if length(@matching_entries) > @limit do %>
<div class="flex justify-center">
<button class="button button-outlined-gray" phx-click="more" phx-target="<%= @myself %>">
Older
</button>
</div>
<% end %>
</div>
<% end %>
</div>
</div>
"""
end
@impl true
def handle_event("search", %{"search" => search}, socket) do
{:noreply, assign(socket, search: search, limit: @initial_limit) |> assign_matching_entries()}
end
def handle_event("more", %{}, socket) do
{:noreply, assign(socket, limit: socket.assigns.limit + @limit_step)}
end
def handle_event("restore", %{"cell_id" => cell_id}, socket) do
Livebook.Session.restore_cell(socket.assigns.session_id, cell_id)
{:noreply, push_patch(socket, to: socket.assigns.return_to)}
end
defp assign_matching_entries(socket) do
matching_entries = filter_matching(socket.assigns.bin_entries, socket.assigns.search)
assign(socket, matching_entries: matching_entries)
end
defp filter_matching(entries, search) do
parts =
search
|> String.split()
|> Enum.map(fn part -> ~r/#{Regex.escape(part)}/i end)
Enum.filter(entries, fn entry ->
Enum.all?(parts, fn part ->
entry.cell.source =~ part
end)
end)
end
end