Entity picker
betaRecord reference field: removable record chips plus Add, built on the canonical RecordSelect (server search, recent, create from e-mail).
`apps/web/src/lib/anchored-panel.ts` is gone from origin/main — anchoring moved into the shared Popover (acrm 26de4b54b, AddRefMenu/RecipientInput/AssignPicker/deal-parts); the reference was dropped from this entry. Optional prop `recent` (27 Sep 2026, `RelationDialog`'s entity picker is the first consumer): on an empty/below-minimum query it offers recently opened records of the allowed `types` instead of the "type at least N characters" hint, sourced from `useRecentRecords` (`GET /search/recent` → `record_views`, written server-side when a record's detail loads — real opens, not search clicks). Rows reuse the exact `.acrm-recordselect-opt` markup and `add()` path as a search result; already-selected and locked records are filtered out client-side. Without the prop the picker behaves exactly as before. Debt: `EntityPicker` lives in `apps/web/src/components/EntityPicker.tsx`, not `packages/ui`, so it has no Ladle story — behaviour is covered by `relation-dialog.test` instead. Move it to `packages/ui` to get stories (AD 27 Sep 2026). 3 Oct 2026 (AD): deprecated → beta. The duplicate shell is gone (AddRefMenu removed, 4a9a934e0; ProductPicker on RecordSelect since 1 Oct); it is a composition over RecordSelect, not a second picker. Migrating its consumers would rebuild the same composition each time. Canon after: move into @acrm/ui with stories (Default · States · Composition) + baselines, usage/a11y rewritten. 3 Oct 2026: shell is DS.RecordRefField in @acrm/ui (acrm !4502); EntityPicker / ProductPicker are data wrappers around it, DOM 1:1. Focus returns to Přidat on close. Registry repaired 17 Oct 2026 (review F1): AddRefMenu.tsx is gone from main (file reference dropped). Registry repaired 17 Oct 2026 (review F2): removed classes with no CSS definition and no TSX use on main: .acrm-popover.eref-add-pop.
Used in 13 files · 11 areas · 14× — components/ProductPicker, components/calendar, components/deal, components/dialer, components/mail, components/meetings …
apps/web <EntityPicker> <RecordRefField>.eref-field .eref-add .add-ref .acrm-recordselect-search +4SourceLegacy card Inputs.htmlSeveral references to records of mixed types (user, contact, organization, deal) as removable tokens — the form variant of Entity reference. App component EntityPicker (apps/web/src/components/EntityPicker.tsx): a composition over the canonical Record select, not a select of its own. Picked records render as <EntityRef removable> tokens in .eref-field; the always-present "Přidat" (.add-ref) opens RecordSelect anchored to it in adding mode (keepOpen, value={null}) with server search over /mentions/search.
Status: beta (AD 3 Oct 2026). The former duplicate shell (AddRefMenu) is gone (acrm 4a9a934e0): the panel, search row, rows, states, keyboard and focus are Record select's. Canon after the DoD — stories, visual baseline, audit — the shell is DS.RecordRefField in @acrm/ui (acrm !4502); this component is its data wrapper.
When to use
- A field that links several records to a task, meeting, note or relation and shows them as tokens: participants, linked contacts and companies, deals of a meeting.
- Server search over the whole CRM (thousands of records) with a type filter:
types={PERSON_TYPES}(users + contacts + organizations, P8) orDEAL_TYPES. max={1}when the relation is single but the picked record must stay visible as a token with "×" (company of a contact).
When not to use
- One value in a field or dialog row — plain Record select (
useRecordSearchfor server search). One value = Record select, several record references = Entity picker. - A rail row — Rail picker.
- An in-memory list (≤ a few hundred rows) where the choice is a set of options, not records — Record select
multior Selectmultiple. - Products — Product picker.
Anatomy
.acrm-field>.acrm-field-label(unlesshideLabel) >.eref-field.- Tokens:
<EntityRef removable>viaEntityRefPreview(hover preview; name opens the record whenonOpenEntityis given). Optional state after the name (statusOf):.eref-dotwith tone and title, deliverystatusicon, short.eref-meta— meeting invitations only. - E-mail tokens (
emailsmode): a bare<EntityRef>with just the typed address — the contact is created by the server on save. .eref-add>button.add-ref("Přidat",aria-haspopup="listbox",aria-expanded), hidden oncemaxis reached.- Panel:
RecordSelectwithanchorRef=.eref-add,keepOpen,filter={false},onQueryChange(debounce 180 ms),minWidth={320}. Rows carry the type mark (entityType) andsub= sublabel · type label (recordTypeLabel). Already-picked records are filtered out — no check in rows. - States from Record select: below
MENTION_MIN_QUERY"Napište alespoň N znaky", loading only without data in hand, no results.
Recent records (recent)
With recent, a query below MENTION_MIN_QUERY lists recently opened records of the allowed types (useRecentRecords, GET /search/recent, genuine detail opens) instead of the hint. Locked records and disallowed types are filtered out; same rows and same pick path as search results. First consumer: the other-side picker in RelationDialog.
Invite by e-mail (emails)
A typed valid address is offered as the first row ("Nový kontakt: …", createPosition="top", icon mail) and Enter takes it — unless a contact with exactly that address is found, which is offered instead so no duplicate contact is created. The address survives closing the panel by click outside; Esc discards it. Only New meeting uses it.
Best practices
- Pass
labeleven withhideLabel— it names the listbox. - Use
max={1}instead of a single-value variant when the token must stay visible. - Turn on
recentwhere the user usually picks something just opened.
- Build "tokens + Přidat + search" again around a bare Record select — that is this component.
- A visible bordered input with tokens inside — references float, search lives in the panel.
- Use it for one value in a form row — that is Record select.
Content
- "Přidat" (
addLabel); search placeholder fromplaceholder(default "Hledat…"). - Short-query hint "Napište alespoň N znaky" is a state, not an explanation.
- Type label in
subfromrecordTypeLabel("Kolega", "Kontakt", "Firma", "Obchod").
Related
record-select— the panel (adding mode, server search,onCreatetop row).entity-ref(variant removable) — the tokens.product-picker— the same composition for the product catalog.rail-picker— the rail equivalent.formless-dialog— New meeting, Edit meeting, Relation, New project use it.- Stories: Components / Record reference field — Default · States · Composition (
DS.RecordRefField, acrm !4502).
Source: docs/select/entity-picker.usage.md · GitLab