# `PhoenixKitCatalogue.Web.Components.ItemPicker`
[🔗](https://github.com/BeamLabEU/phoenix_kit_catalogue/blob/0.1.14/lib/phoenix_kit_catalogue/web/components/item_picker.ex#L1)

Combobox LiveComponent for picking a single item from the catalogue
via server-side search.

Drop one into any LiveView — typically many, one per row in a picker
table. Each instance owns its own search state; the parent LV only
reacts to two messages:

    {:item_picker_select, id, %Item{}}  # user chose an item
    {:item_picker_clear,  id}           # user cleared the selection

### API

    <.item_picker
      id="row-42-picker"
      category_uuids={[category.uuid]}
      selected_item={@chosen_item}
      excluded_uuids={@already_used_uuids}
      locale="en"
    />

Attrs:

  * `:id` (required) — unique DOM/component id. The `:item_picker_*`
    messages echo this back so a parent with N pickers knows which
    fired.
  * `:category_uuids` — scope search to these categories. `nil` or
    `[]` means "all categories + uncategorized" (matches
    `Catalogue.search_items/2`).
  * `:catalogue_uuids` — scope search to these catalogues. Composes
    with `:category_uuids` (AND).
  * `:include_descendants` — when `true` (default), `:category_uuids`
    is expanded through the V103 tree; pass `false` for literal
    set semantics.
  * `:only` — `:uncategorized_only` restricts results to items without
    a category; `:categorized_only` restricts to items in some
    category; `nil` (default) is unrestricted. Forwards to
    `Catalogue.search_items/2`'s `:only` opt.
  * `:selected_item` — the `%Item{}` currently chosen (or `nil`).
    Drives the input text and the `aria-selected` / primary-border
    styling in the dropdown.
  * `:excluded_uuids` — items in this list are rendered dim +
    `aria-disabled` and cannot be clicked. Use for "already picked in
    another row" state.
  * `:locale` (required) — locale string for translated display
    names (`"en"`, `"es"`, etc.). Resolved via
    `Catalogue.get_translation/2`.
  * `:placeholder` — input placeholder. Defaults to "Search items…".
  * `:empty_query_limit` — how many items to show when the query is
    empty (the "just focused" state). Defaults to `10`.
  * `:page_size` — max results fetched per query. Defaults to `20`.
    When the unbounded count exceeds this the dropdown shows a
    "Type to refine…" sentinel row so the user knows there's more.
  * `:disabled` — disables the input and hides the clear button.
  * `:format_price` — 1-arity function taking an `%Item{}` (with
    `:catalogue` preloaded — the search always does this) and
    returning a display string or `nil`. Defaults to a Decimal
    stringifier of `item_pricing(item).final_price`. Return `nil` to
    omit the price column entirely.

### Keyboard / a11y

Handled client-side by the colocated `ItemPicker` hook:

  * ArrowDown / ArrowUp cycle through enabled options (announced via
    `aria-activedescendant`; DOM focus stays on the input).
  * Home / End jump to first / last enabled option.
  * Enter activates the focused option (simulates a click so the
    normal `select` event fires).
  * Escape closes the dropdown and keeps focus on the input.
  * Clicking outside the picker closes it (`phx-click-away`).

The dropdown is absolutely positioned and elevated with `z-50`; the
parent container must allow overflow (`overflow: visible` or just
don't set `overflow: hidden` on an ancestor that clips it).

---

*Consult [api-reference.md](api-reference.md) for complete listing*
