Components › Overlays & dialogs › Dialog › Form-less dialog

Form-less dialog

canon

Creates or edits one record without form fields: an icon carries each row and the placeholder names it. Shape A writes text plus properties as chips in the body; shape B asks what · when · with whom · how · where.

Promoted to a rule by the product owner on 9 September 2026 ("promote form-less to a rule and review the other form dialogs with the aim of unifying them"); the pattern itself was approved by the AD on 7 September and shape B shipped to dev on 8 September (ACRM-132, MRs !3163–!3166). The code is the source of truth. THE RULE: form-less when the dialog creates or edits one record whose properties can be named by a placeholder; a form when it is data entry with many typed values and the label carries the meaning — the test is whether the label can be replaced by a placeholder without losing the field identity. Shape B (`.fl-*`) is the documented engine and the only one with a shared sheet. Shape A still runs as three private copies — `.tk-*` (task-dialog.css), `.dl-*` (deal-dialog.css), `.nd-*` + `.dlg-title` (note-dialog.css, dialog-doc.css) — with MetaChip declared twice in TSX; merging them onto `.fl-*` is wave 0 and until it lands shape A is a named shape, not a catalogued one. Adoption measured at main 06fb6b236: 2 dialogs on shape B, 3 on shape A, 43 forms — of which 12 are correctly forms (accounting document 13 fields, Spravovat segment 37, payroll relation, VAT period, mailbox) and 12 are conversion candidates with their questions listed in docs/REVIEW-form-dialogy.md. No stories yet — the pattern lives in apps/web, which packages/ui must not import, so they will be ported markup like Filter bar and Command palette. Three defects open in the shipped code (all in the review): the RSVP dot has no accessible name because aria-label sits on a role="generic" span (EntityPicker.tsx:191); --ic-amber 2.14:1 light and --ic-grey 2.97:1 dark fail the 3:1 non-text minimum while colour is the state’s only carrier; the availability day is mouse-only and role="presentation" (MeetingAvailability.tsx:233). A row may carry several values stacked in `.fl-vals` with the icon on the first: the video-call link sits under the meeting type rather than asking its own question (AD 9 September 2026), which also ended the `link` glyph naming two different rows; the in-person meeting type took `coffee` the same day, so `users` names only the attendees and no glyph in the dialog carries two meanings. The rule is one meaning per AXIS, not per app: the *Typy událostí* filter keeps `users` for its Schůzky row because it switches categories of event rather than describing one meeting (AD, 9 September 2026). The EDIT footer deliberately has no Zrušit — over an existing record the word reads as "cancel the meeting", and the close control already closes the window (product owner, 8 September 2026). The CREATE dialog still has it, because with nothing saved yet the word is unambiguous; whether create should drop it too is open. Delivered 13 Sep 2026 (FL-5; ACRM-223/224/226; acrm !3466–!3476): shape A in all of task, deal and note; header = primary record (EntityRef) then classification, separated by --sp-5 rhythm without a slash, classification and ID in --fg-muted (.head-muted); links chip names records instead of counting them and never repeats the primary; the title is a growing textarea. The note keeps its 1128 × 828 work window. Person dialog (shape B, on main since 15 September 2026, acrm !3561 — `apps/web/src/components/person/NewContactDialog.tsx`, channel editor draft mode in acrm !3548, wiring into the people list and palette in !3575): reuses the record rail's own multi-value channel editor in draft mode (`local-channels.ts`, `draft`/`plainValues`) for e-mail and phone, adds `.fl-dup`/`.fl-chan`/`.fl-chan-v`, and is the first shape-B dialog to use `masked-date-input` for a typed field. `.fl-dup`/`.fl-chan`/`.fl-chan-v` ship in `dialog-formless.css`. See the "person dialog" subsection in formless-dialog.usage.md. Person dialog, custom fields (acrm !3699, 16 Sep 2026, ACRM-277): behind the same "More details" chip, after the tag row, the create dialog draws the tenant's custom fields of a person from the SAME registry the record rail reads (`api.customFields('contact')`) — no drift, a deactivated definition is not drawn. The definition's type picks the input: date → dd.mm.rrrr mask, select and yes/no → PickerMenu (free text would put a value outside the code list into the jsonb, where no filter finds it), number → numeric keyboard but not type="number". Two items per row (like title prefix/suffix), leading glyph `clipboard-list` as in the rail. Values are written only after the record exists, via `setCustomValues` (the only path that merges jsonb; PATCH would overwrite keys from import, enrichment and the agent) and only the filled ones. Task dialog, suggestions from the typed title (acrm !3771, ACRM-213, approved 17 Sep 2026 — Linear "Apply all quick suggestions"): in a NEW task the existing `.flc-navrhy` group also offers what the title says — due date, priority, assignee only, only for an empty property (the default "Ty" assignee counts as empty), assignee only when the name fits exactly one colleague. Chip = canonical `.tk-chip` with the glyph of the property it sets (calendar · flag · user) and the sentence's own word ("zítra", "v pátek"); a quiet `IconButton` bolt (sm, 24px) BEFORE the chips applies all, drawn from two suggestions up. No new CSS. Computed in the browser (`lib/task-suggest.ts`), no model, no server call. ACRM-355 (acrm !3775): the note `.fl-note` is drawn by MentionInput (`.mi-editor.mi-multiline` = form-field frame, 88px); the form-less selectors carry `.mi-editor.mi-multiline.fl-note` so the note stays borderless at rest, on focus and while typing, whatever the CSS order. Relation dialog (shape B, acrm !4011, 27 Sep 2026 — `apps/web/src/components/relations/RelationDialog.tsx`, data/rules in `relations-data.ts`): the „New relation"/„Edit relation" dialog on a person or company card. The anchor (the card's own subject) is fixed as the `LeadEntity` in the header and is never repicked — a relation is an edge between two subjects, and `PATCH /relations/:id` does not accept `from`/`to` — so on edit the anchor never shows as an editable row. Icon+value rows carry no labels. The other side is a **generic entity** — company OR person, one `EntityPicker` (`types={['organization','contact']}`, `max={1}`) — because the product owner asked for the picker to stay general ("vztah můžu mít i s osobou", 27 Sep 2026) rather than splitting into a company dialog and a person dialog. Relation type is a `PickerMenu` grouped by the chosen entity's kind, with a `sep: true` separator row between the two groups; before an entity is picked, both groups show at once (the entity picked after the type discards a type that belongs to the other kind). "Valid from" is optional and typed into the `dd.mm.rrrr` mask (`MaskedDateInput`): the placeholder reads `field.validFromToday` while creating with nothing chosen (empty submits today) and `field.validFrom` on edit (empty keeps the stored date — clearing the field is never a silent delete of the start date). Position and share % rows draw only when the selected relation type's `allowsPosition`/`allowsShare` say so; a field outside the allowed set is always sent as `null` on submit, so switching type (e.g. „Společník" → „Zaměstnanec") clears it server-side instead of leaving a stale value. Footer: create = Cancel `Esc` + „Add relation" `⌘↵`; edit = „Save" only, no Cancel (the family rule — over an existing record `Zrušit` reads as "cancel the relation", and the close control already closes the window). Relationship type catalogue, person↔person (acrm !4010, migration `0416_relation_types_chot_prarodic.sql`, same 27 Sep 2026 change): „Manžel/ka" renamed to „Choť" (one word for both spouses), „Partner/ka" renamed to „Partner", and a new „Prarodič / Vnouče" (Grandparent/Grandchild) type was added after Parent/Child. The migration only rewrites rows still holding the old default name — an admin rename made in Nastavení → Data → Vztahy is left alone. Shape B — the add-to-list dialog (acrm !4019, 27 Sep 2026): `AddToListDialog` (`apps/web/src/components/segment/AddToListDialog.tsx`) is a multi-select-from-search instance of the pattern — "Multi-select from search in a dialog". The card's own record (company or person) is the `LeadEntity` anchor in the header, unpickable, same as the relation dialog's anchor. The body is ONE field, "Hledat nebo založit seznam" (search or create a list), filtering the tenant's manual lists (`segment kind='manual'`) by normalized, diacritic-insensitive match. Each list is a row with the anatomy of `check-menu` — `.acrm-popover-item` holding the list's own segment tile (`.sg`, 18px, reused from the left menu — see "Segment tile debt" below) then the name and member count, with `.acrm-checkbox` on the right (`aria-pressed` on the row) — and a click toggles membership immediately, so the footer carries only "Hotovo". A "Založit seznam „…"" row is appended only when nothing in the list matches the typed text exactly (no row for a name that already exists, so re-adding it is a toggle, not a duplicate). Enter is overloaded on the match count, not just an exact-match shortcut: with exactly one row still shown — even from a partial match like "fin" → "Fintech — sledovat" — Enter toggles that row; otherwise it creates the typed name (verified in `add-to-list-dialog.test.tsx`, case "Enter při jediné ČÁSTEČNÉ shodě list přepne"). Dynamic (filter) segments are excluded from this dialog on purpose — membership in them can't be set by hand, so an earlier version that listed them read-only was pure noise; the old `.atl-row`/`.atl-ic` row shape this dialog used before 27 Sep 2026 is now only used by the Trash page and the duplicates panel, not by any form-less dialog. "Change role" on the company card's person row (acrm !4020, 27 Sep 2026) now opens this same `RelationDialog` with `presetOther` set to the person — not a separate dialog: the older `PersonRoleDialog` component is REMOVED (`company/PersonRoleRelationDialog.tsx` is the new, thin wrapper that resolves the existing relation row, or passes `presetOther` when the person has no relation yet, and renders `RelationDialog`). `presetOther` (a `Mention`) fixes the picked-but-not-yet-saved other side of a NEW relation the same way `anchor` fixes the card's own side, so the entity picker does not show when the context already names both ends. Contact log dialog (shape A, `apps/web/src/components/timeline/ContactLogDialog.tsx`) — ONE dialog for „Zaznamenat kontakt“ on person, company, deal AND project (product owner 27 Sep 2026: „to je ten stejný dialog… použij ten samý prvek z DS“). Project as the main entity (acrm !4084, 28 Sep 2026): `entityType="project"` writes the 4th slot via `POST /timeline/entry` (`projectId`, from !4048) rather than a picked link; the old, project-only `ProjectEntryDialog` is REMOVED (279-line component + its test deleted, `ProjectMeetingsTab` now opens `ContactLogDialog`). Recording chip (acrm !4072, 28 Sep 2026, approved „doplň možnost přidání nahrávky všude“): a `MetaChip` (glyph `microphone`) driven by `PickerMenu` in the chip row — items „Z Plaudu“ (only when `plaudQ.data?.configured`) and „Nahrát soubor“, plus a clear row „Odebrat nahrávku“ once something is picked. The chip's VALUE is the SOURCE (`t('contactLog.recordingPlaud')` / „N z Plaudu“ / „Soubor“), never the file or dictaphone item's name — those run long and the chip must not truncate (source: `apps/web/src/components/meetings/recording-sources.ts`, `RECORDING_SOURCE_ICON`). A log with a recording is submitted as a proběhlá SCHŮZKA (only a meeting can hold a recording): `POST /timeline/entry` returns `meetingId`, then the binary/Plaud import goes as a second call; a failed second call closes the dialog like a success with a warning toast rather than reopening a half-filled form (review !4072). `.fl-note.fl-note-open`: the writing area is the dialog's CORE (REVIEW-form-dialogy.md §3.1) and must hold its three-line height even after focus leaves it for a chip — before this the area collapsed on every chip click and the dialog jumped 38 px (28 Sep 2026). Pitfall, already hit once: the fix must NOT be a bare `.open` class on `.fl-note` — `ds:sync-check` then reads `.x.open` elsewhere in the DS sheet as adopted-and-missing and broke the person-record golden; the CSS/TSX carry the dialog-specific `.fl-note-open` name instead. Header centring: `.acrm-dialog-lead .flc { align-items: center }` plus, in a row with more than the entity alone, `margin-top: -1px` on the wrapper around the entity's `.eref-chip` (its 1px dashed name underline extends the box 1px down, which without the correction put the entity's optical centre 2.8px above the classification chip and the × — product owner 28 Sep 2026: „položky v záhlaví nejsou zarovnané vertikálně na střed“). The single-entity header next to the title alone (ACRM-319) is untouched — the rule only fires with a second flex child. 14 Oct 2026 (owner "26 souhlas", proto/schuzka-ticho@19b777808, acrm !5041): Nová schůzka is the quiet reference — row placeholders are one or two words (Účastníci · Místo nebo odkaz · Cíl · Poznámka pro účastníky), a property of a row (internal goal) is an icon with tooltip, never a sentence in the placeholder. Dev check 14 Oct 1440 + 390: rows single-line (40 / 44 px). Registry repaired 17 Oct 2026 (review F2): removed classes with no CSS definition and no TSX use on main: .fl-copy, .tk-att-w, .tk-att-x. Registry repaired 17 Oct 2026 (review F4/F11): attached css share-dialog-formless.css.

Used in 29 files · 17 areas · 31× — components/communication, components/company, components/deal, components/meetings, components/notes, components/person …

Changelog since 28. 9. 2026