PhoenixKitCatalogue.Web.Components (PhoenixKitCatalogue v0.19.1)

Copy Markdown View Source

Reusable UI components for the Catalogue module.

All components are designed to be opt-in — features are off by default and enabled via attributes. Import into any LiveView with:

import PhoenixKitCatalogue.Web.Components

Components

  • search_input/1 — search bar with debounce and clear button
  • search_results_summary/1 — "N results for …" / "X of Y" summary line
  • scope_selector/1 — disclosure with catalogue/category checkbox lists for narrowing a search (pairs with Catalogue.search_items/2 filters)
  • catalogue_rules_picker/1 — smart-catalogue rule editor (checkbox + value + unit per catalogue; pairs with Catalogue.put_catalogue_rules/3)
  • view_mode_toggle/1 — table/card view toggle synced via localStorage
  • item_table/1 — configurable item table with selectable columns
  • item_picker/1 — combobox for picking a single item via server-side search; backed by Components.ItemPicker LiveComponent, fires {:item_picker_select, id, item} / {:item_picker_clear, id} upward
  • featured_image_card/1 — the shared featured-image card used on catalogue / category / item forms (thumbnail or empty state + picker buttons). Expects open_featured_image_picker / clear_featured_image events wired up in the owning LV — see Attachments.
  • metadata_editor/1 — the shared metadata tab body for catalogue and item forms (opt-in fields from Metadata.definitions/1). Expects add_meta_field and remove_meta_field events wired up in the LV; text edits are absorbed via the form's validate.

Embeddable browse surfaces (separate modules)

  • Components.ItemSelectorModal — LiveComponent: the client-facing "pick items + quantities from the catalogue" modal. Scoped via search_items/2 opts, reports {:items_selected, %{picks: …}} / {:item_selector_closed, _} to the host LV.
  • Components.CatalogueBrowse — LiveComponent: the same browse surface (search + category chips + card grid) without selection chrome, for embedding a catalogue view on any logged-in page. Reports {:catalogue_browse, %{event: :item_clicked, …}}.
  • Components.Browse — the pure function components both are built from (item_card/1, item_grid/1, category_chips/1, qty_stepper/1, grid_skeleton/1, present_items/2) for hosts that want to compose their own surface with Catalogue.BrowseState.

Several of these (search_input, search_results_summary, view_mode_toggle) are deliberately generic — no catalogue-specific schema knowledge — and are candidates for promotion to phoenix_kit core once a coordinated release lands. Keeping them here for now avoids coupling catalogue's hex dep to unpublished core features.

Examples

<%!-- Minimal item table: just name and SKU --%>
<.item_table items={@items} columns={[:name, :sku]} />

<%!-- Full-featured table with search, pricing, and actions --%>
<.item_table
  items={@items}
  columns={[:name, :sku, :base_price, :price, :unit, :status, :category, :manufacturer]}
  markup_percentage={@catalogue.markup_percentage}
  edit_path={&Paths.item_edit/1}
  on_delete="delete_item"
/>

<%!-- Search bar --%>
<.search_input query={@search_query} placeholder={Gettext.gettext(PhoenixKitCatalogue.Gettext, "Search items...")} />

Summary

Functions

Whether any row in a list carries a featured image — gates the photo column in table views, so a table with no images at all doesn't spend a permanently empty column on them.

Like any_featured_thumb?/1, but also counts the paperclip indicator: the media column earns its place when some row has an image OR attached documents (per the counts map from Catalogue.attached_file_counts/1).

The shared "Photos and Files" tab panel: featured-image card plus the attached-files manager (dropzone, in-flight uploads, file grid with signed links and a confirm-guarded remove). One implementation for the catalogue / category / item forms so the three tabs cannot drift.

Renders the smart-catalogue rule editor: one row per candidate catalogue with a checkbox, a numeric value input, and a unit dropdown.

Renders the featured-image card used on catalogue, category, and item forms.

Featured-image thumbnail for list rows, rendered to the left of the name. Renders nothing when the resource carries no attached image. Works on anything with a data map holding "featured_image_uuid" — catalogue / category / item structs and the index's Map.from_struct/1 row maps alike.

