2022-01-17 03:37:00 +08:00
|
|
|
defmodule LivebookWeb.Output.FrameComponent do
|
|
|
|
use LivebookWeb, :live_component
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def mount(socket) do
|
2022-01-19 21:43:35 +08:00
|
|
|
{:ok, assign(socket, counter: 0, output_count: 0, persistent_id_map: %{})}
|
2022-01-17 03:37:00 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
@impl true
|
|
|
|
def update(assigns, socket) do
|
|
|
|
{update_type, assigns} = Map.pop(assigns, :update_type, nil)
|
|
|
|
{outputs, assigns} = Map.pop!(assigns, :outputs)
|
|
|
|
|
|
|
|
socket = assign(socket, assigns)
|
|
|
|
|
|
|
|
socket =
|
|
|
|
if socket.assigns.counter == 0 do
|
2022-01-18 22:13:50 +08:00
|
|
|
assign(socket,
|
|
|
|
counter: 1,
|
|
|
|
output_count: length(outputs),
|
2022-01-19 21:43:35 +08:00
|
|
|
persistent_id_map: map_idx_to_persistent_id(outputs, socket.assigns.id)
|
2022-01-18 22:13:50 +08:00
|
|
|
)
|
2022-01-17 03:37:00 +08:00
|
|
|
else
|
|
|
|
socket
|
|
|
|
end
|
|
|
|
|
|
|
|
socket =
|
|
|
|
case update_type do
|
|
|
|
nil ->
|
|
|
|
assign(socket, outputs: outputs)
|
|
|
|
|
|
|
|
:replace ->
|
2022-01-18 22:13:50 +08:00
|
|
|
prev_output_count = socket.assigns.output_count
|
2022-01-19 21:43:35 +08:00
|
|
|
prev_persistent_id_map = socket.assigns.persistent_id_map
|
2022-01-18 22:13:50 +08:00
|
|
|
|
|
|
|
output_count = length(outputs)
|
2022-01-19 21:43:35 +08:00
|
|
|
persistent_id_map = map_idx_to_persistent_id(outputs, socket.assigns.id)
|
2022-01-18 22:13:50 +08:00
|
|
|
|
|
|
|
socket =
|
|
|
|
assign(socket,
|
|
|
|
outputs: outputs,
|
|
|
|
output_count: output_count,
|
2022-01-19 21:43:35 +08:00
|
|
|
persistent_id_map: persistent_id_map
|
2022-01-18 22:13:50 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
less_outputs? = prev_output_count > output_count
|
2022-01-19 21:43:35 +08:00
|
|
|
appended_outputs? = prev_output_count > map_size(prev_persistent_id_map)
|
2022-01-18 22:13:50 +08:00
|
|
|
|
|
|
|
# If there are outputs that we need to remove, increase the counter.
|
2022-01-19 21:43:35 +08:00
|
|
|
# Otherwise we reuse DOM element ids via persistent_id_map
|
2022-01-18 22:13:50 +08:00
|
|
|
if less_outputs? or appended_outputs? do
|
|
|
|
update(socket, :counter, &(&1 + 1))
|
|
|
|
else
|
|
|
|
socket
|
|
|
|
end
|
2022-01-17 03:37:00 +08:00
|
|
|
|
|
|
|
:append ->
|
|
|
|
socket
|
|
|
|
|> update(:outputs, &(outputs ++ &1))
|
2022-01-18 22:13:50 +08:00
|
|
|
|> update(:output_count, &(length(outputs) + &1))
|
2022-01-17 03:37:00 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
{:ok, socket}
|
|
|
|
end
|
|
|
|
|
2022-01-19 21:43:35 +08:00
|
|
|
defp map_idx_to_persistent_id(outputs, root_id) do
|
2022-01-18 22:13:50 +08:00
|
|
|
outputs
|
|
|
|
|> Enum.with_index()
|
2022-01-19 21:43:35 +08:00
|
|
|
|> Map.new(fn {{output_idx, _}, idx} -> {output_idx, "#{root_id}-#{idx}"} end)
|
2022-01-18 22:13:50 +08:00
|
|
|
end
|
|
|
|
|
2022-01-17 03:37:00 +08:00
|
|
|
@impl true
|
|
|
|
def render(assigns) do
|
|
|
|
~H"""
|
|
|
|
<div id={"frame-output-#{@id}"}>
|
2022-01-18 22:13:50 +08:00
|
|
|
<%= if @output_count == 0 do %>
|
2022-01-17 03:37:00 +08:00
|
|
|
<div class="text-gray-300 p-4 rounded-lg border border-gray-200">
|
|
|
|
Empty output frame
|
|
|
|
</div>
|
|
|
|
<% else %>
|
|
|
|
<div id={"frame-outputs-#{@id}-#{@counter}"} phx-update="append">
|
|
|
|
<LivebookWeb.Output.outputs
|
|
|
|
outputs={@outputs}
|
2022-01-19 21:43:35 +08:00
|
|
|
dom_id_map={@persistent_id_map}
|
2022-01-17 03:37:00 +08:00
|
|
|
socket={@socket}
|
|
|
|
session_id={@session_id}
|
2022-04-04 18:49:17 +08:00
|
|
|
input_values={@input_values} />
|
2022-01-17 03:37:00 +08:00
|
|
|
</div>
|
|
|
|
<% end %>
|
|
|
|
</div>
|
|
|
|
"""
|
|
|
|
end
|
|
|
|
end
|