diff --git a/assets/css/components.css b/assets/css/components.css
index 4731110de..93b3e2429 100644
--- a/assets/css/components.css
+++ b/assets/css/components.css
@@ -100,6 +100,10 @@
@apply appearance-none border-transparent bg-blue-600 hover:bg-blue-700 cursor-pointer rounded-xl;
}
+ .input-select {
+ @apply w-60;
+ }
+
.input-label {
@apply mb-0.5 text-sm text-gray-800 font-medium;
}
diff --git a/lib/livebook/notebook/cell/input.ex b/lib/livebook/notebook/cell/input.ex
index d4631ecca..2e74017be 100644
--- a/lib/livebook/notebook/cell/input.ex
+++ b/lib/livebook/notebook/cell/input.ex
@@ -21,7 +21,7 @@ defmodule Livebook.Notebook.Cell.Input do
props: props()
}
- @type type :: :text | :url | :number | :password | :textarea | :color | :range
+ @type type :: :text | :url | :number | :password | :textarea | :color | :range | :select
@typedoc """
Additional properties adjusting the given input type.
@@ -97,6 +97,7 @@ defmodule Livebook.Notebook.Cell.Input do
def default_props(type)
def default_props(:range), do: %{min: 0, max: 100, step: 1}
+ def default_props(:select), do: %{options: [""]}
def default_props(_type), do: %{}
@doc """
diff --git a/lib/livebook_web/live/session_live/cell_component.ex b/lib/livebook_web/live/session_live/cell_component.ex
index e6be07bae..f2fe7d43c 100644
--- a/lib/livebook_web/live/session_live/cell_component.ex
+++ b/lib/livebook_web/live/session_live/cell_component.ex
@@ -159,6 +159,26 @@ defmodule LivebookWeb.SessionLive.CellComponent do
"""
end
+ defp cell_input(%{cell_view: %{input_type: :select}} = assigns) do
+ ~H"""
+
+
+
+ """
+ end
+
defp cell_input(assigns) do
~H"""
Name
- <.extra_fields type={@attrs.type} props={@attrs.props} />
+ <.extra_fields type={@attrs.type} props={@attrs.props} myself={@myself} />
<.switch_checkbox
name="attrs[reactive]"
label="Reactive (reevaluates dependent cells on change)"
@@ -78,6 +78,46 @@ defmodule LivebookWeb.SessionLive.InputCellSettingsComponent do
"""
end
+ defp extra_fields(%{type: :select} = assigns) do
+ ~H"""
+
+
Options
+
+ <%= for {option, idx} <- Enum.with_index(@props.options) do %>
+
+
+
+
+ <% end %>
+
+
+
+
+
+ """
+ end
+
defp extra_fields(assigns), do: ~H""
@impl true
@@ -100,6 +140,24 @@ defmodule LivebookWeb.SessionLive.InputCellSettingsComponent do
{:noreply, push_patch(socket, to: socket.assigns.return_to)}
end
+ def handle_event("select_options_action", params, socket) do
+ {action, params} = Map.pop!(params, "action")
+ attrs = socket.assigns.attrs
+ options = select_options_action(action, params, attrs.props.options)
+ attrs = put_in(attrs.props.options, options)
+ valid? = valid_options?(options)
+ {:noreply, socket |> assign(attrs: attrs) |> assign(valid: valid?)}
+ end
+
+ defp select_options_action("add", _params, options) do
+ options ++ [""]
+ end
+
+ defp select_options_action("delete", %{"index" => index}, options) do
+ index = String.to_integer(index)
+ List.delete_at(options, index)
+ end
+
defp validate_attrs(data, prev_attrs) do
name = data["name"]
type = data["type"] |> String.to_existing_atom()
@@ -126,10 +184,20 @@ defmodule LivebookWeb.SessionLive.InputCellSettingsComponent do
{valid?, data}
end
+ defp validate_props(data, :select) do
+ options = data["options"] || []
+ valid? = valid_options?(options)
+ {valid?, %{options: options}}
+ end
+
defp validate_props(_data, _type) do
{true, %{}}
end
+ defp valid_options?(options) do
+ options != [] and options == Enum.uniq(options)
+ end
+
defp parse_number(string) do
case Float.parse(string) do
{number, _} ->
@@ -143,6 +211,7 @@ defmodule LivebookWeb.SessionLive.InputCellSettingsComponent do
defp default_value(:color, _props), do: "#3E64FF"
defp default_value(:range, %{min: min}), do: to_string(min)
+ defp default_value(:select, %{options: [option | _]}), do: option
defp default_value(_type, _props), do: ""
defp input_types do
@@ -153,7 +222,8 @@ defmodule LivebookWeb.SessionLive.InputCellSettingsComponent do
text: "Text",
textarea: "Textarea",
url: "URL",
- range: "Range"
+ range: "Range",
+ select: "Select"
]
end
end