Skip to content

Commit

Permalink
add :append option to add values to the selection
Browse files Browse the repository at this point in the history
  • Loading branch information
shamanime committed May 1, 2024
1 parent 351e498 commit 09932c3
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 9 deletions.
13 changes: 13 additions & 0 deletions lib/live_select.ex
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,22 @@ defmodule LiveSelect do
`new_selection` must be a single element in `:single` mode, a list in `:tags` mode. If it's `nil`, the selection will be cleared.
After updating the selection, `LiveSelect` will trigger a change event in the form.
`new_selection` must be a single element in `:single` mode, a list in `:tags` mode. If it's `nil`, the selection will be cleared.
After updating the selection, `LiveSelect` will trigger a change event in the form.
To set a custom id for the component to use with `Phoenix.LiveView.send_update/3`, you can pass the `id` assign to `live_select/1`.
### Appending to the selection in `:tags` mode
When you want to append to the current selection, you can use the `:append` key in the `send_update` call:
```
send_update(LiveSelect.Component, id: live_select_id, append: values_to_append)
```
`values_to_append` can be a single element or a list to append to the current selection. If the value to append already exists in the selection, it will be ignored.
Note: This does not work in `:single` mode. Use `:value` instead to replace the selection.
## Examples
Expand Down
47 changes: 38 additions & 9 deletions lib/live_select/component.ex
Original file line number Diff line number Diff line change
Expand Up @@ -164,15 +164,25 @@ defmodule LiveSelect.Component do
socket
end


socket =
if Map.has_key?(assigns, :value) do
update(socket, :selection, fn
selection, %{options: options, value: value, mode: mode, value_mapper: value_mapper} ->
update_selection(value, selection, options, mode, value_mapper)
end)
|> client_select(%{input_event: true})
else
socket
cond do
Map.has_key?(assigns, :value) ->
update(socket, :selection, fn
selection, %{options: options, value: value, mode: mode, value_mapper: value_mapper} ->
update_selection(value, selection, options, mode, value_mapper)
end)
|> client_select(%{input_event: true})

Map.has_key?(assigns, :append) ->
update(socket, :selection, fn
selection, %{append: value, mode: mode, value_mapper: value_mapper} ->
append_selection(value, selection, mode, value_mapper)
end)
|> client_select(%{input_event: true})

true ->
socket
end

{:ok, socket}
Expand Down Expand Up @@ -365,6 +375,7 @@ defmodule LiveSelect.Component do
:clear_button,
:hide_dropdown,
:value_mapper,
:append,
# for backwards compatibility
:form
]
Expand All @@ -376,7 +387,7 @@ defmodule LiveSelect.Component do
|> Enum.sort_by(&String.jaro_distance(to_string(&1), to_string(assign)))
|> List.last()

raise ~s(Invalid assign: "#{assign}". Did you mean "#{most_similar}" ?)
raise ~s(Invalid assign: "#{assign}". Did you mean "#{most_similar}"?)
end
end
end
Expand Down Expand Up @@ -527,6 +538,24 @@ defmodule LiveSelect.Component do
|> Enum.reject(&is_nil/1)
end

defp append_selection(_value, _current_selection, :single, _value_mapper) do
raise """
Appending values only works in `:tags` mode
"""
end

defp append_selection(nil, current_selection, _mode, _value_mapper), do: current_selection

defp append_selection(value, current_selection, :tags, value_mapper) do
value = if Enumerable.impl_for(value), do: value, else: [value]

normalized_value = Enum.map(value, &normalize_selection_value(&1, current_selection, value_mapper))

current_selection ++ normalized_value
|> Enum.reject(&is_nil/1)
|> Enum.uniq()
end

defp normalize_selection_value(%Ecto.Changeset{action: :replace}, _options, _value_mapper),
do: nil

Expand Down
52 changes: 52 additions & 0 deletions test/live_select_tags_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,58 @@ defmodule LiveSelectTagsTest do
assert_selected_multiple(live, [%{label: "C", value: 3}, %{label: "E", value: 5}])
end

test "can append values to the selection", %{conn: conn} do
{:ok, live, _html} = live(conn, "/?mode=tags")

stub_options(~w(A B C))

type(live, "ABC")

select_nth_option(live, 1)

assert_selected_multiple(live, ~w(A))

send_update(live, append: ["B"])

assert_selected_multiple(live, ~w(A B))

send_update(live, append: "C")

assert_selected_multiple(live, ~w(A B C))
end

test "does not duplicate selection when appending values", %{conn: conn} do
{:ok, live, _html} = live(conn, "/?mode=tags")

stub_options(~w(A B C))

type(live, "ABC")

select_nth_option(live, 1)

assert_selected_multiple(live, ~w(A))

send_update(live, append: ~w(A))

assert_selected_multiple(live, ~w(A))
end

test "does not change the selection when appending nil values", %{conn: conn} do
{:ok, live, _html} = live(conn, "/?mode=tags")

stub_options(~w(A B C))

type(live, "ABC")

select_nth_option(live, 1)

assert_selected_multiple(live, ~w(A))

send_update(live, append: nil)

assert_selected_multiple(live, ~w(A))
end

test "can render custom clear button", %{conn: conn} do
{:ok, live, _html} = live(conn, "/live_component_test")

Expand Down

0 comments on commit 09932c3

Please sign in to comment.