"""
end
def render(assigns) when assigns.input.attrs.type == :utc_datetime do
~H"""
<.input_label
label={@input.attrs.label}
changed={@changed}
help="Choose the time in your local time zone"
/>
"""
end
def render(assigns) when assigns.input.attrs.type == :utc_time do
~H"""
<.input_label
label={@input.attrs.label}
changed={@changed}
help="Choose the time in your local time zone"
/>
"""
end
def render(assigns) do
~H"""
"""
end
defp input_output(%{attrs: %{type: :select}} = assigns) do
~H"""
"""
end
defp input_output(%{attrs: %{type: :checkbox}} = assigns) do
~H"""
"""
end
defp input_output(%{attrs: %{type: :range}} = assigns) do
~H"""
<%= @attrs.min %>
<%= @attrs.max %>
"""
end
defp input_output(%{attrs: %{type: :textarea}} = assigns) do
~H"""
"""
end
defp input_output(%{attrs: %{type: :password}} = assigns) do
~H"""
<.with_password_toggle id={"#{@id}-password-toggle"}>
"""
end
defp input_output(%{attrs: %{type: :date}} = assigns) do
~H"""
"""
end
defp input_output(%{attrs: %{type: type}} = assigns)
when type in [:number, :color, :url, :text] do
~H"""
"""
end
defp input_output(assigns) do
~H"""
"""
end
defp html_input_type(:number), do: "number"
defp html_input_type(:color), do: "color"
defp html_input_type(:url), do: "url"
defp html_input_type(:text), do: "text"
@impl true
def handle_event("change", %{"html_value" => html_value}, socket) do
case parse(html_value, socket.assigns.input.attrs) do
{:ok, value} ->
{:noreply, handle_change(socket, value)}
:error ->
# Force the current value
{:noreply, update(socket, :counter, &(&1 + 1))}
end
end
def handle_event("submit", %{"html_value" => html_value}, socket) do
case parse(html_value, socket.assigns.input.attrs) do
{:ok, value} ->
socket = handle_change(socket, value)
send(self(), {:queue_bound_cells_evaluation, socket.assigns.input.id})
{:noreply, socket}
:error ->
{:noreply, socket}
end
end
defp handle_change(socket, value) do
prev_value = socket.assigns.value
socket = assign(socket, value: value)
if value != prev_value do
report_change(socket)
end
socket
end
defp report_change(%{assigns: assigns} = socket) do
send(self(), {:set_input_values, [{assigns.input.id, assigns.value}], assigns.local})
unless assigns.local do
report_event(socket, assigns.value)
end
end
defp parse(html_value, %{type: :text}) do
{:ok, html_value}
end
defp parse(html_value, %{type: :textarea}) do
# The browser may normalize newlines to \r\n, but we prefer just \n
value = String.replace(html_value, "\r\n", "\n")
{:ok, value}
end
defp parse(html_value, %{type: :password}) do
{:ok, html_value}
end
defp parse(html_value, %{type: :number}) do
if html_value == "" do
{:ok, nil}
else
case Integer.parse(html_value) do
{number, ""} ->
{:ok, number}
_ ->
{number, ""} = Float.parse(html_value)
{:ok, number}
end
end
end
defp parse(html_value, %{type: :url}) do
cond do
html_value == "" -> {:ok, nil}
Livebook.Utils.valid_url?(html_value) -> {:ok, html_value}
true -> :error
end
end
defp parse(html_value, %{type: :select, options: options}) do
selected_idx = String.to_integer(html_value)
options
|> Enum.with_index()
|> Enum.find_value(fn {{key, _label}, idx} ->
idx == selected_idx && {:ok, key}
end)
end
defp parse(html_value, %{type: :checkbox}) do
{:ok, html_value == "true"}
end
defp parse(html_value, %{type: :range}) do
{number, ""} = Float.parse(html_value)
{:ok, number}
end
defp parse(html_value, %{type: :color}) do
{:ok, html_value}
end
defp parse(html_value, %{type: :utc_datetime} = attrs) do
if html_value do
with {:ok, datetime} <- NaiveDateTime.from_iso8601(html_value),
datetime <- truncate_datetime(datetime),
true <- in_range?(datetime, attrs.min, attrs.max) do
{:ok, datetime}
else
_ -> :error
end
else
{:ok, nil}
end
end
defp parse(html_value, %{type: :utc_time} = attrs) do
if html_value do
with {:ok, time} <- Time.from_iso8601(html_value),
time <- truncate_time(time),
true <- in_range?(time, attrs.min, attrs.max) do
{:ok, time}
else
_ -> :error
end
else
{:ok, nil}
end
end
defp parse(html_value, %{type: :date} = attrs) do
if html_value == "" do
{:ok, nil}
else
with {:ok, date} <- Date.from_iso8601(html_value),
true <- in_range?(date, attrs.min, attrs.max) do
{:ok, date}
else
_ -> :error
end
end
end
defp truncate_datetime(datetime) do
datetime
|> NaiveDateTime.truncate(:second)
|> Map.replace!(:second, 0)
end
defp truncate_time(time) do
time
|> Time.truncate(:second)
|> Map.replace!(:second, 0)
end
defp in_range?(%struct{} = datetime, min, max)
when struct in [NaiveDateTime, Time, Date] do
(min == nil or struct.compare(datetime, min) != :lt) and
(max == nil or struct.compare(datetime, max) != :gt)
end
defp report_event(socket, value) do
topic = socket.assigns.input.ref
event = %{value: value, origin: socket.assigns.client_id, type: :change}
send(socket.assigns.input.destination, {:event, topic, event})
end
end