PhoenixKitCatalogue. Web. Components. CatalogueBrowse
(PhoenixKitCatalogue v0.24.0)
Copy Markdown
View Source
Embeddable catalogue browse surface: search, category chips, and the
item grid or admin-look table as one drop-in LiveComponent — the same
BrowseState + Browse.* stack ItemSelectorModal runs on, minus the
selection chrome. Put a catalogue (or a scoped slice of one) on any
logged-in page:
<.live_component
module={PhoenixKitCatalogue.Web.Components.CatalogueBrowse}
id="showroom"
scope={%{catalogue_uuids: [@catalogue.uuid], statuses: ["active"]}}
/>Views and columns (2026-08-30)
Two presentations over the same fetch, exactly like the modal:
view: "card" (the default here — existing embeds keep their grid) and
view: "table", the admin-look list. A toggle beside the search box
switches them; the attr only sets the starting view.
columns is the same host contract the modal enforces
(Browse.table_columns/0, unknown entries raise) and it is a GRANT:
the cards read it too, so a column the host didn't grant can't
reappear by flipping the view. Omitted, the default set applies minus
what show_sku/show_prices opt out of, and minus :qty — selection
chrome this surface doesn't have (a host that explicitly asks for
:qty gets an empty cell).
The TABLE additionally starts without the modal's default-hidden pair
(:sku, :breadcrumb): without the modal's Columns dropdown they'd
render outright, and :breadcrumb beside the :category column shows
the category twice per row. That is visibility, not grant — the cards
keep their SKU line, which is what show_sku has always controlled.
An explicit columns list is taken verbatim on both surfaces.
Messages to the host
One generic message, so a host writes a single clause and switches on the event:
handle_info({:catalogue_browse, %{id: id, event: :item_clicked, item: item}}, socket)item is a presented map (Browse.present_items/2): uuid, translated
name, sku, price, unit, photo_url. Cards/rows are clickable only when
the host opts in with on_item_click: true — a purely decorative
embedding never receives (or needs to handle) anything.
Scope
Identical semantics to ItemSelectorModal: fixed at mount, every fetch
re-derived from it, category narrowing only within it — including the
subtree expansion a parent-category scope needs (Browse.expand_scope/1,
shared with the modal since 2026-08-30; this surface previously hid
descendant chips and rejected narrowing to them). See
PhoenixKitCatalogue.Catalogue.BrowseState.