import "./state_matrix.css"; import type * as React from "react"; import { type ReactNode } from "react"; import { useRender } from "@base-ui/react/use-render"; import { type ColorName } from "./colors"; import { type StyleProps } from "./style_props"; /** The glyph's geometry. The legend draws the same one, so a cell and its * legend entry can never disagree. */ export type StateMatrixShape = "filled" | "hollow" | "dot" | "none"; export interface StateMatrixState { key: string; label: string; /** Palette family — the glyph and the legend swatch derive from it. */ color: ColorName; /** Default `filled`. `none` keeps the box and draws nothing — "nothing was * due here", as distinct from a state worth a glyph. */ shape?: StateMatrixShape; } export interface StateMatrixColumn { key: string; /** One or two characters — a day number, an hour. Shown ONCE in the header. */ label: string; /** A second header line — the weekday under the day number. */ caption?: string; /** The column the reader is standing in. Tinted down the grid, `aria-current`. */ current?: boolean; } export interface StateMatrixCell { /** Which state this cell is in, under `mark="glyph"`. */ state?: string; /** The number this cell holds — the DATUM under `mark="intensity"`, and a * count that replaces the glyph under `mark="glyph"`, when one cell stands * for several. */ value?: number; /** Only a pressable cell is a `button`; the rest announce as marks. */ pressable?: boolean; /** The cell the host has OPEN, announced as `aria-pressed` on a pressable * cell. */ selected?: boolean; /** What a screen reader says — the row, the column and the value, in words. */ name: string; } export interface StateMatrixRow { key: string; label: ReactNode; /** An identity mark before the label. */ leading?: ReactNode; /** A pinned figure beside the label — "9/14". */ total?: string; /** One per column, in column order — or, with no axis, the row's whole pile. */ cells: StateMatrixCell[]; } export interface StateMatrixLabels { /** Header of the pinned total column. */ total: string; /** The words at the two ends of the intensity scale. */ less: string; more: string; } interface StateMatrixBase extends StyleProps { rows: StateMatrixRow[]; /** * `dated` (the default) runs the columns across the top and pins each row's * name to the left — a month, a rota, a week of shifts. `none` is the * WALLBOARD: no axis at all, the cells wrapping as a dense field the reader * scans for the one that is red. */ axis?: "dated" | "none"; /** The axis across the top. Required by `dated`; ignored without one. */ columns?: StateMatrixColumn[]; onPressCell?: (rowKey: string, colKey: string) => void; /** Width of the pinned label block. Default 180. */ rowLabelWidth?: number; labels?: Partial; testID?: string; ref?: React.Ref; render?: useRender.RenderProp; } /** Each cell is in a STATE, drawn as that state's glyph. */ interface StateMatrixGlyphProps extends StateMatrixBase { mark?: "glyph"; states: StateMatrixState[]; /** The state the reader drilled into, from the legend. Cells in every other * state dim and stop being pressable — they are outside the slice. */ selectedState?: string | null; /** Press a legend entry to drill into its state (press it again to clear). * Omit for an informational legend. */ onSelectState?: (key: string | null) => void; color?: never; formatValue?: never; } /** Each cell holds a NUMBER, drawn as one hue at the value's intensity. */ interface StateMatrixIntensityProps extends StateMatrixBase { mark: "intensity"; /** The hue. Intensity is a mix within it; a zero cell goes neutral, so a gap * reads as quiet rather than as a little bit of data. Defaults to the blue * the sheet declares. */ color?: string; formatValue?: (n: number) => string; states?: never; selectedState?: never; onSelectState?: never; } export type StateMatrixProps = StateMatrixGlyphProps | StateMatrixIntensityProps; /** * A CATEGORICAL cross-tab: rows of subjects, a fixed axis of positions across * the top, and in every cell a STATE drawn as a glyph — an attendance sheet, * a fleet's month day by day, a rota. * * TWO AXES, and both are about what the cell MEANS. `axis` asks whether the * positions are named: `dated` draws the header and pins the row's name, `none` * is the wallboard. `mark` asks what the cell HOLDS: a `glyph` names one of the * `states`, `intensity` carries a number as one hue's density. * * It is not `Matrix`: a glyph is not a number, and a blank cell means "nothing * was due", never zero. It is not `Timetable`, whose positions are a pattern * with no dates and whose every mark is its own editor. * * ONE SCROLL CONTAINER, and both edges stick to it. The COLUMNS SHARE THE * SLACK — a cell's width is a floor, not a size. */ export declare function StateMatrix(props: StateMatrixProps): React.ReactElement>; export {};