import "./record_facts.css"; import type * as React from "react"; import { useRender } from "@base-ui/react/use-render"; import { type FieldAnnotationProps } from "./field_annotations"; import { type DisplayFile } from "./file_thumbnail"; import { type RecordLabels } from "./locale"; import { type MeasureReading, type Stage } from "./shape_frame"; import { type StyleProps } from "./style_props"; /** THE RECORD'S FACTS — the fields no other section owns, as label/value pairs * read at a GLANCE, drawn with the cell the REGISTER draws them with. A GRID, * NOT A COLUMN OF ROWS: each fact is its LABEL OVER ITS VALUE, across as many * columns as the block's OWN container affords (`record_facts.css`, * `constitution.md` §10). */ /** WHAT SAVING THIS FACT DOES — and, by its PRESENCE, that the fact is editable * at all. May be async: the value echoes at once, saves in the background, and * on a failure reverts and states it AT the field (`useFieldCommit`). */ export type FactSave = (next: T) => void | Promise; /** A row a LINK fact may point at — the id it is saved as, and the name the * reader picks it by. */ export interface FactLink { id: string; name: string; } export type FactValue = { kind: "text"; text: string | null | undefined; onSave?: FactSave; } /** A `text (markdown)` field — the SOURCE, rendered. */ | { kind: "markdown"; text: string | null | undefined; onSave?: FactSave; } | { kind: "money"; amount: number | null | undefined; currency?: string; onSave?: FactSave; } | { kind: "number"; value: number | null | undefined; unit?: string; onSave?: FactSave; } /** ISO; `DateCell` formats it in the active locale. `due` makes it a * DEADLINE, exactly as the register's own `when` column does. */ | { kind: "date"; date: string | null | undefined; due?: boolean; onSave?: FactSave; } /** The chosen option, and — to EDIT it — the field's whole set, which a fact * otherwise never carries. */ | { kind: "stage"; stage: Stage | null | undefined; options?: readonly Stage[]; onSave?: FactSave; } /** A linked record. THE DOOR SURVIVES THE EDIT, but stops being the NAME: at * rest the name is the door, because a link's press is navigation * (`data_entry.md` § A field that saves itself), and an editable link's field * press is the PICKER. */ | ({ kind: "link"; name: string | null | undefined; onOpen?: () => void; } & ({ onSave?: undefined; id?: undefined; options?: undefined; onSearchChange?: undefined; } | { /** Point the link at another row, or `null` to leave it empty. */ onSave: FactSave; id: string | null; options: readonly FactLink[]; /** Narrow `options` from the caller's own side, debounced. */ onSearchChange?: (query: string) => void; })) | { kind: "contact"; text: string | null | undefined; onSave?: FactSave; } /** A boolean, in the app's OWN words — "Yes"/"No" is not a fact about a record. * The editor picks between those same two words. */ | { kind: "flag"; value: boolean | null | undefined; yes: string; no: string; onSave?: FactSave; } /** A multi-select's chosen options; `choices` is the field's whole set, which * editing needs and reading does not. */ | { kind: "set"; options: readonly Stage[]; choices?: readonly Stage[]; onSave?: FactSave; } /** The field's files — pictures as a strip of tiles, everything else named, * both opening the same viewer. */ | { kind: "files"; files: readonly DisplayFile[]; onAdd?: (picked: File[]) => void | Promise; onRemove?: (file: DisplayFile) => void; } /** A level read against the LIMIT it is judged by, and the side of that limit * (`alert`) that needs attention. `onSave` edits the LEVEL; the limit is the * field's. `reading` is which of the three a bound IS * ({@link MeasureReading}). `currency` makes BOTH figures money and `unit` is * what a level is counted in where it is not, so a fact states one of them. */ | { kind: "level"; value: number | null | undefined; limit: number; alert: "over" | "under"; reading?: MeasureReading; unit?: string; currency?: string; onSave?: FactSave; }; /** HOW MUCH OF THE READER'S FIRST GLANCE THIS FACT IS WORTH. `primary` leads its * band, `secondary` follows it in the same grid, `tertiary` is filed behind the * fold with the unstated fields; within a tier the plan's own order is kept. A * fact does not change rung, colour or device with its tier. */ export type FactTier = "primary" | "secondary" | "tertiary"; /** A fact, and whatever has to be said about it. A record's resident field that * must not be empty is an `error`, not a required marker; "required" belongs to * a `FormField` on a create draft. */ export interface Fact extends FieldAnnotationProps { label: string; value: FactValue; /** Default `primary`. {@link FactTier}. */ tier?: FactTier; } /** A BAND OF FACTS THAT ANSWER ONE QUESTION — one grid, under what the fields * have in common. A band whose facts ALL fold draws nothing. */ export interface FactGroup { /** What these facts are, titled over them at the named-group rung. */ caption?: string; facts: readonly Fact[]; } interface RecordFactsBase extends StyleProps { labels?: Partial; testID?: string; ref?: React.Ref; render?: useRender.RenderProp; } /** ONE RUN, in declared order — no caption to write. */ interface RecordFactsFlatProps extends RecordFactsBase { facts: readonly Fact[]; groups?: never; } /** BANDS — the first answering the screen's own question, each under what its * facts have in common. */ interface RecordFactsGroupedProps extends RecordFactsBase { groups: readonly FactGroup[]; facts?: never; } export type RecordFactsProps = RecordFactsFlatProps | RecordFactsGroupedProps; export declare function RecordFacts(props: RecordFactsProps): React.ReactElement>; export {};