import "./entry_grid.css"; import type * as React from "react"; import { type ReactNode } from "react"; import { useRender } from "@base-ui/react/use-render"; import { type StyleProps } from "./style_props"; /** One POSITION on the across axis — a period, or an option of a fixed set. */ export interface EntryGridPosition { key: string; /** What the column is, in the header — a day number, a month, an option's label. */ label: string; /** A second header line: the weekday under the day number, the year under the month. */ caption?: string; /** The position the reader is standing in. Tinted down the grid, `aria-current`. */ current?: boolean; } /** * WHAT ONE (subject, position) PAIR HOLDS — the three answers a fillable grid * can give, and they are three different facts. * * `absent` is the one a read-only cross-tab cannot say: NOTHING WAS DUE here. * A blank is not a zero — a driver with no shift on Sunday did not drive zero * kilometres — so the pair draws the kit's own absence mark and is never * counted, never edited and never a door. */ export type EntryGridCell = { kind: "absent"; name: string; content?: never; control?: never; onOpen?: never; selected?: never; } /** The datum, READ. `onOpen` makes the cell the door onto the record behind it. */ | { kind: "reading"; name: string; content: ReactNode; onOpen?: () => void; selected?: boolean; control?: never; } /** * THE DATUM'S OWN CONTROL — the cell IS the editor, seated at the grid's rung * with its chrome given up to the box. * * The caller CHOOSES the control (a number field, a select), because which one * a datum takes is the datum's own answer and not a grid's. What the grid owns * is the seat: full-bleed inside the cell, so a column of controls keeps the * rhythm a column of readings has and the reader's eye still runs down it. */ | { kind: "editor"; name: string; control: ReactNode; content?: never; onOpen?: never; selected?: never; }; export interface EntryGridRow { key: string; /** What the row is — the subject. */ label: ReactNode; /** An identity mark before the label. */ leading?: ReactNode; /** * WHAT THE ROW COMES TO, pinned beside its name rather than trailing the last * position. * * A row's own figure is the second thing the grid is read for, and a month of * columns is wider than any pane: trailing the axis it is the one number the * reader has to scroll to find. Pinned, it rides the name it belongs to — * which is where `StateMatrix` already puts it, so two cross-tabs do not * answer one question two ways. */ total?: ReactNode; /** One per position, in position order; `undefined` where the row is short. */ cells: readonly (EntryGridCell | undefined)[]; } export interface EntryGridLabels { /** The heading over the pinned row figures, and the foot's own name. */ total: string; } export interface EntryGridProps extends StyleProps { /** The subjects, down the side, in the order the caller sorted them. */ rows: readonly EntryGridRow[]; /** The positions, across the top. Few and FIXED — they share the width. */ positions: readonly EntryGridPosition[]; /** * THE FOOT — what each position comes to, and the corner where the two edges * meet. * * `cells` is one figure per position, in position order; `corner` is what the * whole grid comes to. Omitted, the grid closes on its last row: a total band * over a grid of states would be a sum of things that do not add up. */ foot?: { cells: readonly ReactNode[]; corner?: ReactNode; }; /** Width of the pinned label block. Default 180. */ rowLabelWidth?: number; labels?: Partial; testID?: string; ref?: React.Ref; render?: useRender.RenderProp; } /** * A CROSS-TAB A READER FILLS: subjects down the side, fixed positions across the * top, and in every cell the one value that pair holds — a timesheet, a * production plan, a month of readings taken per unit. * * It is not `Matrix` and not `StateMatrix`: both DRAW a cross-tab somebody else * computed, and a cell there is a number or a glyph the reader can at most * select. Here the cell is where the value is WRITTEN, so the grid seats the * caller's own control in the cell's box — and can say the one thing a computed * cross-tab never has to: that a pair holds nothing because nothing was DUE, * which is not a zero and must not be summed into one. * * ONE SCROLL CONTAINER, and both edges stick to it: the axis at the top, the * subject's name and its figure at the left. The COLUMNS SHARE THE SLACK — a * cell's width is a floor, not a size — so a fortnight across a wide pane fills * it and a month scrolls. */ export declare function EntryGrid(props: EntryGridProps): React.ReactElement>;