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.htmlKeyboard
- "Přidat" is a
button.add-ref: Tab focuses it, Enter or Space opens the panel; focus moves to the search input on the next animation frame (Record select). - Typing searches on the server after
MENTION_MIN_QUERYcharacters (debounce 180 ms). - Enter in the search picks the first row (adding mode): the first result, the first recent record (
recent), or the typed address (emails, offered first). Nothing is picked while results are loading. - Arrow keys, Home and End move between rows (canonical
Popover); rows are also Tab stops; Enter or Space picks. The panel stays open after a pick and the search keeps focus (keepOpen); withmaxreached it closes. - Esc closes the panel and, in
emailsmode, discards a typed address. Click outside closes it and keeps a typed valid address as a token. - Removing a token uses its own "×" (Entity reference, focusable).
ARIA
- Trigger:
aria-haspopup="listbox",aria-expanded. - Panel = Record select: search input with
aria-label(the placeholder); listrole="listbox"witharia-label={label}(required even withhideLabel); rowsbutton[role="option"]witharia-selected="false"— picked records leave the list. Adding mode is not multi, so the listbox has noaria-multiselectable; the picked set is the tokens, not the list. - Token state (
statusOf): the dot carriesaria-label= its title, so the invitation state is not colour only. - The type mark in a row is decorative; the type name is text in
sub.
Focus
- On close (Esc, outside click,
maxreached) focus returns to „Přidat“ (DS.RecordRefField, acrm !4502).
.add-ref, rows and token "×" use the foundation ringoutline: 2px solid var(--accent); outline-offset: 1px.- Search input has no ring of its own (Record select).
- After a pick focus stays in the search (
keepOpen).
Contrast
Record select tokens: row name --ink-900 17.59:1 (light) / 14.53:1 (dark); sub --fg-secondary 300 12px (#707477 since 2 Oct 2026) ≥ 4.5:1 (light) / 6.2:1 (dark); hint and empty text --fg-muted 2.85:1 (light) / 3.63:1 (dark). Tokens, the .add-ref pill and state dots are documented with Entity reference.
Known limits
- The switch from recent records to search results (
recent) is visual only — no group name, no live announcement. - Hint and empty text use
--fg-muted(2.85:1 light) on purpose: supplementary text stays light (owner, 3 Oct 2026); readable text uses--fg-secondary.
Source: docs/select/entity-picker.a11y.md · GitLab