mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-09-10 23:14:35 +08:00
Reflect input change when clicking the reevaluate button (#1269)
This commit is contained in:
parent
1757e21bca
commit
75e47aa593
3 changed files with 49 additions and 63 deletions
|
@ -166,11 +166,14 @@ defmodule Livebook.Utils do
|
||||||
|
|
||||||
iex> Livebook.Utils.valid_url?("http://localhost")
|
iex> Livebook.Utils.valid_url?("http://localhost")
|
||||||
true
|
true
|
||||||
|
|
||||||
|
iex> Livebook.Utils.valid_url?("http://")
|
||||||
|
false
|
||||||
"""
|
"""
|
||||||
@spec valid_url?(String.t()) :: boolean()
|
@spec valid_url?(String.t()) :: boolean()
|
||||||
def valid_url?(url) do
|
def valid_url?(url) do
|
||||||
uri = URI.parse(url)
|
uri = URI.parse(url)
|
||||||
uri.scheme != nil and uri.host != nil
|
uri.scheme != nil and uri.host not in [nil, ""]
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
|
|
@ -3,7 +3,7 @@ defmodule LivebookWeb.Output.InputComponent do
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def mount(socket) do
|
def mount(socket) do
|
||||||
{:ok, assign(socket, error: nil, local: false)}
|
{:ok, assign(socket, local: false, counter: 0)}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
@ -13,7 +13,7 @@ defmodule LivebookWeb.Output.InputComponent do
|
||||||
socket =
|
socket =
|
||||||
socket
|
socket
|
||||||
|> assign(assigns)
|
|> assign(assigns)
|
||||||
|> assign(value: value, initial_value: value)
|
|> assign(value: value)
|
||||||
|
|
||||||
{:ok, socket}
|
{:ok, socket}
|
||||||
end
|
end
|
||||||
|
@ -21,7 +21,7 @@ defmodule LivebookWeb.Output.InputComponent do
|
||||||
@impl true
|
@impl true
|
||||||
def render(assigns) do
|
def render(assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<form phx-change="change" phx-submit="submit" phx-target={@myself}>
|
<form id={"#{@id}-form-#{@counter}"} phx-change="change" phx-submit="submit" phx-target={@myself}>
|
||||||
<div class="input-label">
|
<div class="input-label">
|
||||||
<%= @attrs.label %>
|
<%= @attrs.label %>
|
||||||
</div>
|
</div>
|
||||||
|
@ -30,14 +30,7 @@ defmodule LivebookWeb.Output.InputComponent do
|
||||||
id={"#{@id}-input"}
|
id={"#{@id}-input"}
|
||||||
attrs={@attrs}
|
attrs={@attrs}
|
||||||
value={@value}
|
value={@value}
|
||||||
error={@error}
|
|
||||||
myself={@myself} />
|
myself={@myself} />
|
||||||
|
|
||||||
<%= if @error do %>
|
|
||||||
<div class="input-error">
|
|
||||||
<%= @error %>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
</form>
|
</form>
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
@ -77,8 +70,7 @@ defmodule LivebookWeb.Output.InputComponent do
|
||||||
class="input-range"
|
class="input-range"
|
||||||
name="value"
|
name="value"
|
||||||
value={@value}
|
value={@value}
|
||||||
phx-debounce="300"
|
phx-debounce="blur"
|
||||||
phx-blur="blur"
|
|
||||||
phx-target={@myself}
|
phx-target={@myself}
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
|
@ -96,8 +88,7 @@ defmodule LivebookWeb.Output.InputComponent do
|
||||||
data-el-input
|
data-el-input
|
||||||
class="input min-h-[200px] tiny-scrollbar"
|
class="input min-h-[200px] tiny-scrollbar"
|
||||||
name="value"
|
name="value"
|
||||||
phx-debounce="300"
|
phx-debounce="blur"
|
||||||
phx-blur="blur"
|
|
||||||
phx-target={@myself}
|
phx-target={@myself}
|
||||||
spellcheck="false"><%= [?\n, @value] %></textarea>
|
spellcheck="false"><%= [?\n, @value] %></textarea>
|
||||||
"""
|
"""
|
||||||
|
@ -111,8 +102,7 @@ defmodule LivebookWeb.Output.InputComponent do
|
||||||
class="input w-auto bg-gray-50"
|
class="input w-auto bg-gray-50"
|
||||||
name="value"
|
name="value"
|
||||||
value={@value}
|
value={@value}
|
||||||
phx-debounce="300"
|
phx-debounce="blur"
|
||||||
phx-blur="blur"
|
|
||||||
phx-target={@myself}
|
phx-target={@myself}
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
autocomplete="off" />
|
autocomplete="off" />
|
||||||
|
@ -124,11 +114,10 @@ defmodule LivebookWeb.Output.InputComponent do
|
||||||
~H"""
|
~H"""
|
||||||
<input type={html_input_type(@attrs.type)}
|
<input type={html_input_type(@attrs.type)}
|
||||||
data-el-input
|
data-el-input
|
||||||
class={"input w-auto #{if(@error, do: "input--error")}"}
|
class="input w-auto invalid:input--error"
|
||||||
name="value"
|
name="value"
|
||||||
value={to_string(@value)}
|
value={to_string(@value)}
|
||||||
phx-debounce="300"
|
phx-debounce="blur"
|
||||||
phx-blur="blur"
|
|
||||||
phx-target={@myself}
|
phx-target={@myself}
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
autocomplete="off" />
|
autocomplete="off" />
|
||||||
|
@ -145,62 +134,56 @@ defmodule LivebookWeb.Output.InputComponent do
|
||||||
|
|
||||||
defp html_input_type(:number), do: "number"
|
defp html_input_type(:number), do: "number"
|
||||||
defp html_input_type(:color), do: "color"
|
defp html_input_type(:color), do: "color"
|
||||||
defp html_input_type(:url), do: "text"
|
defp html_input_type(:url), do: "url"
|
||||||
defp html_input_type(:text), do: "text"
|
defp html_input_type(:text), do: "text"
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("change", %{"value" => html_value}, socket) do
|
def handle_event("change", %{"value" => html_value}, socket) do
|
||||||
socket = handle_html_value(socket, html_value)
|
case handle_change(socket, html_value) do
|
||||||
|
{:ok, socket} ->
|
||||||
|
{:noreply, socket}
|
||||||
|
|
||||||
socket =
|
:error ->
|
||||||
if report_immediately?(socket.assigns.attrs.type) do
|
# Force the current value
|
||||||
maybe_report_change(socket)
|
{:noreply, update(socket, :counter, &(&1 + 1))}
|
||||||
else
|
|
||||||
socket
|
|
||||||
end
|
|
||||||
|
|
||||||
{:noreply, socket}
|
|
||||||
end
|
|
||||||
|
|
||||||
def handle_event("blur", %{"value" => html_value}, socket) do
|
|
||||||
socket = socket |> handle_html_value(html_value) |> maybe_report_change()
|
|
||||||
|
|
||||||
if socket.assigns.error do
|
|
||||||
{:noreply, assign(socket, value: socket.assigns.initial_value, error: nil)}
|
|
||||||
else
|
|
||||||
{:noreply, socket}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_event("submit", %{"value" => html_value}, socket) do
|
def handle_event("submit", %{"value" => html_value}, socket) do
|
||||||
socket = socket |> handle_html_value(html_value) |> maybe_report_change()
|
case handle_change(socket, html_value) do
|
||||||
send(self(), {:queue_bound_cells_evaluation, socket.assigns.attrs.id})
|
{:ok, socket} ->
|
||||||
{:noreply, socket}
|
send(self(), {:queue_bound_cells_evaluation, socket.assigns.attrs.id})
|
||||||
end
|
{:noreply, socket}
|
||||||
|
|
||||||
defp handle_html_value(socket, html_value) do
|
:error ->
|
||||||
case parse(html_value, socket.assigns.attrs) do
|
{:noreply, socket}
|
||||||
{:ok, value} ->
|
|
||||||
assign(socket, value: value, error: nil)
|
|
||||||
|
|
||||||
{:error, error, value} ->
|
|
||||||
assign(socket, value: value, error: error)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp report_immediately?(type), do: type in [:select, :checkbox]
|
defp handle_change(socket, html_value) do
|
||||||
|
case parse(html_value, socket.assigns.attrs) do
|
||||||
|
{:ok, value} ->
|
||||||
|
prev_value = socket.assigns.value
|
||||||
|
|
||||||
defp maybe_report_change(socket) when socket.assigns.error != nil, do: socket
|
socket = assign(socket, value: value)
|
||||||
defp maybe_report_change(%{assigns: %{value: value, initial_value: value}} = socket), do: socket
|
|
||||||
|
|
||||||
defp maybe_report_change(%{assigns: assigns} = socket) do
|
if value != prev_value do
|
||||||
|
report_change(socket)
|
||||||
|
end
|
||||||
|
|
||||||
|
{:ok, socket}
|
||||||
|
|
||||||
|
{:error, _error} ->
|
||||||
|
:error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp report_change(%{assigns: assigns} = socket) do
|
||||||
send(self(), {:set_input_values, [{assigns.attrs.id, assigns.value}], assigns.local})
|
send(self(), {:set_input_values, [{assigns.attrs.id, assigns.value}], assigns.local})
|
||||||
|
|
||||||
unless assigns.local do
|
unless assigns.local do
|
||||||
report_event(socket, assigns.value)
|
report_event(socket, assigns.value)
|
||||||
end
|
end
|
||||||
|
|
||||||
socket
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp parse(html_value, %{type: :text}) do
|
defp parse(html_value, %{type: :text}) do
|
||||||
|
@ -236,7 +219,7 @@ defmodule LivebookWeb.Output.InputComponent do
|
||||||
cond do
|
cond do
|
||||||
html_value == "" -> {:ok, nil}
|
html_value == "" -> {:ok, nil}
|
||||||
Livebook.Utils.valid_url?(html_value) -> {:ok, html_value}
|
Livebook.Utils.valid_url?(html_value) -> {:ok, html_value}
|
||||||
true -> {:error, "not a valid URL", html_value}
|
true -> {:error, "not a valid URL"}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -245,8 +245,8 @@ defmodule LivebookWeb.SessionLiveTest do
|
||||||
{:ok, view, _} = live(conn, "/sessions/#{session.id}")
|
{:ok, view, _} = live(conn, "/sessions/#{session.id}")
|
||||||
|
|
||||||
view
|
view
|
||||||
|> element(~s/[data-el-outputs-container] input/)
|
|> element(~s/[data-el-outputs-container] form/)
|
||||||
|> render_blur(%{"value" => "10"})
|
|> render_change(%{"value" => "10"})
|
||||||
|
|
||||||
assert %{input_values: %{"input1" => 10}} = Session.get_data(session.pid)
|
assert %{input_values: %{"input1" => 10}} = Session.get_data(session.pid)
|
||||||
|
|
||||||
|
@ -274,8 +274,8 @@ defmodule LivebookWeb.SessionLiveTest do
|
||||||
{:ok, view, _} = live(conn, "/sessions/#{session.id}")
|
{:ok, view, _} = live(conn, "/sessions/#{session.id}")
|
||||||
|
|
||||||
view
|
view
|
||||||
|> element(~s/[data-el-outputs-container] textarea/)
|
|> element(~s/[data-el-outputs-container] form/)
|
||||||
|> render_blur(%{"value" => "line\r\nline"})
|
|> render_change(%{"value" => "line\r\nline"})
|
||||||
|
|
||||||
assert %{input_values: %{"input1" => "line\nline"}} = Session.get_data(session.pid)
|
assert %{input_values: %{"input1" => "line\nline"}} = Session.get_data(session.pid)
|
||||||
end
|
end
|
||||||
|
@ -312,8 +312,8 @@ defmodule LivebookWeb.SessionLiveTest do
|
||||||
{:ok, view, _} = live(conn, "/sessions/#{session.id}")
|
{:ok, view, _} = live(conn, "/sessions/#{session.id}")
|
||||||
|
|
||||||
view
|
view
|
||||||
|> element(~s/[data-el-outputs-container] input/)
|
|> element(~s/[data-el-outputs-container] form/)
|
||||||
|> render_blur(%{"value" => "sherlock"})
|
|> render_change(%{"value" => "sherlock"})
|
||||||
|
|
||||||
# The new value is on the page
|
# The new value is on the page
|
||||||
assert render(view) =~ "sherlock"
|
assert render(view) =~ "sherlock"
|
||||||
|
|
Loading…
Add table
Reference in a new issue