Skip to content

Commit

Permalink
live_select
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanminutillo committed Feb 3, 2025
1 parent 63d44fa commit 59773c9
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 25 deletions.
1 change: 1 addition & 0 deletions assets/static/images/icons/icons.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion deps.hex
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ makeup_sql = "~> 0.1.0"
makeup_graphql = "~> 0.1.2"
makeup_erlang = "~> 1.0.1"
solid = "~> 0.13"
live_select = "~> 1.5.2"
live_select = "~> 1.5.4"
#plug_static_index_html = "~> 1.0"
chameleon= "~> 2.5"
phoenix_live_favicon= "~> 0.2.0"
Expand Down
48 changes: 36 additions & 12 deletions lib/components/multiselect/live_select_integration_live.ex
Original file line number Diff line number Diff line change
@@ -1,29 +1,38 @@
defmodule Bonfire.UI.Common.LiveSelectIntegrationLive do
use Bonfire.UI.Common.Web, :function_component

def live_select(assigns) do
# field={@form[@form_input_name]}
@doc """
A LiveSelect integration component for Bonfire that handles form integration and events.
# TODO: fix warning: instead of passing separate form and field attributes, pass a single field attribute of type Phoenix.HTML.FormField
## Examples
<LiveSelectIntegrationLive.live_select
form={@form}
field={:my_field}
mode={:tags}
event_target={@myself}
options={@options}
value={@selected_values}
/>
"""
def live_select(assigns) do
~H"""
<LiveSelect.live_select
field={@form[@field]}
mode={@mode}
phx-target={@event_target}
options={@options}
value={@value}
allow_clear={true}
allow_clear={Map.get(assigns, :allow_clear, true)}
update_min_len={@update_min_len || 1}
debounce={0}
debounce={Map.get(assigns, :debounce, 0)}
placeholder={@placeholder}
disabled={@disabled}
style={:daisyui}
text_input_extra_class={@text_input_class}
container_extra_class="w-full flex flex-col"
option_extra_class="{@option_extra_class}"
tag_class="badge badge-neutral badge-lg gap-2"
dropdown_extra_class="z-[99999999999999999999999999999999] max-h-60 flex-nowrap border border-base-content/10 !bg-base-100 overflow-y-auto "
tags_container_class="flex flex-wrap gap-1 order-last"
dropdown_extra_class="z-50 max-h-60 flex-nowrap border border-base-content/10 !bg-base-100 overflow-y-auto top-12"
tags_container_class="flex flex-wrap gap-1 order-last mt-1"
value_mapper={&value_mapper/1}
>
<:option :let={option}>
<div class="flex p-0 gap-2 items-center">
Expand All @@ -42,7 +51,7 @@ defmodule Bonfire.UI.Common.LiveSelectIntegrationLive do
{option.label}
</p>
<% else %>
<%= if Map.has_key?(debug(option).value, :icon) or Map.has_key?(option.value, "icon") do %>
<%= if Map.has_key?(option.value, :icon) or Map.has_key?(option.value, "icon") do %>
<div class="w-8 h-8">
<img src={e(option.value, :icon, nil)} alt="" class="w-full h-full rounded-full" />
</div>
Expand All @@ -67,14 +76,14 @@ defmodule Bonfire.UI.Common.LiveSelectIntegrationLive do
{option.label}
</p>
<% else %>
<%= if Map.has_key?(debug(option.value), :icon) or Map.has_key?(option.value, "icon") do %>
<%= if Map.has_key?(option.value, :icon) or Map.has_key?(option.value, "icon") do %>
<div class="w-6 h-6">
<img src={e(option.value, :icon, nil)} alt="" class="w-full h-full rounded-full" />
</div>
<% end %>
<div class="text-sm">
<p class="font-semibold">
{e(debug(option, "tag oppppt"), :value, :name, nil) ||
{e(option.value, :name, nil) ||
e(option.value, :profile, :name, nil) || e(option.value, :username, nil) ||
e(option.value, :named, :name, nil)}
</p>
Expand All @@ -85,4 +94,19 @@ defmodule Bonfire.UI.Common.LiveSelectIntegrationLive do
</LiveSelect.live_select>
"""
end

defp value_mapper(%{id: id, name: name} = value) do
%{label: name, value: value}
end

defp value_mapper(%{id: id} = value) do
name =
e(value, :name, nil) || e(value, :profile, :name, nil) ||
e(value, :username, nil) || e(value, :named, :name, nil)

%{label: name, value: value}
end

defp value_mapper(value) when is_binary(value), do: %{label: value, value: value}
defp value_mapper(value), do: value
end
36 changes: 32 additions & 4 deletions lib/components/multiselect/multiselect_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,29 @@ defmodule Bonfire.UI.Common.MultiselectLive do
use Bonfire.UI.Common.Web, :stateless_component
use Bonfire.Common.Utils

