Skip to content

Commit

Permalink
fix: flickering on multiselect combobox [MDS-1352] (#854)
Browse files Browse the repository at this point in the history
Co-authored-by: Dmytro Kireiev <[email protected]>
  • Loading branch information
ffcabbar and dkireev authored Nov 12, 2024
1 parent adea3f7 commit f540440
Show file tree
Hide file tree
Showing 7 changed files with 89 additions and 27 deletions.
5 changes: 3 additions & 2 deletions assets/js/hooks/combobox.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
export default {
mounted() {
this.input = this.el.querySelector('.moon-comboinput');
this.dropdown = this.el.children[1];
this.el.addEventListener("click", (event) => {
event.stopPropagation();
});
},

updated() {
Expand Down
5 changes: 5 additions & 0 deletions lib/moon/design/dropdown/Input.ex
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ defmodule Moon.Design.Dropdown.Input do
@doc "Keyword | Map of additional attributes for the input"
prop(attrs, :any, default: %{})

prop(on_focus, :event)
prop(on_blur, :event)

@doc "Some buttons and icons over the input"
slot(default)

Expand All @@ -46,6 +49,8 @@ defmodule Moon.Design.Dropdown.Input do
data-testid={@testid}
:on-keyup={@on_keyup}
:on-focus={@open_me}
{=@on_focus}
{=@on_blur}
{=@error}
{=@disabled}
autocomplete="off"
Expand Down
3 changes: 3 additions & 0 deletions lib/moon/design/dropdown/option.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ defmodule Moon.Design.Dropdown.Option do
@doc "If the button is disabled"
prop(disabled, :boolean)

@doc "Attribute phx-hook. Used for dependant components"
prop(hook, :string)
@doc "On click event, in most cases got from context"
prop(on_click, :event, from_context: :on_click)
@doc "Value of the option, in most cases got from context"
Expand Down Expand Up @@ -51,6 +53,7 @@ defmodule Moon.Design.Dropdown.Option do
@class
])}
data-testid={@testid}
phx-hook={@hook}
>
<#slot>{@title}</#slot>
</button>
Expand Down
44 changes: 27 additions & 17 deletions lib/moon/design/form/combobox.ex
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ defmodule Moon.Design.Form.Combobox do
@doc "Filtering value for the options"
prop(filter, :string)

prop(on_focus, :event)
prop(on_blur, :event)

@doc "Slot used for rendering single option. option[:key] will be used if not given"
slot(option)

Expand All @@ -73,7 +76,7 @@ defmodule Moon.Design.Form.Combobox do

