StatifierBlocks.Editor.PaletteBrowser (StatifierBlocks v0.3.0)

Copy Markdown View Source

The grouped, searchable, filterable palette (ADR-0005 decisions 8, 10, 13).

The entry's icon

An entry renders the tile the card it produces will carry, resolved by the same seam and through the same icon assign - Editor hands this component and Editor.BlockNode the identical value. The tile was declared here and rendered nowhere for the whole of the graduated editor's life (sb-jja), so a host could not put an icon on a palette row at all; a palette that showed no icons above a canvas that did was the visible half of that.

Everything it renders comes from palette_entry/0 through StatifierBlocks.ViewModel, which already applied decision 10's defaults - label to the type name, group to "Other", description to "", icon to nil, keywords to [], order to 0. A block type that implements none of the callback still renders here, and ADR-0002 decision 5 promised it would.

Two filters compose, and they are different in kind:

  • Search is the author's, over label, description, type name and keywords. Purely presentational.
  • Acceptance is the slot's. When the palette was opened from a specific gap, allowed carries the type names that slot will take, computed by StatifierBlocks.Editor with the same predicate a drag uses - Edit.Targets.droppable_slots_for/3 against a probe block of each candidate type. Decision 8 is explicit that the filter uses the same predicate as decision 5, not a parallel implementation, and this component deliberately computes none of it: it is handed a set.

The strip, below 780 (7A)

The 2026-08-29 shell amendment gives the palette a second shape: below a container width of 780 it collapses to a strip - a label and a "+" - that opens as a sheet over the canvas, so the inspector gets the full row. Both shapes are always in the markup and the stylesheet decides which one is on screen, because the breakpoint is a container query and the server does not know how wide the host gave the editor. What the server owns is whether the sheet is open, which is one boolean and one event; selecting a block or picking an entry closes it, since a sheet left open covers the thing the author just chose.

Picking an entry emits an :insert at exactly the position the "+" named, which is the identical command a successful drop would produce. That is what makes the whole insertion path exercisable in LiveViewTest without simulating a drag, and it is why decision 8 is not only an accessibility affordance - though it is that, drag-and-drop being unusable by keyboard and hostile on touch.

Summary

Functions

The groups a query and an acceptance set leave visible, with empty groups dropped. Pure, so the palette's filtering is asserted directly rather than through markup.

The palette: a search box, then a section per entry.group.

Functions

filter(groups, query, allowed)

The groups a query and an acceptance set leave visible, with empty groups dropped. Pure, so the palette's filtering is asserted directly rather than through markup.

palette_browser(assigns)

The palette: a search box, then a section per entry.group.

Attributes

  • groups (:list) (required)

  • query (:string) - Defaults to "".

  • allowed (:any) - MapSet of accepted type names, or nil for unfiltered. Defaults to nil.

  • target (:any) (required)

  • icon (:any) - The host's icon component, or nil for StatifierBlocks.Editor.Icons. The same value the canvas cards get, so a type looks the same before and after the pick that puts it in the document.

    Defaults to nil.

  • class (:string) - Defaults to nil.

  • sheet_open (:boolean) - Whether the narrow-layout sheet is open (7A). Ignored above 780. Defaults to false.