Components › Forms & input › Select › Record select

Record select

canon

Search-driven choice from records: framed field, or panel anchored to a rail row or a chip. Sections separated by a named hairline, one value = accent check.

Absorbed Rail picker on 12 September 2026 (AD, RP-1; acrm !3425–!3443): with anchorRef the panel hangs under a rail row or a chip instead of a framed trigger, so the rail has no picker of its own and RailPickerMenu is deleted. Also new: sections whose name sits IN the hairline (only when there are two or more; a single name would be the forbidden heading above a menu), selected pinned on top, accent check for one value (F261), diacritics-folded search over sub and keywords, quick-create footer, minWidth, loading/empty/busy, UserAvatar for a colleague. Known limit: the section name is var(--label) in --fg-muted = 2.85:1 light / 3.63:1 dark, below AA in both themes — the lighter tone is the AD's explicit choice of 12 September 2026 (ACRM-227, acrm !3460), made with the numbers in front of him; the group is announced from role=group + aria-label, not from this text. The hand-made panel shell is blocked by the validator rule recordselect-shell-handmade. Slot wrapOption(option, row) (acrm !3913, AD 26 Sep 2026): the host may WRAP an option row in an element that clicks nothing and does not change its size (hover preview EntityPreviewChip); selection, keys and look stay with RecordSelect. Used by RecordLinksChip — the one links chip of shape-A dialogs (task, deal, meeting, note), selected records show the record preview on hover.

Used in 46 files · 31 areas · 52× — components/RecordLinksChip, components/ReservationControl, components/TagsEditor, components/accounting, components/acl, components/clen-tymu …

Import

import { RecordSelect } from '@acrm/ui'

Props

RecordSelect packages/ui/src/components/RecordSelect.tsx

NameTypeDefaultDescription
options *RecordOption[]——
value *string | string[] | null | undefined—multi=false → string|null; multi=true → string[].
onChange *(value: string | string[]) => void——
multibooleanfalse—
placeholderstring'Doplnit…'—
searchPlaceholderstring'Hledat záznamy…'—
classNamestring——
ariaLabelstring——
fullWidthbooleanfalseTrigger na plnou šířku kontejneru (jinak default 280px).
clearablebooleanfalseZobraz „×" pro vymazání hodnoty (single: → '', multi: → []).
disabledbooleanfalseZamkne výběr (načítání / jediná možnost) — trigger disabled, neotevře se.
anchorRefRefObject<HTMLElement | null>—Cizí kotva místo rámovaného pole (hodnota řádku railu, chip). Namontováno = otevřeno; Esc, klik mimo kotvu i panel a výběr u jedné hodnoty volají onClose.
onClose() => void——
onQueryChange(query: string) => void—Hledání běží na serveru: text jde ven, options přijdou zpět.
loadingbooleanfalseMísto seznamu „Načítám…".
error{ text: string; onRetry: () => void; }—Zdroj voleb selhal (AD 30. 9. 2026): místo seznamu JEDEN řádek — věta a akce „Zkusit znovu" (kompaktní ghost tlačítko), ne šedá vysvětlivka a ne prázdná nabídka.
emptyTextstring—Text prázdného číselníku (bez hledání). Nenalezeno je vždy „Žádné výsledky".
onCreate(text: string) => void—Patička „Založit „…"" (+ Enter) — nabídne se, když napsané přesně nesedí na žádnou položku.
createLabel(text: string) => string—Text patičky onCreate místo „Založit „…"" — např. „Použít text „…"" u volného textu produktu. Bez vysvětlivky v řádku (AD 1. 10. 2026): rozdíl nese glyf createIcon.
createIconstring'plus'Glyf patičky onCreate (výchozí plus), např. type u volného textu.
createPosition'top' | 'bottom''bottom'Kde stojí řádek onCreate: bottom = patička (výchozí), top = první řádek seznamu — napsaná e-mailová adresa „Nový kontakt: …" (AD 1. 10. 2026): fulltext na adresu vrací hlavně šum, takže odpovědí je akce. V režimu přidávání ho pak Enter bere jako první.
filterbooleantruefalse = volby už vyfiltroval server (onQueryChange) a klient je podruhé nefiltruje — jinak by zahodil shody, které server našel podle údaje mimo jméno (telefon, IČO). Používá se JEN s hledáním na serveru. emptyText pak platí i pro prázdnou odpověď na dotaz („Napište alespoň 3 znaky").
keepOpenbooleanfalseRežim přidávání (AD 1. 10. 2026): výběr zavolá onChange, vymaže dotaz a panel zůstane otevřený s fokusem v hledání pro další položku. Bez checkboxů — není to vícevýběr sady, vybraná položka se u hostitele stává chipem a ze seznamu zmizí. Na telefonu v šuplíku dialogu má panel dole Hotovo jako vícevýběr; Esc a tap mimo zavírají.
busybooleanfalseZápis běží (fronta mutací) — jen aria-busy, volby zůstávají klikací.
minWidthnumber—Minimální šířka panelu u úzké kotvy (chip); jinak šířka = kotva.
maxWidthnumber—Strop šířky panelu u široké kotvy — pole přes celý řádek dialogu (Komu, Řešitel): panel návrhů by jinak měl šířku celého řádku (1021 px na 1440).
wrapOption(option: RecordOption, row: ReactElement<unknown, string | JSXElementConstructor<any>>) => ReactNode—Obal řádku volby (26. 9. 2026, ArtDirector): host smí řádek obalit vlastním prvkem, který sám nic neklikne — typicky hover náhled záznamu (EntityPreviewChip). Výběr, klávesy i vzhled řádku dál řídí RecordSelect; bez propu je řádek beze změny. Obal NESMÍ mít vlastní onClick ani měnit rozměr řádku (jen naslouchá najetí a fokusu).
querystring—Řízený dotaz (AD 1. 10. 2026, stejné pravidlo jako PickerMenuPanel přes useControlledQuery): když je query zadané, panel dotaz nedrží a jen hlásí změny přes onQueryChange.
searchFieldbooleantruefalse = panel bez vlastního hledání: píše se v poli VOLAJÍCÍHO (našeptávač u pole — Komu, Řešitel, položky obchodu; bod B, AD 1. 10. 2026). Dotaz pak přichází přes query, panel se nekreslí jako šuplík (je to našeptávání při psaní, ne výběr) a klávesnice jde z fieldRef.
fieldRefRefObject<HTMLInputElement | null>—Pole volajícího u searchField={false}: fokus v něm zůstává, ↑↓ posouvají aktivní řádek (aria-activedescendant), Enter/Tab vybere aktivní, Esc zavře. Pole dostane roli combobox.

CSS classes

.acrm-recordselect.acrm-recordselect-trigger.acrm-recordselect-pop.acrm-recordselect-search.acrm-recordselect-opt.acrm-recordselect-empty.acrm-recordselect-list.acrm-recordselect-tx.acrm-recordselect-clear.acrm-recordselect--full.acrm-recordselect-ck.acrm-recordselect-sec.acrm-recordselect-grp.acrm-recordselect-foot.acrm-recordselect-create

Files