@doc """
A multiselect component that can use either LiveSelect or BasicMultiselect implementations.
## Props
* `form` - The form to attach the multiselect to
* `form_input_name` - The name of the form input field
* `field` - The field name in the form
* `label` - Label text for the input
* `preloaded_options` - List of initial options
* `selected_options` - List of initially selected options
* `show_search` - Whether to show search functionality
* `focus_event` - Event to trigger on focus
* `pick_event` - Event to trigger when an option is picked
* `remove_event` - Event to trigger when an option is removed
* `event_target` - Target for events (usually @myself)
* `context_id` - Context ID for the component
* `is_editable` - Whether the component is editable
* `implementation` - :live_select or :basic
* `mode` - :single or :tags mode
* `type` - Type of the multiselect
* `max_selectable` - Maximum number of selectable items (0 for unlimited)
* `update_min_len` - Minimum length of text before triggering updates
"""
prop form, :any, default: :multi_select
prop form_input_name, :any, required: true
prop field, :any, default: nil
Expand All @@ -10,7 +33,6 @@ defmodule Bonfire.UI.Common.MultiselectLive do
prop selected_options, :any, default: nil
prop show_search, :boolean, default: false
prop focus_event, :string, required: false
# prop autocomplete_event, :string, required: false
prop pick_event, :string, required: false
prop remove_event, :string, default: nil
prop event_target, :any, default: nil
Expand All @@ -20,6 +42,9 @@ defmodule Bonfire.UI.Common.MultiselectLive do
prop mode, :atom, default: :single
prop type, :atom, default: nil
prop class, :string, default: "bg-transparent text-sm rounded h-10 w-full input liveselect"
# 0 means unlimited
prop max_selectable, :integer, default: 0
prop update_min_len, :integer, default: 1

prop text_input_class, :string,
default: "bg-transparent text-sm rounded h-10 w-full input liveselect"
Expand All @@ -35,10 +60,16 @@ defmodule Bonfire.UI.Common.MultiselectLive do
|> render_sface()
end

@doc """
Prepares preloaded options for the multiselect.
"""
def preloaded_options(preloaded_options) do
Enum.map(preloaded_options || [], &prepare_entry/1)
end

@doc """
Prepares selected options for the multiselect.
"""
def selected_options(selected_options, field_name, context, preloaded_options) do
do_selected_options(e(context, field_name, nil) || selected_options || [], preloaded_options)
end
Expand All @@ -58,8 +89,6 @@ defmodule Bonfire.UI.Common.MultiselectLive do
end

defp prepare_entry(%{} = object, _preloaded_options) do
debug(object)

{id(object),
e(object, :name, nil) || e(object, :username, nil) || e(object, :profile, :name, nil) ||
e(object, :character, :username, nil) ||
Expand All @@ -69,7 +98,6 @@ defmodule Bonfire.UI.Common.MultiselectLive do
defp prepare_entry(entry, preloaded_options)
when is_binary(entry) and is_list(preloaded_options) and preloaded_options != [] do
preloaded_options(preloaded_options)
# |> debug(entry)
|> Enum.filter(fn
{_name, id} when id == entry -> true
_ -> false
Expand Down
19 changes: 11 additions & 8 deletions lib/components/multiselect/multiselect_live.sface
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
{#if @implementation == :live_select}
<Bonfire.UI.Common.LiveSelectIntegrationLive.live_select
form={@form}
class={@class}
field={@form_input_name}
mode={@mode}
event_target={@event_target}
Expand All @@ -14,14 +13,18 @@
text_input_class={@text_input_class}
disabled={!@is_editable}
type={@type}
update_min_len={1}
update_min_len={@update_min_len}
max_selectable={@max_selectable}
allow_clear
/>
<div
class="absolute top-[8px] right-[8px] z-30 flex items-center w-[26px] h-[26px] min-h-[26px]"
phx-click={JS.dispatch("change", to: "#smart_input_form")}
>
<#Icon solid="Search" class="w-4 h-4 text-base-content/70" />
</div>
{#if @show_search}
<div
class="absolute top-[8px] right-[8px] z-30 flex items-center w-[26px] h-[26px] min-h-[26px]"
phx-click={JS.dispatch("change", to: "#smart_input_form")}
>
<#Icon solid="Search" class="w-4 h-4 text-base-content/70" />
</div>
{/if}
{#else}
<Bonfire.UI.Common.BasicMultiselectLive
id={random_dom_id()}
Expand Down
4 changes: 4 additions & 0 deletions lib/components/smart_input/select_recipients_live.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ defmodule Bonfire.UI.Common.SelectRecipientsLive do
:search,
[search]
)
|> Enum.map(fn
%Needle.Pointer{activity: %{object: user}} -> user
other -> other
end)
|> results_for_multiselect()
|> maybe_send_update(LiveSelect.Component, live_select_id, options: ...)

Expand Down

0 comments on commit 59773c9

Please sign in to comment.