Packages
livebook
0.16.3
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
import LivebookWeb.NotebookComponents
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)
{:ok,
socket
|> assign(:bin_entries, bin_entries)
|> assign(assigns)
|> assign_matching_entries()}
end
@impl true
def render(assigns) do
~H"""
<div class="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">
<div class="flex justify-between items-center">
<p class="text-gray-700 basis-4/5">
Find all your deleted cells from this notebook session
</p>
<form
:if={@bin_entries != []}
phx-change="search"
phx-nosubmit
phx-target={@myself}
class="mt-1 relative"
>
<span class="absolute inset-y-0 left-0 pl-3 flex items-center">
<.remix_icon icon="search-line" class="align-bottom text-gray-500" />
</span>
<.text_field
class="pl-10"
name="search"
value={@search}
placeholder="Search"
autocomplete="off"
spellcheck="false"
autofocus
/>
</form>
</div>
<%= cond do %>
<% @bin_entries == [] -> %>
<div class="p-5 py-24 flex flex-col gap-3 space-x-4 items-center">
<div>
<.remix_icon
icon="delete-bin-6-line"
class="text-gray-700 text-xl bg-gray-100 p-3 rounded-full"
/>
</div>
<div class="w-64 text-gray-600 text-center">
You haven't deleted any cells or sections yet. Once you do, they'll appear here.
</div>
</div>
<% @matching_entries == [] -> %>
<div class="p-5 py-24 flex flex-col gap-3 space-x-4 items-center">
<div>
<.remix_icon
icon="file-search-line"
class="text-gray-700 text-xl bg-gray-100 p-3 rounded-full"
/>
</div>
<div class="w-64 text-gray-600 text-center">
We couldn't find any results for your query.
</div>
</div>
<% true -> %>
<div class="flex flex-col space-y-8 min-h-[30rem] pt-3">
<div
:for={%{cell: cell} = entry <- Enum.take(@matching_entries, @limit)}
class="flex flex-col space-y-1"
>
<div class="flex justify-between items-center">
<div class="flex py-1">
<.cell_icon cell_type={Cell.type(cell)} language={Map.get(cell, :language)} />
<p class="ml-1 text-sm text-gray-700 self-center">
<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
</p>
</div>
<div class="flex justify-end space-x-2">
<span class="text-sm text-gray-500">
{LivebookWeb.HTMLHelpers.format_datetime_relatively(entry.deleted_at)} ago
</span>
</div>
</div>
<.code_preview
source_id={"bin-cell-#{cell.id}-source"}
language={cell_language(cell)}
source={cell.source}
/>
<div class="pt-1 pb-4 border-b border-gray-200">
<.button
color="gray"
small
aria-label="restore"
phx-click="restore"
phx-value-cell_id={entry.cell.id}
phx-target={@myself}
>
<.remix_icon icon="arrow-go-back-line" />
<span>Restore</span>
</.button>
<.button
color="gray"
small
aria-label="copy source"
phx-click={JS.dispatch("lb:clipcopy", to: "#bin-cell-#{cell.id}-source")}
>
<.remix_icon icon="clipboard-line" />
<span>Copy source</span>
</.button>
</div>
</div>
<div :if={length(@matching_entries) > @limit} class="flex justify-center">
<.button color="gray" outlined phx-click="more" phx-target={@myself}>
Older
</.button>
</div>
</div>
<% end %>
</div>
</div>
"""
end
defp cell_language(%Cell.Markdown{}), do: "markdown"
defp cell_language(%Cell.Code{language: language}), do: Atom.to_string(language)
defp cell_language(%Cell.Smart{}), do: "elixir"
@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.pid, 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