import "./shape_frame.css"; import type * as React from "react"; import { type ReactNode } from "react"; import { useRender } from "@base-ui/react/use-render"; import { type AvatarSize } from "./avatar"; import type { ColorName } from "./colors"; import { type ActionMenuItem } from "./action_menu"; import type { ConfirmRequest } from "./alert"; import { type DangerAction } from "./danger_section"; import { type IconName } from "./icon"; import { type RecordSummaryMetric } from "./record_summary"; import { type StyleProps } from "./style_props"; import { type TableColumn } from "./table"; import { type TotalsLineItem } from "./totals_line"; /** THE FRAME EVERY SCREEN SHAPE IS A CONFIGURATION OF — `[tabs] + list → record` * (`docs/templates.md`). The shapes name their slots by the field ROLE that * fills them; this module is the one anatomy under them, and it publishes the * CELLS so a hand-added column draws a value the way a slot does. */ /** A slot of a shape — the field the plan bound to it, and how it is read. */ export interface Slot { /** The column's heading — the field's own label. */ label: string; value: (row: T) => V | null | undefined; /** A device of the caller's for the value, in place of the shape's default. */ render?: (value: V, row: T) => ReactNode; } /** A SLOT WHOSE VALUE IS TEXT — and how many lines of it a cell may draw (one by * default). The number belongs to the FIELD rather than to the screen: whether * a clamp cuts the identifying words is decided by what the column holds. */ export type TextSlot = Slot & { lines?: number; }; /** A number the shape formats as money. */ export interface MoneySlot extends Slot { /** ISO 4217; the kit's default is VND. */ currency?: string; } /** A number the shape formats as a count or a measure, with its unit after it. */ export interface NumberSlot extends Slot { unit?: string; } /** WHAT A `measure` COUNTS IN — a unit, or a CURRENCY. A measure is money * whenever what it is judged by is money. Exclusive: stating the currency is * what makes the cell draw money. */ export type FigureUnit = { unit?: string; currency?: never; } | { currency: string; unit?: never; }; export type MeasureFigure = Slot & FigureUnit; /** * THE THREE READINGS A BOUND CAN BE, because a limit is not a denominator. * * `fill` (the default) — the bound is the WHOLE and the level a share of it; a * bound of zero is no bound. `threshold` — the bound is an ALARM the level must * stay one side of, so the FIGURE carries the reading and the bound is named * under it; zero is a real bound here. `countdown` — the level is a signed count * of DAYS (`daysUntil`'s own sign) in the kit's countdown words, with the bound * as the day-count from which it reads as danger, so `unit` names nothing. * * A reading is of TWO numbers, so an ABSENT bound draws none of it: a level is * then a FIGURE and nothing else, on every surface. The countdown is the * exception — the days ARE the reading and the limit only says where the ink * turns. */ export type MeasureReading = "fill" | "threshold" | "countdown"; /** The `measure` role — a level, the limit it is read against where the plan * named one, and WHICH READING that pair is. A union rather than two optional * fields because a limit nobody stated leaves a bare count, and a countdown is * the one reading a boundless measure can still state. */ export type MeasureSlot = MeasureFigure & ({ against: (row: T) => number | null | undefined; alert: "over" | "under"; reading?: MeasureReading; } | { against?: never; alert?: never; reading?: Extract; }); /** * WHICH READING A MEASURE DRAWS — the one the plan stated, else the SHARE; `null` * where no bound was named. NOT DERIVED FROM THE ALERT SIDE, which says which * way the alarm points and never what KIND of bound it is. One answer, read by * the cell, the column's width, its alignment and the record's own fact. */ export declare function measureReading(measure: { against?: unknown; alert?: "over" | "under"; reading?: MeasureReading; }): MeasureReading | null; /** A slot bound to a DATE — `time` where the field carries the HOUR, which the * cell then prints beside the day rather than dropping. */ export interface DateSlot extends Slot { time?: boolean; } /** A slot bound to a SELECT — the row answers with one option's key, and the * options carry the labels and the colours the field itself states. */ export type SelectSlot = Slot & { options: readonly Stage[]; }; /** One option of a select — a stage, a book, a category — in the field's order. */ export interface Stage { key: string; label: string; color?: ColorName; } /** A lifecycle's stage: the one option kind that can leave the flow. */ export interface LifecycleStage extends Stage { /** A stage the record LEAVES the flow by — done, cancelled. */ place?: "flow" | "outcome"; /** WHAT THE DOOR ANSWERS, for an outcome the record took — `fail` for a * refusal, `pass` for a settlement in favour. Absent, the door is drawn as * where the record STANDS and never as a completion: a tick would assert the * business's fact, not the kit's. */ verdict?: "pass" | "fail"; } /** A select field whose options are the strip: each row answers which one it is in. */ export interface ShapeTabs { /** The field's label — the strip's accessible name. */ label: string; options: readonly Stage[]; value: (row: T) => string | null | undefined; } /** The record behind a row — a drawer that steps ◀ ▶ over the band, the page the * app routes to, or what the row REVEALS in place, decided by what is given. */ export type ShapeRecord = { /** The drawer's title; the row's name by default. */ title?: (row: T) => string; /** IDENTITY QUALIFIERS UNDER THE NAME. The page's `subtitle`, off the same * derivation, drawn under the panel's own title because a subject and its * supporting line are ONE object. */ subtitle?: (row: T) => string | undefined; /** THE HEADLINE FIGURE — the number the row was OPENED for, drawn with * `RecordFigure` off the same derivation as the page (`recordHeader`). */ figure?: (row: T) => RecordSummaryMetric | undefined; /** THE RECORD'S OWN ACTS, at the top of the panel where `RecordPage` puts * them, so a record's primary act has ONE place whichever door opened. It * rides ABOVE the scrolling body rather than in the drawer's bar, which * is the overlay's chrome. */ actions?: (row: T) => ReactNode; /** THE RECORD'S DESTRUCTIVE ACTS, fenced LAST inside the body — each with * the `ConfirmRequest` it raises, as `RecordPage` takes them, so the * standard path cannot ship an unconfirmed delete. */ danger?: (row: T) => RecordDanger | undefined; /** The drawer's body. */ render: (row: T) => ReactNode; onOpen?: never; expand?: never; } | { /** Where the row goes — the app's route. */ onOpen: (row: T) => void; title?: never; subtitle?: never; figure?: never; render?: never; actions?: never; danger?: never; expand?: never; } | { /** WHAT THE ROW HOLDS, REVEALED UNDER IT — for rows with no record worth a * door. ONE row is open at a time, and Escape closes it. */ expand: (row: T) => ReactNode; title?: never; subtitle?: never; figure?: never; render?: never; actions?: never; danger?: never; onOpen?: never; }; /** A RECORD'S DESTRUCTIVE ACTS AND WHAT GOES WITH IT — the one declaration both * of a record's doors take. */ export interface RecordDanger { /** ONE sentence naming the consequence — what goes with the record. */ description?: string; actions: readonly [DangerAction, ...DangerAction[]]; } /** A value the reader arranged, held by the app so it can survive a reload (`usePersistedState`). */ export interface Arranged { value: V; onValueChange: (value: V) => void; } export interface ShapeLabels { search: string; all: string; /** The grouping control's own word — its placeholder while nothing groups. */ groupBy: string; empty: string; noMatch: string; emptyBand: string; /** A DAY OF THE RUN NOBODY FILED AGAINST, said on the day's own line: a line * carrying a date and nothing else is indistinguishable from one whose read * failed. */ emptyDay: string; /** "3 of 12" — a drawer's position in the band, and an unnamed row's name. */ position: (index: number, total: number) => string; /** "2 of 5 attached" — an expected set's meter. */ attached: (present: number, required: number) => string; /** The gap below a minimum. */ shortfall: string; /** The gap above a capacity. */ excess: string; /** A level read against its limit — "120 of 80". */ level: (level: string, limit: string) => string; /** A READING ON THE SIDE OF ITS LIMIT THAT NEEDS ACTING ON, NAMED — what a band * counting those rows calls the set. Two labels and a WORD, ordered by the * pack: a symbol between them is punctuation doing a word's job. * {@link ShapeLabels.shortfall} / {@link ShapeLabels.excess} names the GAP. */ under: (measure: string, limit: string) => string; over: (measure: string, limit: string) => string; /** The bound a level is judged by, named beside the figure — for a surface * that gave up the meter and has no track left to read the limit off. */ limit: string; /** The FLOOR a threshold reading must stay above, named under the figure — * "min 5%". A floor is not a denominator, so there is no track to read it * off and the words are the only place it can be said. */ floor: (limit: string) => string; /** The CEILING it must stay under — "max 30 days". */ ceiling: (limit: string) => string; /** A READING THAT STANDS AT THE END OF THE WINDOW rather than adding up over * it. The band draws stocks and sums side by side, and without the word a * figure that is not the sum of what is on screen reads as one that does not * add up. */ closing: (figure: string) => string; /** THE OBLIGATION DESK'S FOUR COLUMNS, which no field can name: its rows are * fanned out of SEVERAL date fields, so the obligation's name is the cell's * VALUE here. */ obligation: string; /** How much of the runway has gone. */ elapsed: string; /** The date it falls due. */ due: string; /** How long is left — the countdown's own column. */ remaining: string; previousPeriod: string; /** What a headline says INSTEAD of naming the previous period, when that * period holds no rows: a comparator with no figure after it compares this * period against nothing. */ noPreviousPeriod: string; partialBucket: string; /** A read the server's cap CUT — the rows are part of a longer set, so every * figure folded from them is short and nothing on the surface would say so. */ partialSet: string; /** A composition drawn over a measure that carries both signs — the slices are * gross, the total above them is net, and the two do not add up. */ grossMovement: string; rows: string; /** The act that asks for the next page of a keyset read. */ loadMore: string; /** How much of a longer set is on screen — "250 rows loaded". The figure * arrives already in the reader's own grouping. */ loadedRows: (count: string) => string; /** A summed column's line where the register has STACKED. Side by side the sum * stands under the column it sums; in the pile there is no column, so without * its own word the line reads as one more row. */ totalOf: (label: string | undefined) => string; /** WHEN A LIVE BOARD IS SHOWING — the word that stands where the other * dashboard shape puts its period control. The kit's, because no field can * name it; a screen wanting the operation's word passes `labels`. */ now: string; /** THE TWO WORDS A RECONCILIATION DESK OWNS, which no field can name: the * difference column is ARITHMETIC over the two figures beside it, and the * band under it is the part nobody has accounted for. */ difference: string; unexplained: string; /** A STRETCH OF A LANE NOTHING IS ON, and how long it runs. Two phrases rather * than one with a unit passed in, because "free" lands on a different side of * the figure by language. */ freeHours: (hours: number) => string; freeDays: (days: number) => string; } export interface ShapeBaseProps extends StyleProps { rows: readonly T[]; rowKey: (row: T) => string; /** Rows are being read; the register is a placeholder until they land. */ loading?: boolean; /** HOW MANY ROWS ARE COMING — a count the surface knows before the rows exist. * The waiting register draws that many instead of {@link SKELETON_ROWS}, and * a register with none in yet WAITS rather than standing empty. A count of * NONE leaves the empty state alone. Bounded by {@link waitingRows}. */ expected?: number; /** The read failed — a failed read is not an empty register. */ error?: { message: string; onRetry?: () => void; }; /** THE REGISTER HOLDS ONE PAGE OF A LONGER SET, and how to ask for the next — * the app SDK's keyset read, which APPENDS. The register then closes with the * loaded count, **and every summed figure stands down** * ({@link ShapeFrameProps.totals}, {@link ShapeFrameProps.above}, * {@link ShapeColumn.total}): a sum over the first page is drawn exactly like * a sum over the set. */ more?: { hasMore: boolean; loading?: boolean; onLoadMore: () => void; }; record?: ShapeRecord; /** The text a row is found by; given, the toolbar carries a search field. */ search?: (row: T) => string; /** The strip's selected option, held by the app; absent, the frame holds it. */ tab?: Arranged; /** The search text, held by the app; absent, the frame holds it. */ query?: Arranged; /** `FilterChip`s for the toolbar. */ filters?: ReactNode; /** The toolbar's one act, at its right edge — the New CTA. */ action?: ReactNode; /** THE ROWS IN VIEW, SAVED — the export verb, handed the rows the strip and * the search KEPT, because a caller reading its own array back would save the * rows the reader filtered away. Secondary, left of the CTA. */ saved?: (visible: readonly T[]) => ReactNode; /** ROWS IN, FROM A FILE — the import verb. A NODE rather than a function of * the visible rows: the rows an import is about do not exist yet. Leftmost of * the three. */ taken?: ReactNode; /** * The acts over a ticked SET. The checkbox sits outside the record's door, so * ticking a row never opens it. ONE SCREEN, ONE CLAIM: the selection is over * the rows IN VIEW, so another band or another search drops it. */ selection?: { /** The benign bulk acts. OPTIONAL: a register whose only bulk verb is the * delete states `danger` and nothing else. */ actions?: (rows: readonly T[]) => ReactNode; /** THE DESTRUCTIVE BULK ACT, confirming by construction. The rows are handed * over rather than re-derived, so the question and the act cannot run over * different sets. */ danger?: { label: string; confirm: (rows: readonly T[]) => ConfirmRequest; }; }; /** THE ROW'S OWN ACTS — the ⋯ menu in the trailing gutter. `composition.md` * §"Row actions are always-visible siblings". A destructive item carries * `danger: true` and raises `Alert.confirm` from its own handler. The gutter * is reserved the moment ANY row can act. */ rowActions?: (row: T) => readonly ActionMenuItem[]; /** THE ONE VERB DRAWN ON THE ROW'S OWN SURFACE, before its ⋯. ONE, and the * rest in `rowActions`: the trailing gutter is paid for by every row. */ rowAction?: (row: T) => ReactNode; labels?: Partial; testID?: string; ref?: React.Ref; render?: useRender.RenderProp; } /** A register column and the cell it draws. */ export interface ShapeColumn extends TableColumn { cell: (row: T) => ReactNode; /** DRAWN ONLY OVER THE WHOLE SET — the column whose answer the STRIP gives * once a band is selected, where repeating it down every row is noise. */ whole?: boolean; /** WHAT THIS COLUMN ADDS UP TO over the rows in view — the register's last row, * in this column, because a derived result has to END the expression it * derives from. A whole-view aggregate stays {@link ShapeFrameProps.totals}; * a register never makes both. */ total?: (rows: readonly T[]) => ReactNode; } /** Every strip leads with ALL and opens on it — the reader is shown the whole * set first and narrows from there. What the order decides is how the bands * after it are DRAWN. */ export type StripSpec = /** A pipeline strip keeps the stages' ORDER, with ALL standing outside the walk. */ { order: "sequence"; tabs: Omit, "options"> & { options: readonly LifecycleStage[]; }; } /** A free strip is a flat set of destinations. */ | { order: "free"; tabs: ShapeTabs; }; /** ONE FIGURE THE REGISTER ADDS UP — the field, and what its sum is called. The * rows it folds are the frame's (the strip and the search applied), never the * caller's own subset: a band over a set the register is not showing is a * second claim on one screen. */ export interface ShapeSum { label: string; /** What this row contributes to the sum. */ value: (row: T) => number | null | undefined; /** ISO 4217 — the sum is money, and prints as money. */ currency?: string; /** What the sum is counted in, where it is not money. */ unit?: string; /** A STOCK RATHER THAN A FLOW — when this row's reading was taken, as a key * that sorts (an ISO date). The band then states the LAST reading in the * window instead of adding the readings up. A row whose reading is undated is * not a candidate — it stands at no moment. */ at?: (row: T) => string | null | undefined; } /** * WHAT THE ROWS IN VIEW COUNT AND ADD UP TO — the register's band, as data. * Counts and sums, because those are the two statements a register can make * about its own rows that no single row makes. */ export interface ShapeFigures { /** COUNT THE ROWS IN VIEW, under the kit's own word for them. */ rows?: boolean; /** THE ROWS THAT NEED ACTING ON, counted and named by the screen. ONE such set. * Drawn in the attention ink while it is not zero, which makes a PROMISE * (`TotalsLine`): the reader must be able to reach those rows from the chrome * above. */ attention?: { label: string; when: (row: T) => boolean; }; /** Labelled sums over the rows in view. */ sums?: readonly ShapeSum[]; /** WHAT IS OWED, AGED BY HOW LATE IT IS — the receivables ladder, as the kit's * own bands (`ageingBands`). DATA rather than a chart the screen composes: the * words, the order and the ramp are a contract. WHAT IS NOT YET DUE IS A BAND, * not an exclusion, or the composition is full at every input; a row the plan * cannot date has no age and is not in the ladder. */ ageing?: { /** What each bucket adds up — what is still owed on the row. */ amount: (row: T) => number | null | undefined; /** The day the row was owed by (ISO date); a row is bucketed by how far past it is. */ due: (row: T) => string | null | undefined; /** Bucket edges in days past due, ascending; absent, the kit's default. */ buckets?: readonly number[]; currency?: string; }; } /** THE RUNS A LIST FALLS INTO — one subhead per key, the rows under it. The key * is what {@link order} sorts, as TEXT, so a key that has to order is an ISO * date or a zero-padded code. IT IS NOT A SECOND COLUMN: group by a value and * the column for it goes. */ export interface ShapeGroup { /** What this row has in common with the rest of its run. */ by: (row: T) => string; label: (key: string) => string; /** Sort the KEYS as text. Unstated, the runs keep the order their rows * arrived in — which is the order the caller already sorted them into, and * the one order the frame cannot improve on. */ order?: "asc" | "desc"; } /** One grouping the band offers, named for the reader. */ export interface ShapeGroupOption { key: string; label: string; group: ShapeGroup; } /** ONE RUN OF THE BODY. The frame folds the runs before anything reads a * position, so a device draws the same sequence the ordinal, the ◀ ▶ and the * selection are numbered against. */ export interface ShapeBodyRun { key: string; label: string; rows: readonly T[]; } /** * THE ROWS, AND EVERYTHING A DEVICE NEEDS TO DRAW ONE. A LAYOUT IS NOT A SECOND * FRAME: everything the device would otherwise re-derive is handed to it. */ export interface ShapeBody { /** The rows the strip and the search kept, in the order they are drawn. */ rows: readonly T[]; /** The runs the reader folded them into, or nothing while the register is flat. */ runs: readonly ShapeBodyRun[] | null; rowKey: (row: T) => string; /** What the row is called — its own name, or its place where it has none. */ identity: (row: T) => string; /** Open the row's record; nothing where the screen has no door. */ open: ((row: T) => void) | undefined; /** The row's own verb and its ⋯, exactly as the register's gutter draws them. */ acts: (row: T) => ReactNode; /** The row's tick; nothing where the screen takes no selection over its rows. */ tick: ((row: T) => { checked: boolean; onChange: (on: boolean) => void; }) | undefined; } /** WHAT THE SHAPE'S OWN BAND IS DRAWN OVER — the rows in view, and the door onto * one of them, because a band above the rows can BE a reading of them and * opening one has to be the same door the row below opens. */ export interface ShapeHead { /** The rows the strip and the search kept, in the order the register draws them. */ visible: readonly T[]; /** Whether a row is in view — for a fold the head makes over a set of its own. */ keep: (row: T) => boolean; /** Open a row's record — the register's own door, whichever kind it is. */ open: (row: T) => void; /** The row the door stands on, by its key; `null` while nothing is open. */ openKey: string | null; } export interface ShapeFrameProps extends ShapeBaseProps { /** The strip, when the shape or the screen names a select for it. */ strip?: StripSpec; /** The register's own columns. One of these and {@link ShapeFrameProps.body}, * never both and never neither. */ columns?: readonly ShapeColumn[]; /** THE DEVICE THE KEPT ROWS ARE DRAWN WITH, in place of the register's table. * The frame's own prop rather than something a shape composes beside itself, * because everything AROUND the rows is the frame's. There is nothing to page * a skeleton into, so a body WAITS as one state. */ body?: (view: ShapeBody) => ReactNode; /** DRAW THE ROWS AS RUNS, under a subhead per key. ONE FRAME, so the column * grid, the fit budget and a column's own total stay the register's: a width * that cannot seat a column sheds it for the whole list. The subheads are * {@link TableGroup} bands, inert here. */ group?: ShapeGroup; /** EACH RUN CLOSES WITH ITS OWN SUBTOTAL ROW — the columns the register already * sums ({@link ShapeColumn.total}), over that run's rows alone. STATED rather * than assumed, because a run is not always a part of one answer. It draws * only where a grouping is in force AND a column states a total; a partial * read stands both down. */ subtotals?: boolean; /** THE GROUPINGS THE READER MAY CHOOSE — a "group by" select, ungrouped first, * because a grouping is a view the reader arranges. A `group` prop is the one * grouping the screen states with no choice; give one or the other. */ groups?: readonly ShapeGroupOption[]; /** The row's name — the drawer's title and the row's accessible name; empty where the row has none. */ identity: (row: T) => string; /** WHICH ROWS ARE A RECORD — all of them unless the shape says otherwise. A * shape whose register is COMPLETE draws rows that stand for nothing stored, * and a door or a tick on one is withheld: the row draws inert, and the pager * walks the same set. */ opens?: (row: T) => boolean; /** The glyph a register with NOTHING IN IT stands on — the shape's own * subject, because one inbox glyph over every shape says only "a list". */ emptyIcon: IconName; /** FIGURES OVER THE REGISTER — what the rows in view count and add up to, as * DATA and drawn by the frame: the light inline band under the toolbar * (`TotalsLine`'s device). Not a node, so a shape cannot draw a dashboard over * its own rows. THE DERIVED BANDS ARE EVERY REGISTER'S: each shape re-exposes * this and {@link ShapeFrameProps.totals} by a `Pick` of what it CAN draw. */ above?: ShapeFigures; /** THE SHAPE'S OWN ANALYSIS, over its chrome. The SHAPE's, never a screen's * band of figures: those are {@link ShapeFrameProps.above}, which is data. It * stands whatever the register holds, because the control that changes the * period has to survive a period with no rows in it. */ head?: (band: ShapeHead) => ReactNode; /** What the whole VIEW adds up to, for an aggregate that BELONGS TO NO COLUMN. * A figure that sums ONE column is {@link ShapeColumn.total}: a band under the * register starts at its left edge, not a money column's. Never both. */ totals?: (visible: readonly T[]) => TotalsLineItem[]; } /** THE ROW'S SUBJECT AS A MARK. The kind is `MarkStack`'s own, so a row and the * record it opens state the subject ONCE. A `thing` is never initials. */ export interface IdentityMark { kind: "person" | "group" | "thing"; /** Who or what this is, for anyone listening. */ name: string; image: string | null; } /** THE RUNG A ROW'S MARK IS DRAWN AT — the height of the TEXT BLOCK it * identifies, never the row's: two tight lines is `AVATAR_PX.lg`, a lone * untightened line `AVATAR_PX.sm`, and `AVATAR_PX.md` belongs on no register * row. A THING floors at the larger rung whatever its lines, because its * picture is the row's CONTENT. At `sm` the initials drop to ONE letter * (`avatarInitials`). */ export declare function identityRung(kind: IdentityMark["kind"], hasCaption: boolean): AvatarSize; /** THE GUTTER A SUBJECT COLUMN BUDGETS for its mark — the rung above plus that * gap. ONE STATEMENT, because three places read it: the cell, the column's * `lead`, and the square the waiting register draws. */ export declare function identityLead(kind: IdentityMark["kind"], hasCaption: boolean): number; /** IS THERE A MARK TO SPEND THE GUTTER ON — decided from the ROWS IN HAND, never * from the binding: a bound `mark` is a QUESTION, and a catalogue whose picture * field is empty on every row would spend the gutter on one fallback glyph. A * PERSON or a GROUP always has one, their initials; a THING has none * (`mark_stack.tsx`). The rows the REGISTER was handed, not the band in view: a * gutter that came and went as the reader typed would move every name. */ export declare function drawsMark(kind: IdentityMark["kind"], rows: readonly T[], mark: ((row: T) => string | null | undefined) | undefined): boolean; /** * THE ROWS A PROMISED REGISTER WAITS AS — `null` where nothing was promised. * `expected` is DATA and arrives at any size, so it is capped at twice * {@link SKELETON_ROWS}; under that the count is exact. */ export declare function waitingRows(expected: number | undefined): number | null; /** * HOW WIDE A BAR IS — a share of the column the value will land in, the * supporting line the shorter of the two. A FLEXIBLE column has no width of its * own until the register is laid out, so the share is taken against * `FLEX_MIN_WIDTH` (`table_fit.ts`) and the bar stays a line at every width. */ export declare const BAR_FILL: { readonly value: 0.72; readonly caption: 0.44; }; /** A MARK IN A CELL — `Progress`'s own control-row diameter, and the same number * for the glyph a verdict draws, so a ring and a tick in neighbouring columns * sit on one line. */ export declare const CELL_MARK = 16; export declare function useShapeLabels(labels: Partial | undefined): ShapeLabels; export declare function ShapeFrame(props: ShapeFrameProps): React.ReactElement>; /** * THE BAND'S ITEMS, FOLDED FROM THE ROWS IN VIEW. Every figure is read off * `visible`, the rows the strip and the search kept: one screen, one claim. */ export declare function figureItems(figures: ShapeFigures, visible: readonly T[], words: ShapeLabels, tag: string): TotalsLineItem[]; /** * THE SUBJECT CELL — the name, its supporting line, and the row's mark. The mark * is a DESCRIPTOR rather than a node, because its rung is not the caller's to * pick: it follows the text block this cell draws (`identityRung`). * * `MarkStack` with one mark is the device the record header draws its subject * with. Decorative: the name is the next thing on the line. */ export declare function IdentityCell({ title, caption, mark, lines: allowed }: { title: string; caption?: string; mark?: IdentityMark; /** * HOW MANY LINES THIS SUBJECT MAY TAKE in a column — one by default. Two where * the identifying words are at the END of the name and a clamp cuts exactly * them: a company's legal form leads. A CEILING, never a reservation. */ lines?: number; }): React.JSX.Element; /** * THE TWO FIGURES AS STRINGS — ONE VALUE, ONE RENDERING. A record's EDITABLE * fact draws its resting figure inside an inline editor rather than a cell, so * it has to format the value itself and would otherwise be a second definition. */ export declare function formatMoneyValue(value: number, currency: string | undefined, locale: string): string; /** A count, carrying the field's unit where it has one — joined by the kit's one * rule, which binds a percent sign to its number (`format_unit.ts`). */ export declare function formatCountValue(value: number, unit: string | undefined, locale: string): string; export declare function MoneyCell({ value, currency, color }: { value: number; currency?: string; color?: "muted" | "danger" | "warning"; }): React.JSX.Element; export declare function NumberCell({ value, unit, color }: { value: number; unit?: string; color?: "muted" | "danger" | "warning"; }): React.JSX.Element; /** * A DATE, and — where the shape says the date is a DEADLINE — how it stands * against today. * * PASSING `due` AT ALL makes this a deadline column, and its VALUE says whether * this row is still working to the date. The count is the kit's countdown * vocabulary (`deadline.ts`). A row that is NOT being judged draws the date * ALONE, on one line. * * A date NOBODY RECORDED says so, in the one mark the record's own facts use for * a missing value: a blank cell is indistinguishable from a failed read. * * `time` where the field carries the HOUR: a `datetime` printed date-only is a * different fact from the one stored. */ export declare function DateCell({ value, due, time }: { value: string | null | undefined; due?: boolean; time?: boolean; }): React.JSX.Element; /** * WHAT A DATE COLUMN IS WIDE — the string it draws, MEASURED * ({@link textWidthSm}), never a chosen number. The DATE's own width, so a * column that draws a second LINE under it states that line's own floor. A * longer heading is already answered: `computeTableFit` floors every column at * its own heading. */ export declare function dateColumnWidth(time?: boolean): number; /** * WHAT A TEXT COLUMN'S OWN ROWS DRAW — the `content` the fit budgets a flexible * column at (`TableColumn.content`), or `undefined` where the cell is the * caller's own node. The same reading the meter column is sized by * ({@link measureColumnWidth}) — read the ROWS, never choose a magnitude. * * BOTH LINES of a stacked subject, because the caption is regularly the longer * one. */ export declare function textColumnInk(rows: readonly T[], line: Pick, "value" | "render">, caption?: (row: T) => string | null | undefined): number | undefined; /** THE COMPACT METER'S FIXED HALF — the floor its track may not shrink below and * the gap before the figure, both declared in `progress.css` and held to this * number by `text_advance.test.ts`. */ export declare const COMPACT_METER_CHROME: number; /** WHAT A MEASURE COLUMN IS WIDE for a COUNT — the meter's chrome plus the `9ch` * figure slot `progress.css` reserves, and room for a unit after it. */ export declare const MEASURE_COLUMN = 176; /** THE KIT'S COUNTDOWN SENTENCE, as a column — the ONE answer, because two * surfaces print it. A locale whose countdown is longer clamps it rather than * widening the column. */ export declare const COUNTDOWN_LINE = 104; /** * WHAT A MEASURE COLUMN IS WIDE — the count's own column, and for MONEY the * meter beside the widest amount the rows actually print, read from the ROWS: a * chosen magnitude wide enough for đồng over-reserves everywhere else. * * Never narrower than the count's column, and never derived for a slot with its * own `render`. */ export declare function measureColumnWidth(slot: MeasureSlot, rows: readonly T[], locale: string): number; /** * TEXT, AS A REGISTER READS IT — one line, because the register's rhythm is what * a column of rows is scanned down. `grow` where there is no rhythm to keep: a * RECORD's read-only fact (`data_entry.md` §"A NOTE IS ALWAYS MULTI-LINE"), * whose editable spelling wraps (`TextInput autoGrow`). */ export declare function TextCell({ value, grow, lines }: { value: string; grow?: boolean; lines?: number; }): React.JSX.Element; /** One reachable contact, whole: the value and the control that hands it over. */ export declare function ContactCell({ value, copyLabel }: { value: string; copyLabel: string; }): React.JSX.Element; /** A stage a row is in, in the option's own colour. */ export declare function StageCell({ stage }: { stage: Stage; }): React.JSX.Element; /** * AN ANSWER THAT CAN BE NO — the row's verdict, drawn as the mark the kit draws * a verdict with everywhere else (`ChecklistItem.verdict`). * * A BOOLEAN COLUMN NEVER PRINTS ITS OWN LABEL: the field's name is the column's * heading, and it is what the mark ANNOUNCES — "Reconciled: no" is the whole * reading. * * `fails` says which answer needs attention. Only that side is toned, and the * two sides differ by GLYPH, so the reading never rests on colour. A row that * answers NEITHER draws nothing. */ export declare function VerdictCell({ label, value, fails, word }: { label: string; value: boolean; fails?: boolean; /** * THE ANSWER IN THE FIELD'S OWN WORDS, for a surface with room for it — a * record's fact, where there is no column heading to carry the noun. Given, * the WORD is the reading and the glyph stops announcing. */ word?: string; }): React.JSX.Element; /** WHAT A VERDICT COLUMN IS WIDE — the mark and the cell's own gutters. The * heading decides from here up: `computeTableFit` floors every fixed column at * its own label. */ export declare const VERDICT_COLUMN = 48; /** * A required set against what the row has — absence is the information. A RING * rather than a bar: the question is "is this complete", a proportion read at a * glance. The fraction stands beside it in words, the same words the meter * announces, which is why the text itself is silent. * * `label` names WHICH set, for a column standing among other columns. */ export declare function ExpectedSetCell({ label, present, required, words }: { label?: string; present: number; required: number; words: ShapeLabels; }): React.JSX.Element; /** * A LEVEL AND THE BOUND IT IS READ AGAINST, in the reading that bound IS * ({@link MeasureReading}) — the meter for a share of a whole, the figure under * its alarm for a threshold, the day-count in words for a countdown. * * `currency` makes the figure MONEY: a meter counting it bare says đồng the way * it would say pallets. */ export declare function LevelCell({ label, level, limit, alert, reading, unit, currency, words }: { label: string; level: number; limit: number | null; alert?: "over" | "under"; /** Which reading this is; unstated, the one the bound and its alert side imply * ({@link measureReading}) — so a cell, a column's width and the record's own * fact cannot resolve it three ways. */ reading?: MeasureReading; unit?: string; currency?: string; words: ShapeLabels; }): React.JSX.Element; /** A SELECT SLOT'S CELL — the option the row is in, in the option's own colour, * and nothing where the row answers with a key the field no longer has. The * colour is the FIELD's: it is what makes a select scannable down a column. */ export declare function selectCell(slot: SelectSlot, row: T): ReactNode; /** A MEASURE SLOT'S CELL — the meter where the plan named a limit, the bare count * where it did not, the slot's own `render` over either. Stated here, so each * shape carrying the slot does not re-decide which device answers. */ export declare function measureCell(slot: MeasureSlot, row: T, words: ShapeLabels): ReactNode; /** WHERE A MEASURE COLUMN'S HEADING SITS — over the figures for a reading that IS * a figure, at the column's left edge for a meter, which fills the column * (`progress.css`) and shrinks to its floor in a right-aligned cell. */ export declare function measureAlign(slot: MeasureSlot): "right" | undefined; /** How far a level is on the side of its limit that needs attention; zero or less is the safe side. */ export declare function shortfall(level: number, limit: number, alert: "over" | "under"): number;