Rail
canonThe record panel: context actions, 34px key-value rows edited in place (several values = several rows), tags, description and relations.
Merged 14 September 2026 (AD, RD-1): Key-value row, Editable field and Multi-value were separate pages; they are parts of this one card. The app copy of the row (KeyValueRow, .pr-row) is being replaced by the rail row and deleted. A value with href/onOpen never opens the editor on click and keeps a permanently visible pencil (AD FA10-re); errors are the caller’s toast. The multi-value editor popover lives in the app (components/multivalue/*, ds/multi-value.css). Mobile sheet merged 15 September 2026 (MR !3539): below 720px `MultiValuePopover` renders as a bottom sheet instead of an anchored popover (`.phe-scrim`, `.phe-grab`, `.phe-sheethead`, `.phe-sheetfoot` in `ds/responsive.css`) so it can be closed and confirmed without a keyboard — see "Mobile" in rail.usage.md / rail.a11y.md. Stories rebuilt from the real person rail 15 September 2026 (acrm !3544): four quiet icon actions, Detail záznamu / Procesy / Seznamy, no empty section header; the rail has no shortcuts, so its „With icon & kbd" story and both its baselines are gone. Meeting attendees composition (owner 25.–27. Sep 2026, acrm !4014/!3985, all in `apps/web/src/ds/meeting-record.css` — not yet ported into record-rail.css): field `type: 'number'` for a small bounded count paired with `useConfirm` (speaker count, range 1–20 per the diarization service, raised from 10 on acrm !3985); a relation row's right-aligned talk-share bar (56px `--line-200` track, 4ch tabular-nums percentage) — candidate for a named "share bar" element; an unassigned-voice row with `--sp-3` icon-to-text / `--sp-4` name-to-action spacing; and a scoped exception (`.rr-list--attendees`) that keeps the removable reference's × contextual on touch instead of the global `ds.css` always-visible rule, so it never sits permanently over the share bar. Touch (AD 28 Sep 2026, acrm !4111): the description measures its real clamp (.rr-desc-v.is-clamped, ResizeObserver) and shows the chevron only then; on hover: none copy and the chevron are always visible with 44px hit areas, and copy ↔ pencil keep a 30px gap so two 44px targets never overlap (the rule beats --sp-2). Overrides live in apps/web/src/ds/responsive.css. Registry repaired 17 Oct 2026 (review F4/F11): attached css detail-field.css.
Used in 29 files · 20 areas · 79× — components/PersonaProfilePanel, components/clen-tymu, components/company, components/deal, components/decisions, components/dialer …
@acrm/ui <RecordRail> <EditableField> <useInlineEdit> <RailRows> <RailRow>.rr-row .rr-sec-h .rr-relrow .rr-dot +25SourceStoryLegacy card Record detail rail.htmlLegacy card Multi-value editor.htmlImport
import { RecordRail, EditableField, useInlineEdit, RailRows, RailRow } from '@acrm/ui'
Props
RecordRail packages/ui/src/components/RecordRail.tsx
| Name | Type | Default | Description |
|---|---|---|---|
actions | RRAction[] | — | vrstva 1 — kontextové akce (tlačítka); bez nich se akční řádek vynechá |
title | string | 'Detail záznamu' | titulek KV sekce (default „Detail záznamu") |
count | number | — | Počet v hlavičce KV sekce — span.cnt hned za titulkem, TÝŽ prvek i třídy jako u relace (RRRelation.count). Sekce polí ho potřebuje tam, kde nese seznam věcí stejného druhu (schůzka → „Nahrávky 2", FA198); u běžného „Detail záznamu" se nepředává a nic se nekreslí. |
fields * | RRField[] | — | — |
importantCount | number | — | kolik polí je „nejdůležitějších" — zbytek za „Zobrazit všechny hodnoty" |
onSave | (changes: Record<string, string>) => void | Promise<void> | — | uložení změněného pole {key: value}; bez něj je panel read-only |
relations | RRRelation[] | — | vrstva 3 — relace (Seznamy) |
onCopy | (value: string, label: string) => void | — | Vzetí hodnoty do schránky u polí s copyable. Zápis i potvrzení řeší VOLAJÍCÍ — toast je vrstva appky, ne DS, a rail by si ho jinak musel natáhnout dovnitř. Bez handleru se ikona nekreslí vůbec. |
renderRef | (item: RRRelationItem) => ReactNode | — | Čím se kreslí reference v řádcích relací. Bez něj se kreslí <EntityRef> přesně jako dosud, takže existující volající se nemění ani vizuálně, ani chováním. ⚠️ Proč render-prop a ne id na položce, ze kterého by si rail náhled natáhl sám: DS balíček NESMÍ znát doménový tvar API — @acrm/ui na @acrm/shared závislost nemá a je to záměr (hlavička packages/shared/src/entity-preview.ts). Takhle rail dál ví jen to, že kreslí referenci, a ČÍM ji kreslí, rozhoduje appka. RRRelationItem.id je pro DS neprůhledný řetězec, který jen propustí ven. |
labels | RRLabels | — | — |
locale | string | — | BCP-47 locale pro formátování čísel v editoru (P476). Nechat prázdné je NORMÁLNÍ stav: default se bere z <html lang> (documentLocale), který appka drží v souladu s aktivním jazykem — díky tomu se oddělovače přepnou ve všech railech naráz a nové pole na to nemůže zapomenout. Prop je tu pro toho, kdo potřebuje jiné locale než zbytek dokumentu. |
collapsible | boolean | true | — |
defaultOpen | boolean | true | — |
children | ReactNode | — | obsah pod KV sekcí (nad oddělovačem relací) |
lead | ReactNode | — | Řádky NAD poli sekce (v témže .rr-rows, mimo „Zobrazit vše" — jsou vždy vidět). Pro blok, který skládá řádky z víc zdrojů (náhled obchodu: parametry procesu + pole osoby) pod jeden „Zobrazit vše". Kandidát DS (AD): zpětně kompatibilní, bez něj se nic nemění. |
leadAfter | number | 0 | Kolik viditelných polí stojí NAD lead (výchozí 0 = lead je první). Náhled obchodu: kontakt (Telefon, E-mail) nahoře, pod ním blok obchodu, pak zbytek polí pod „Zobrazit vše". Kandidát DS (AD). |
headless | boolean | false | Bez hlavičky sekce (titulek + chevron): blok nemá nadpis a nejde sbalit. Kandidát DS (AD). |
EditableField packages/ui/src/components/RecordRail.tsx
| Name | Type | Default | Description |
|---|---|---|---|
f * | RRField | — | — |
commit * | (key: string, value: string) => void | Promise<void> | — | commit jednoho pole; reject = hodnota se nezmění |
labels | RRLabels | — | — |
onCopy | (value: string, label: string) => void | — | — |
locale | string | documentLocale() | locale pro formátování čísla v poli — viz RecordRailProps.locale. |
className | string | 'v' | Třída hodnotového slotu — vnějšího <span>. Default v je slot řádku railu (.rr-row .v, record-rail.css). Konzument MIMO .rr-row si pojmenuje svůj vlastní slot: parametr rozbaleného procesu má .pl-v (process-list.css), který drží tytéž míry. ⚠️ Ať je to cokoli, musí to být position: relative — fajfka po uložení (.rr-saved) je uvnitř absolutně. |
useInlineEdit
No props extracted (not in props.json).
RailRows packages/ui/src/components/RecordRail.tsx
| Name | Type | Default | Description |
|---|---|---|---|
fields | RRField[] | — | Pole railu (jako RecordRail.fields); nebo místo nich children = <RailRow>. |
children | ReactNode | — | — |
onSave | (changes: Record<string, string>) => void | Promise<void> | — | Uložení jednoho pole; bez něj jsou řádky read-only. |
onCopy | (value: string, label: string) => void | — | — |
labels | RRLabels | — | — |
locale | string | — | — |
className | string | — | Třída na kořeni .rr-rows. |
childrenAfter | number | 0 | children se vloží za prvních N polí (výchozí 0 = před pole). |
wide | boolean | — | Sekce hlavního sloupce detailu: širší sloupec klíče (--rr-key-w 160 px, na telefonu 108 px), ať se delší klíče (podmínky smluv) neuřezávají. Rail sám je úzký a zůstává na 108 px. |
RailRow packages/ui/src/components/RecordRail.tsx
| Name | Type | Default | Description |
|---|---|---|---|
icon | string | — | — |
label * | ReactNode | — | — |
placeholder | string | '—' | — |
top | boolean | — | Víceřádková hodnota s klíčem nahoře (jako popisové pole railu). |
wrap | boolean | — | Hodnota se smí zalomit na víc řádků (místo zkrácení): řádek se zarovná nahoru a dostane svislý vnitřní okraj. Pro hodnoty typu „stará → nová“, popisy (AD 3. 10. 2026). |
actions | ReactNode | — | Akce řádku vpravo za hodnotou (.rr-acts) — stejný kontrakt jako tužka/copy railu: skryté, odkryjí se na hover/fokus řádku; na dotyku a ≤720 px trvale viditelné s terčem --tap-min, na telefonu pod hodnotou vpravo. Místo je rezervované, řádek při najetí neposkočí. |
children | ReactNode | — | — |
CSS classes
Files
packages/ui/src/components/RecordRail.tsxpackages/ui/src/lib/useInlineEdit.tsapps/web/src/ds/multi-value.cssapps/web/src/components/ChannelsField.tsxapps/web/src/components/multivalue/ChannelsPopoverEditor.tsxapps/web/src/components/multivalue/MultiValuePopover.tsxapps/web/src/ds/responsive.csspackages/ui/stories/record-rail-rows.stories.tsxapps/web/src/ds/record-rail.cssapps/web/src/ds.cssapps/web/src/ds/multi-value.cssapps/web/src/ds/responsive.cssapps/web/src/ds/detail-field.css