defmodule Moon.Design.Search do
@moduledoc "Textinput with search options. Renders as a Moon.Design.Dropdown. Is designed for usage outside of the forms"
use Moon.StatelessComponent
alias Moon.Design.Dropdown
alias __MODULE__
import Moon.Helpers.Form, only: [input_classes_light: 1]
@doc "... format: [page: Module.Name, key: \"Name\"]"
prop(options, :list, required: true)
@doc "Set disabled/non-disabled"
prop(disabled, :boolean)
@doc "Some prompt to be shown on empty value"
prop(prompt, :string)
@doc "Id of the component"
prop(id, :string, required: true)
@doc "Common moon size property"
prop(size, :string, values!: ~w(sm md lg), default: "md")
@doc "Additional classes for the <select> tag"
prop(class, :css_class, from_context: :class)
@doc "Data-testid attribute value"
prop(testid, :string)
@doc "Some additional styling will be set to indicate field is invalid"
prop(error, :boolean, from_context: :error)
@doc "Should dropdown be open"
prop(is_open, :boolean)
@doc "Filtering value for the options, appears in input"
prop(filter, :string)
@doc "On key up event for the input - use it for filter options"
prop(on_keyup, :event)
@doc "Event that fires when smth is chosen from the dropdown menu"
prop(on_change, :event)
@doc "Additional attributes for the option link"
prop(attrs, :map, default: %{})
@doc "Label to use in the beginning in case of no results"
prop(no_results_label, :string, default: "Search for")
@doc "Option for custom stylings - use it to show icons or anything else"
slot(default)
@doc "Trigger element for the dropdown, default is Dropdown.Select"
slot(trigger)
@doc "Slot used for rendering single option. option[:key] will be used if not given"
slot(option)
@doc "When no results are found - this slot will be rendered"
slot(no_result)
def render(assigns) do
~F"""
<div {=@id} class={merge(["w-full", @class])} data-testid={@testid} aria-label="Search">
<Dropdown id={"#{@id}-dropdown"} {=@is_open} hook="Combobox">
<:trigger :let={is_open: is_open}>
<#slot {@trigger, is_open: is_open}>
<Dropdown.Input
placeholder={@prompt}
{=@size}
{=is_open}
{=@error}
{=@disabled}
{=@on_keyup}
value={@filter}
class={
"ps-[2.5rem] bg-goku hover:border-trunks",
input_classes_light(assigns),
"rounded-bl-none rounded-br-none": is_open
}
>
<Search.Icon />
<Search.Button on_click={@on_keyup} />
</Dropdown.Input>
</#slot>
</:trigger>
<#slot {@default}>
<Dropdown.Options
option_module={Dropdown.Link}
{=@on_change}
class="p-2 mt-0 rounded-tl-none rounded-tr-none"
>
<#slot {@option, option: option} :for={option <- @options}>
<Dropdown.Link {=@size} disabled={option[:disabled]} href={option[:page]} {=@attrs}>
{option[:key]}
</Dropdown.Link>
</#slot>
{!-- Use @options == [] to avoid any crash due type input using Enum.empty? --}
<#slot {@no_result, search: @filter, label: @no_results_label, size: @size} :if={@options == []}>
<Search.NoResult label={@no_results_label} search={@filter} {=@size} />
</#slot>
</Dropdown.Options>
</#slot>
</Dropdown>
</div>
"""
end
end