The "Supplier price" cell text for one item's cost ranges (see Catalogue.supplier_cost_ranges/1): one supplier → 5.69, several → 5.69–9.99 (min–max of the current rows). Rows priced in different currencies are shown as separate ranges with their code — 5.69–9.99 EUR, 4.00 USD — so two currencies are never collapsed into one span. Nothing priced → .

The same Edit / Search PDFs / Delete menu WITHOUT the table cell — for card footers, where the boss standard is the ⋮ menu, not a row of icon buttons.

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

The name + SKU + sale-price cells for an item, rendered as standalone <td>s for a core-toolkit <.table_default> row. Pricing uses Catalogue.item_pricing/1 so the figures match the rest of the module. Pass edit_path (a 1-arity uuid -> path fn) to make the name a link.

The per-row action menu for an active item (Edit / Search PDFs / Delete), rendered as a standalone <td> for a core-toolkit row. Mirrors item_table's item_actions action set for the active list.

Renders a configurable item table with optional card view toggle.

Renders the metadata editor used inside the Metadata tab on the item and catalogue forms — heading + empty-state alert + one text input per attached key + add-picker dropdown.

The configurable columns of party_items_table/1, in the shape core's column_settings_modal/1 expects. name is deliberately absent: it is always shown, so it is not the picker's to remove.

An item list for another module to embed — the CRM company page's Catalogue tab today.

Renders a compact scope selector for narrowing a search to a subset of catalogues and/or categories.

Renders a search input with debounce and clear button.

Renders a search results count summary line.

Renders a table/card view toggle that syncs all tables sharing the same storage key.

Functions

any_media_thumb?(rows, counts)

Like any_featured_thumb?/1, but also counts the paperclip indicator: the media column earns its place when some row has an image OR attached documents (per the counts map from Catalogue.attached_file_counts/1).

attachments_files_panel(assigns)

The shared "Photos and Files" tab panel: featured-image card plus the attached-files manager (dropzone, in-flight uploads, file grid with signed links and a confirm-guarded remove). One implementation for the catalogue / category / item forms so the three tabs cannot drift.

Consumer contract: the LiveView uses PhoenixKitCatalogue.Attachments (mount_attachments + allow_attachment_upload) and delegates the cancel_upload and remove_file events; this component only renders.

Attributes

  • uploads (:any) (required)
  • files_state (:map) (required)
  • featured_image_uuid (:any) - Defaults to nil.
  • featured_image_file (:any) - Defaults to nil.
  • featured_subtitle (:string) (required)
  • files_hint (:string) (required)
  • remove_confirm (:string) (required)
  • remove_title (:string) (required)

catalogue_rules_picker(assigns)

Renders the smart-catalogue rule editor: one row per candidate catalogue with a checkbox, a numeric value input, and a unit dropdown.

Pairs with PhoenixKitCatalogue.Catalogue.put_catalogue_rules/3. The component is thin — the caller (usually ItemFormLive) owns the working-rules state in a map %{referenced_catalogue_uuid => %{value, unit}} and calls put_catalogue_rules/3 on save.

Event flow:

  • on_toggle%{"uuid" => uuid} when the checkbox is clicked. Caller toggles membership in its rules map.
  • on_set_value%{"uuid" => uuid, "value" => string} when the user edits the amount input.
  • on_set_unit%{"uuid" => uuid, "unit" => string} when the user picks a different unit.
  • on_clear — no params; clear every checked row. Shown only when at least one rule is active.

Rows for an unchecked catalogue render disabled inputs but stay visible so the user always sees the full picker. When value is blank and item_default_value is given, the input's placeholder previews the inherited default (e.g. "Inherit: 5"). The unit dropdown is self-contained per row — it does not inherit from any item-level default, so changing the item's default_unit never flips a rule row's visible unit.

Attributes

  • catalogues — list of %Catalogue{} the user can pick (required). Typically Catalogue.list_catalogues() filtered to active/archived and excluding the parent smart catalogue itself.
  • rules — map %{referenced_catalogue_uuid => %{value, unit}} (or %CatalogueRule{} values; only :value / :unit are read). Unchecked catalogues simply don't appear in the map (default %{}).
  • item_default_value — item's default_value, used as the value input's placeholder (default nil)
  • units — list of unit options for the dropdown (default ["percent", "flat"]). The first entry is the fallback shown when a rule has no unit set yet.
  • on_toggle — event name (default "toggle_catalogue_rule")
  • on_set_value — event name (default "set_catalogue_rule_value")
  • on_set_unit — event name (default "set_catalogue_rule_unit")
  • on_clear — event name (default "clear_catalogue_rules")
  • id — DOM id (default "catalogue-rules-picker")
  • class — extra wrapper classes