def render(assigns) do
~F"""
<Dropdown id={dropdown_id(assigns)} {=@is_open} hook="Combobox" {=@testid} {=@class}>
<Dropdown id={dropdown_id(assigns)} {=@is_open} {=@testid} {=@class}>
<:trigger :let={is_open: is_open, on_trigger: on_trigger}>
<#slot {@trigger, is_open: is_open, on_trigger: on_trigger} context_put={on_keyup: @on_keyup}>
<Dropdown.Input
Expand All @@ -84,6 +87,8 @@ defmodule Moon.Design.Form.Combobox do
{=@error}
{=@disabled}
{=@on_keyup}
{=@on_focus}
{=@on_blur}
value={(select_value(assigns) && select_value(assigns)[:key]) || @filter}
class={
input_classes(assigns) ++
Expand Down Expand Up @@ -118,26 +123,31 @@ defmodule Moon.Design.Form.Combobox do
<#slot {@default}>
<Dropdown.Options>
<Dropdown.Option
id={gen_rand_id()}
:for={option <- @options}
{=@size}
class={(is_hidden(option, @filter) && "hidden") || ""}
hook="Combobox"
>
<Checkbox
checked_value={option[:value]}
:if={@is_multiple}
disabled={option[:disabled]}
{=@size}
hidden_input={false}
is_multiple
>
<#slot {@option, option: option}>{option[:key]}</#slot>
</Checkbox>
<Radio.Button value={option[:value]} :if={!@is_multiple} disabled={option[:disabled]} {=@size}>
<#slot {@option, option: option}>
<Radio.Indicator class="hidden" />
{option[:key]}
</#slot>
</Radio.Button>
<div class="flex w-full h-full">
<Checkbox
checked_value={option[:value]}
:if={@is_multiple}
disabled={option[:disabled]}
{=@size}
hidden_input={false}
is_multiple
checkbox_label_class="w-full flex pb-0 h-full items-start"
>
<div class="w-full text-start"><#slot {@option, option: option}>{option[:key]}</#slot></div>
</Checkbox>
<Radio.Button value={option[:value]} :if={!@is_multiple} disabled={option[:disabled]} {=@size}>
<#slot {@option, option: option}>
<Radio.Indicator class="hidden" />
{option[:key]}
</#slot>
</Radio.Button>
</div>
</Dropdown.Option>
</Dropdown.Options>
</#slot>
Expand Down
6 changes: 6 additions & 0 deletions lib/moon/helpers/form.ex
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ defmodule Moon.Helpers.Form do
def dropdown_id(%{id: id}) when not is_nil(id), do: "#{id}-dropdown"
def dropdown_id(%{form: form, field: field}), do: "#{form[field].id}-dropdown"

def gen_rand_id() do
:crypto.strong_rand_bytes(5)
|> Base.encode32(case: :lower)
|> String.replace(~r/=/, "")
end

def select_value(%{is_multiple: true}), do: nil

def select_value(%{form: form, field: field, options: options}) do
Expand Down
28 changes: 24 additions & 4 deletions lib/moon_web/examples/design/form/combobox_example/default.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ defmodule MoonWeb.Examples.Design.Form.ComboboxExample.Default do
use MoonWeb, :example

alias Moon.Design.Form

alias MoonWeb.Schema.User

prop(titles, :list,
Expand All @@ -24,9 +23,21 @@ defmodule MoonWeb.Examples.Design.Form.ComboboxExample.Default do
prop(changeset, :any, default: User.changeset(%User{name: nil}))

prop(filter, :string, default: "")
prop(is_open, :boolean, default: false)

def handle_event("change_filter", %{"value" => filter}, socket) do
{:noreply, assign(socket, filter: filter)}
{:noreply,
socket
|> assign(filter: filter)
|> assign(is_open: true)}
end

def handle_event("focus", _params, socket) do
{:noreply, assign(socket, is_open: true)}
end

def handle_event("blur", _params, socket) do
{:noreply, assign(socket, is_open: false)}
end

def handle_event("changed", params, socket) do
Expand All @@ -35,7 +46,8 @@ defmodule MoonWeb.Examples.Design.Form.ComboboxExample.Default do
{:noreply,
assign(socket,
changeset: User.changeset(%User{}, user),
filter: Map.get(user, "name", "")
filter: Map.get(user, "name", ""),
is_open: false
)}
end

Expand All @@ -52,7 +64,15 @@ defmodule MoonWeb.Examples.Design.Form.ComboboxExample.Default do
submit="changed"
>
<Form.Field field={:name} label={"Size #{size}"} hint="Some hint here" {=size}>
<Form.Combobox {=size} filter={@filter} options={@titles} on_keyup="change_filter" />
<Form.Combobox
{=size}
filter={@filter}
options={@titles}
on_keyup="change_filter"
on_focus="focus"
on_blur="blur"
is_open={@is_open}
/>
</Form.Field>
</Form>
</div>
Expand Down
25 changes: 21 additions & 4 deletions lib/moon_web/examples/design/form/combobox_example/multiple.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,45 @@ defmodule MoonWeb.Examples.Design.Form.ComboboxExample.Multiple do

prop(changeset_sm, :any, default: User.changeset(%User{gender: nil}))
prop(target_sm, :list, default: [])
prop(is_open_sm, :boolean, default: false)

prop(changeset_md, :any, default: User.changeset(%User{gender: nil}))
prop(target_md, :list, default: [])
prop(is_open_md, :boolean, default: false)

prop(changeset_lg, :any, default: User.changeset(%User{gender: nil}))
prop(target_lg, :list, default: [])
prop(is_open_lg, :boolean, default: false)

prop(filter_sm, :string, default: "")
prop(filter_md, :string, default: "")
prop(filter_lg, :string, default: "")

def handle_event("change_filter_" <> num, %{"value" => filter}, socket) do
{:noreply, assign(socket, "filter_#{num}": filter)}
{:noreply,
socket
|> assign("filter_#{num}": filter)
|> assign("is_open_#{num}": true)}
end

def handle_event("focus_" <> num, _params, socket) do
{:noreply, assign(socket, "is_open_#{num}": true)}
end

def handle_event("blur_" <> num, _params, socket) do
{:noreply, assign(socket, "is_open_#{num}": false)}
end

def handle_event("change_" <> num, params, socket) do
changeset =
User.changeset(%User{}, Map.get(params, "user", socket.assigns["changeset_#{num}"]))
User.changeset(%User{}, Map.get(params, "user", %{}))

{:noreply,
socket
|> assign(
"changeset_#{num}": changeset,
"target_#{num}": Map.get(params, "_target", [])
"target_#{num}": Map.get(params, "_target", []),
"is_open_#{num}": true
)}
end

Expand All @@ -55,10 +70,12 @@ defmodule MoonWeb.Examples.Design.Form.ComboboxExample.Multiple do
{=size}
is_multiple
on_keyup={"change_filter_#{size}"}
on_focus={"focus_#{size}"}
on_blur={"blur_#{size}"}
filter={assigns[:"filter_#{size}"]}
options={@permissions}
prompt="Select permissions"
is_open={assigns[:"target_#{size}"] === ["user", "permissions"]}
is_open={assigns[:"is_open_#{size}"]}
/>
</Form.Field>
</Form>
Expand Down

0 comments on commit f540440

Please sign in to comment.