Example

<.catalogue_rules_picker
  catalogues={@candidate_catalogues}
  rules={@working_rules}
  item_default_value={@item_default_value}
/>

Attributes

  • catalogues (:list) (required)
  • rules (:map) - Defaults to %{}.
  • item_default_value (:any) - Defaults to nil.
  • units (:list) - Defaults to ["percent", "flat"].
  • on_toggle (:string) - Defaults to "toggle_catalogue_rule".
  • on_set_value (:string) - Defaults to "set_catalogue_rule_value".
  • on_set_unit (:string) - Defaults to "set_catalogue_rule_unit".
  • on_clear (:string) - Defaults to "clear_catalogue_rules".
  • on_reorder (:string) - When set, rule rows are draggable. Defaults to nil.
  • id (:string) - Defaults to "catalogue-rules-picker".
  • class (:string) - Defaults to "".

format_supplier_costs(ranges)

@spec format_supplier_costs(list() | nil) :: String.t()

The "Supplier price" cell text for one item's cost ranges (see Catalogue.supplier_cost_ranges/1): one supplier → 5.69, several → 5.69–9.99 (min–max of the current rows). Rows priced in different currencies are shown as separate ranges with their code — 5.69–9.99 EUR, 4.00 USD — so two currencies are never collapsed into one span. Nothing priced → .

item_card_menu(assigns)

The same Edit / Search PDFs / Delete menu WITHOUT the table cell — for card footers, where the boss standard is the ⋮ menu, not a row of icon buttons.

Attributes

  • item (:any) (required)
  • id_prefix (:string) - Defaults to "item-card-menu".
  • edit_path (:any) - Defaults to nil.
  • on_delete (:string) - Defaults to nil.
  • pdf_search_event (:string) - Defaults to nil.

item_picker(assigns)

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

Thin wrapper around the ItemPicker LiveComponent — it's the LiveComponent that owns search state, events, and the colocated JS hook. This wrapper exists so consumers have an attr-declared call site and don't have to remember <.live_component module={...}>.

The parent LiveView reacts to two messages in its handle_info/2:

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

where id is the :id you passed in — handy for multiple pickers on one page.

Examples

<.item_picker
  id={"row-#{@row.id}-picker"}
  category_uuids={[@category_uuid]}
  selected_item={@row.item}
  excluded_uuids={@used_uuids}
  locale="en"
/>

See PhoenixKitCatalogue.Web.Components.ItemPicker for the full attr reference and the keyboard / a11y contract.

Attributes

  • id (:string) (required)
  • category_uuids (:any) - Defaults to nil.
  • catalogue_uuids (:any) - Defaults to nil.
  • include_descendants (:boolean) - Defaults to true.
  • only (:atom) - Restrict results to uncategorised or categorised items only. Defaults to nil. Must be one of nil, :uncategorized_only, or :categorized_only.
  • selected_item (:any) - Defaults to nil.
  • excluded_uuids (:list) - Defaults to [].
  • locale (:string) (required)
  • placeholder (:string) - Defaults to nil.
  • empty_query_limit (:integer) - Defaults to 10.
  • page_size (:integer) - Defaults to 20.
  • disabled (:boolean) - Defaults to false.
  • format_price (:any) - Defaults to nil.
  • format_unit (:any) - Defaults to nil.
  • show_unit (:boolean) - Defaults to false.
  • highlight_selected (:boolean) - Defaults to true.
  • initial_query (:string) - Defaults to nil.
  • photo_clickable (:boolean) - Defaults to false.

item_pricing_cell(assigns)

The name + SKU + sale-price cells for an item, rendered as standalone <td>s for a core-toolkit <.table_default> row. Pricing uses Catalogue.item_pricing/1 so the figures match the rest of the module. Pass edit_path (a 1-arity uuid -> path fn) to make the name a link.

Renders the name cell (link), then one cell per entry in columns"sku" / "price" / "unit" / "status" — in the given order, so a Columns configuration controls both visibility and sequence.

Attributes

  • item (:any) (required)
  • edit_path (:any) - Defaults to nil.
  • has_attributes (:boolean) - Defaults to false.
  • file_count (:integer) - Defaults to 0.
  • columns (:list) - Defaults to ["sku", "price", "unit", "status"].
  • supplier_costs (:list) - This item's entry from Catalogue.supplier_cost_ranges/1 (drives "supplier_price"). Defaults to [].

item_row_menu(assigns)

The per-row action menu for an active item (Edit / Search PDFs / Delete), rendered as a standalone <td> for a core-toolkit row. Mirrors item_table's item_actions action set for the active list.

Attributes

  • item (:any) (required)
  • edit_path (:any) - Defaults to nil.
  • on_delete (:string) - Defaults to nil.
  • pdf_search_event (:string) - Defaults to nil.

item_table(assigns)

Renders a configurable item table with optional card view toggle.

Columns are opt-in — only the columns you list are shown. Actions (edit, delete, restore) are opt-in via their respective attributes.

Attributes

  • items — list of items to display (required)
  • columns — list of column atoms to show (default: [:name, :sku, :base_price, :status]) Available: [:name, :sku, :base_price, :price, :discount, :final_price, :unit, :status, :category, :catalogue, :manufacturer]
  • cards — enable card view toggle (default: false). When enabled, renders a table/card toggle button and shows items as cards on mobile. The card view shows the item name as the title, selected columns as key-value fields, and action buttons in the card footer.
  • id — unique ID for the component (required when cards is true, used by the JS hook to persist view preference)
  • markup_percentage — catalogue markup for :price and :final_price columns (required when either is listed; ignored otherwise)
  • discount_percentage — catalogue discount for :discount and :final_price columns (required when either is listed; ignored otherwise). The :discount column honors per-item overrides via Item.effective_discount/2.
  • edit_path — 1-arity function (uuid -> path) to enable edit links
  • on_delete — event name for soft-delete button (e.g. "delete_item")
  • on_restore — event name for restore button (e.g. "restore_item")
  • on_permanent_delete — event name for permanent delete (e.g. "show_delete_confirm")
  • permanent_delete_type — type string passed as phx-value-type (e.g. "item")
  • catalogue_path — 1-arity function (uuid -> path) for catalogue links in :catalogue column
  • variant — table variant: "default" or "zebra" (default: "default")
  • size — table size: "xs", "sm", "md", "lg" (default: "sm")
  • wrapper_class — override wrapper CSS class

Examples

<%!-- Table only --%>
<.item_table items={@items} columns={[:name, :sku, :base_price]} />

<%!-- With card view toggle --%>
<.item_table
  items={@items}
  columns={[:name, :sku, :base_price, :price, :status]}
  cards={true}
  id="catalogue-items"
  markup_percentage={@catalogue.markup_percentage}
  edit_path={&Paths.item_edit/1}
  on_delete="delete_item"
/>

Attributes

  • items (:list) (required)

  • columns (:list) - Defaults to [:name, :sku, :base_price, :status].

  • cards (:boolean) - Defaults to true.

  • photo_click (:string) - Event pushed (with the item's uuid) when a featured-image thumb is clicked — the host renders the ProductCard modal and handles its events. nil keeps thumbs inert. Defaults to nil.

  • file_counts (:map) - %{item_uuid => attached-document count} from Catalogue.attached_file_counts/1 — drives the paperclip indicator in the photo column. Computed by the caller (function components must not query). Defaults to %{}.

  • attribute_map (:map) - %{item_uuid => attribute_group_uuid} from Catalogue.item_attribute_group_map/1 — drives the swatch indicator beside the name. Computed by the caller. Defaults to %{}.

  • show_toggle (:boolean) - Defaults to true.

  • id (:string) - Defaults to nil.

  • storage_key (:string) - Defaults to nil.

  • markup_percentage (:any) - Defaults to nil.

  • discount_percentage (:any) - Defaults to nil.

  • edit_path (:any) - Defaults to nil.

  • name_path (:any) - Where an item's NAME links, as a 1-arity function of the item. Defaults to edit_path (the catalogue's own convention). An embedded, read-only list can point it somewhere else — landing on an edit form from a list you are only reading is a surprise.

    Defaults to nil.

  • on_delete (:string) - Defaults to nil.

  • on_restore (:string) - Defaults to nil.

  • on_permanent_delete (:string) - Defaults to nil.

  • permanent_delete_type (:string) - Defaults to "item".

  • catalogue_path (:any) - Defaults to nil.

  • variant (:string) - Defaults to "default".

  • size (:string) - Defaults to "sm".

  • wrapper_class (:string) - Defaults to nil.

  • pdf_search_event (:string) - When set, action menu gets a 'Search PDFs' entry that pushes this event with phx-value-uuid. Defaults to nil.

  • on_reorder (:string) - When set, rows become draggable and emit this event. Defaults to nil.

  • reorder_scope (:map) - Map of extra scope values (e.g. %{catalogue_uuid: "...", category_uuid: "..."}) — exposed to the SortableGrid hook as data-sortable-scope-* attrs. Defaults to %{}.

  • reorder_group (:string) - SortableJS group name; tables sharing a group can exchange items via cross-container drag (e.g. items moving between categories). Defaults to nil.

  • selectable (:boolean) - When true, each row gets a checkbox in the leftmost column (combined with the drag handle when reorderable). The drag handle is hidden until the row is hovered. Defaults to false.

  • selected_uuids (:any) - MapSet of selected item UUIDs. Defaults to nil.

  • on_toggle_select (:string) - Event name fired when the user toggles a row's checkbox. The LV handler receives phx-value-uuid. Defaults to nil.

metadata_editor(assigns)

Renders the metadata editor used inside the Metadata tab on the item and catalogue forms — heading + empty-state alert + one text input per attached key + add-picker dropdown.

Owner LV must handle the three events wired up by this component:

  • add_meta_field (from the add-picker <.select>'s phx-change)
  • remove_meta_field (per-row × button)
  • (text edits are absorbed by the form's phx-change="validate" via Metadata.absorb_params/2)

Attributes

  • resource_type:item or :catalogue; drives which Metadata.definitions/1 list is consumed for the add-picker and for legacy-key detection
  • state — the %{attached: [key], values: %{key => string}} map produced by Metadata.build_state/2 and kept on the socket
  • id_prefix — DOM-id prefix for inputs and the add-picker (so the same Metadata editor can render twice on a page without colliding)
  • title — heading text (optional, defaults to "Metadata")
  • description — the grey subtitle under the heading (optional)

Examples

<.metadata_editor
  resource_type={:catalogue}
  state={@meta_state}
  id_prefix="catalogue"
/>

Attributes

  • resource_type (:atom) (required)
  • state (:map) (required)
  • id_prefix (:string) (required)
  • title (:string) - Defaults to nil.
  • description (:string) - Defaults to nil.

party_items_columns()

The configurable columns of party_items_table/1, in the shape core's column_settings_modal/1 expects. name is deliberately absent: it is always shown, so it is not the picker's to remove.

party_items_default_columns()

Default shown columns for party_items_table/1.

party_items_table(assigns)

An item list for another module to embed — the CRM company page's Catalogue tab today.

This is a deliberately narrow wrapper around item_table/1 rather than a second table. A caller outside this package cannot invoke item_table/1 itself: HEEx injects attr defaults at the CALL SITE, so reaching it through apply/3 would mean the caller supplying every attribute by hand and re-supplying each new one we add. Here the defaults are applied inside the catalogue, and the contract with the caller is two keys.

Takes a plain map (no attr defaults are available through apply/3):

  • :items%Item{} structs, ideally hydrated by Manufacturers.hydrate/1 so the manufacturer column has a name
  • :id — unique DOM id for the table
  • :columns — optional; which columns to show, in order. Defaults to a sensible set. Pair it with core's column_settings_modal/1 and managed_columns/0 below to give the embed a working column picker.

Presentation — the image column, the card/table toggle, price and status formatting — stays owned by the catalogue, so an embedded list keeps matching the catalogue's own. That includes the convention that an item's name opens its edit form; the catalogue column is the way back to where the item lives.

scope_selector(assigns)

Renders a compact scope selector for narrowing a search to a subset of catalogues and/or categories.

Designed to pair with Catalogue.search_items/2's :catalogue_uuids and :category_uuids options. The component is thin — the parent LiveView owns the selection state and decides which catalogues and categories are pickable. Typical flow:

# LV loads the pickable set (e.g. via list_catalogues_by_name_prefix/2)
socket
|> assign(:scope_catalogues, Catalogue.list_catalogues_by_name_prefix("Kit"))
|> assign(:scope_categories, [])
|> assign(:selected_catalogue_uuids, [])
|> assign(:selected_category_uuids, [])

Renders as a disclosure with a summary ("2 catalogues · all categories") and two checkbox lists inside. Each section is only rendered when its list is non-empty, so callers can use it for catalogue-only or category-only scoping.

Events

Emits four events (all names customizable via attrs):

  • on_toggle_catalogue%{"uuid" => uuid} when a catalogue is clicked
  • on_toggle_category%{"uuid" => uuid} when a category is clicked
  • on_clear_catalogues — no params; clear all catalogue selections
  • on_clear_categories — no params; clear all category selections

The LV toggles membership in its own selection lists, then re-runs the search with the updated scope.

Attributes

  • catalogues — list of %Catalogue{} the user can pick from (default [])
  • categories — list of %Category{} the user can pick from (default [])
  • selected_catalogue_uuids — currently selected catalogue UUIDs (default [])
  • selected_category_uuids — currently selected category UUIDs (default [])
  • on_toggle_catalogue — event name (default "toggle_catalogue_scope")
  • on_toggle_category — event name (default "toggle_category_scope")
  • on_clear_catalogues — event name (default "clear_catalogue_scope")
  • on_clear_categories — event name (default "clear_category_scope")
  • id — DOM id (default "scope-selector")
  • open — force the disclosure open (default false — collapsed until clicked)
  • class — extra CSS classes on the wrapper

Example

<.scope_selector
  catalogues={@scope_catalogues}
  categories={@scope_categories}
  selected_catalogue_uuids={@selected_catalogue_uuids}
  selected_category_uuids={@selected_category_uuids}
/>

Attributes

  • catalogues (:list) - Defaults to [].
  • categories (:list) - Defaults to [].
  • selected_catalogue_uuids (:list) - Defaults to [].
  • selected_category_uuids (:list) - Defaults to [].
  • on_toggle_catalogue (:string) - Defaults to "toggle_catalogue_scope".
  • on_toggle_category (:string) - Defaults to "toggle_category_scope".
  • on_clear_catalogues (:string) - Defaults to "clear_catalogue_scope".
  • on_clear_categories (:string) - Defaults to "clear_category_scope".
  • id (:string) - Defaults to "scope-selector".
  • open (:boolean) - Defaults to false.
  • class (:string) - Defaults to "".

search_input(assigns)

Renders a search input with debounce and clear button.

Emits search event with %{"query" => value} on change/submit, and clear_search on clear button click. Override event names via attrs.

Attributes

  • query — current search query string (required)
  • placeholder — input placeholder text. nil (default) resolves to a translated gettext("Search...") inside the component body. Pass an explicit string to override (e.g. gettext("Search items...")).
  • on_search — event name for search (default: "search")
  • on_clear — event name for clear (default: "clear_search")
  • debounce — debounce ms (default: 300)
  • class — additional CSS classes on the wrapper div

Attributes

  • query (:string) (required)
  • placeholder (:string) - Defaults to nil.
  • on_search (:string) - Defaults to "search".
  • on_clear (:string) - Defaults to "clear_search".
  • debounce (:integer) - Defaults to 300.
  • class (:string) - Defaults to "".

search_results_summary(assigns)

Renders a search results count summary line.

Attributes

  • count — total number of matching results (required)
  • query — the search query string (required)
  • loaded — optional count of results currently rendered. When given and less than count, the summary shows "X of Y" so users know the list is paging. Omit or pass nil for a plain "N results" line.

Attributes

  • count (:integer) (required)
  • query (:string) (required)
  • loaded (:integer) - Defaults to nil.

view_mode_toggle(assigns)

Renders a table/card view toggle that syncs all tables sharing the same storage key.

Place this once at the top of a page, and set show_toggle={false} + matching storage_key on the individual item_table components.

Uses the same localStorage mechanism as table_default's built-in toggle, so all tables reading the same key will respect the user's choice.

Attributes

  • storage_key — the localStorage key to sync (required, must match the tables)
  • class — additional CSS classes

Examples

<.view_mode_toggle storage_key="catalogue-items" />
<.item_table cards={true} show_toggle={false} storage_key="catalogue-items" ... />

Attributes

  • storage_key (:string) (required)
  • class (:string) - Defaults to "".