Functions
Standalone helpers and factories.
applyGridState
Restore a (partial) view-state snapshot onto the grid (X21). Entries that reference a column which no longer exists are dropped, so a snapshot survives a column-set change without wedging the grid. Pass include / exclude to restore a subset. Silently ignores an absent snapshot.
function applyGridState<TData extends RowData>(table: BstTableInstance<TData>, state: Partial<BstGridState> | null | undefined, select?: BstGridStateSelect): void;
applySettingsOverrides
Apply user overrides onto a table-options object. Only the registry keys are touched; everything else passes through untouched. Object-valued flags (pagination, enableEditing, …) keep their options when toggled back on and become false when toggled off.
function applySettingsOverrides<P extends object>(props: P, overrides: BstSettingsOverrides | undefined): P;
arrayEqual
Shallow array equality — handy for selecting FieldError[] / id lists.
function arrayEqual<T>(a: readonly T[], b: readonly T[]): boolean;
autoGenerateColumns
Infer columns from row data (X27) — one column per key found across a sample of rows (first-seen order), with a humanized header and a guessed cell type. Returns [] for empty / non-object data (the grid then renders no columns).
function autoGenerateColumns<TData extends RowData>(data: readonly TData[], opts?: AutoColumnsOptions): BstTableColumn<TData>[];
BstConditionalFormatBuilder
Neutral, theme-agnostic conditional-format builder (K3) — the styling cousin of <BstFilterBuilder>. Controlled: it reads rules and calls onChange with the next array. Each row is scope · column · operator · value · style. Feed the result to useBstTable({ conditionalFormats }). Styled purely with bst-* classes, so both skins share it.
function BstConditionalFormatBuilder<TData = any>({ rules, onChange, columns, presets, className, icons, }: {
rules: BstFormatRule<TData>[];
onChange: (rules: BstFormatRule<TData>[]) => void;
columns: BstFormatBuilderColumn[];
presets?: BstFormatPreset[];
className?: string;
/** Body-icon overrides (uses the `remove` slot). */
icons?: BstIconOverrides;
}): React.JSX.Element;
BstFilePreview
Dependency-free file preview overlay (B5/I3). Renders the clicked document full-screen: images inline, PDFs in the browser's native viewer (<iframe> — no pdf.js), anything else as an open/download fallback. Closes on Escape or a backdrop click. Exported so adapters can reuse it from their file editors. A file needs a url to preview.
function BstFilePreview({ file, onClose, }: {
file: {
name?: string;
url?: string;
thumbnailUrl?: string;
contentType?: string;
};
onClose: () => void;
}): React.JSX.Element;
BstFilterBuilder
Neutral, theme-agnostic filter-builder UI (E3). Reads/writes v9's columnFilters state (one condition per column) using the operator-aware bstCondition filterFn. Rendered by adapters inside their own popover/panel; styled purely with bst-* classes + CSS vars, so both skins share it.
function BstFilterBuilder({ table, className, icons, }: {
table: any;
className?: string;
/** Body-icon overrides (uses the `remove` slot). */
icons?: BstIconOverrides;
}): React.JSX.Element;
BstNoteEditor
Resizable popover editor for cell notes / comments. Closes on Escape or outside click, saves on Save button or Ctrl/Cmd+Enter.
function BstNoteEditor({ rowId, columnId, initialText, anchorRect, onSave, onDelete, onClose, placeholder, }: BstNoteEditorProps): React.JSX.Element;
BstNotePopover
Read-only floating tooltip/popover when hovering over a cell with a note.
function BstNotePopover({ note, anchorRect, onEdit }: BstNotePopoverProps): React.JSX.Element | null;
BstPdfThumbnailerProvider
Provide a grid-wide PDF thumbnail renderer. Wrap your tables (any adapter) in it; columns with cellMeta.pdfThumbnail: true then render page-1 thumbnails.
function BstPdfThumbnailerProvider({ renderer, children, }: {
renderer: PdfThumbnailRenderer | null;
children: React.ReactNode;
}): React.FunctionComponentElement<React.ProviderProps<PdfThumbnailRenderer | null>>;
BstSetFilter
function BstSetFilter({ column, table, label, value, onChange }: BstSetFilterProps): React.JSX.Element;
BstShortcuts
In-UI keyboard-shortcuts help: a trigger button + a dependency-free overlay that lists only the shortcuts active on this grid (grouped, searchable, platform-aware). Also opens on ?. Self-contained so an adapter just drops in <BstShortcuts table={table} />.
function BstShortcuts<TData extends RowData>({ table, className, platform, }: BstShortcutsProps<TData>): React.JSX.Element;
BstTable
Neutral, theme-agnostic table body renderer (Plan.md §2.6). Uses only semantic HTML + CSS classes driven by CSS custom properties. In Phase 2 each cell is a memoized GridCell that consults the cell-type registry (read path), the interaction store (edit/dirty/error state) and the runtime (edit lifecycle). MUI / shadcn skins theme it purely via --bst-table-* vars.
function BstTable({ table, className, icons, }: {
table: any;
className?: string;
/** Body-icon overrides (sort / expander / pin / boolean / file). Adapters
* forward their own icon set; unspecified slots use the built-in SVGs. */
icons?: BstIconOverrides;
}): React.JSX.Element;
buildPrintHtml
Build a standalone, print-friendly HTML document for the matrix.
function buildPrintHtml(matrix: BstExportMatrix, opts?: PrintOptions): string;
cellKey
Composite key for a cell (rowId first so a row's cells sort together).
function cellKey(rowId: string, columnId: string): string;
clearGridState
Remove a persisted snapshot (leaves the live grid untouched).
function clearGridState(key: string, storage?: BstGridStateStorage): void;
code128
Encode text as Code 128B. Throws on non-printable-ASCII input.
function code128(text: string): BarcodeResult;
combineFilterConditions
Combine several conditions into the value a multi-filter column stores. Keeps every slot (positional, undefined allowed) so the stacked widgets stay aligned; returns undefined when no slot is active (so the column reads as unfiltered). op defaults to and (every part must match).
function combineFilterConditions(conditions: (FilterCondition | undefined)[], op?: 'and' | 'or'): FilterConditionGroup | undefined;
computeAutoWidth
The fitted width for a column: the widest of texts measured in the given font, plus padding, clamped to [min, max]. Feed it the header label + the current page's formatted cell values (sampling, not the whole dataset).
function computeAutoWidth(texts: ReadonlyArray<string>, opts?: AutoSizeOptions): number;
computeCellSpans
Build the span plan for the visible rows × columns. O(rows × cols) — fine for the workflow/reporting tiers (a page of rows), which is the only tier without server-side windowing. getCellSpan (explicit) wins over meta.rowSpan groups for a given origin.
function computeCellSpans(rows: SpanRow[], cols: SpanCol[], getCellSpan?: (ctx: BstSpanContext) => BstCellSpan | undefined): SpanPlan;
createCellTypeRegistry
function createCellTypeRegistry(seed?: Array<CellType<any, any, any>>): CellTypeRegistry;
createClientDataSource
In-memory DataSource — applies filter → quick-filter → sort → page over rows using the SAME operator semantics (evalCondition) as the client grid. Sort is a best-effort natural comparison (numeric when both values parse as numbers, else locale string) — a real server defines its own ordering. Async (resolves a Promise, honours AbortSignal); delayMs simulates latency.
function createClientDataSource<TData>(rows: readonly TData[], opts?: {
/** Read a column's value from a row. Default `(row, id) => row[id]`. */
getValue?: (row: TData, columnId: string) => unknown;
/** Column ids the **quick filter** searches. Default: all own fields of the row. */
columns?: string[];
/** Simulated latency (ms). Default 0. */
delayMs?: number;
}): DataSource<TData>;
createDefaultRegistry
A registry seeded with the neutral defaults (used when no preset is supplied).
function createDefaultRegistry(): CellTypeRegistry;
createFileHandlers
Bridge a DataSource's file verbs (I3) to the files cell's cellMeta.onUpload / cellMeta.onDelete hooks, so ONE server contract drives both the grid query and file storage. Drop the result into a column:
meta: { type: 'files', editable: true, cellMeta: createFileHandlers(source) }
Only the verbs the source actually implements are wired (a source with no uploadFile leaves the cell on its local object-URL preview fallback).
function createFileHandlers<TData>(source: DataSource<TData>, ctx?: DataSourceFileContext): BstFileCellHandlers;
createInteractionStore
function createInteractionStore(): InteractionStore;
createPdfjsThumbnailer
Build a PdfThumbnailRenderer backed by pdf.js. Pass your imported pdfjs-dist module (worker already configured) or a loader () => import('pdfjs-dist') for lazy loading — pdf.js is fetched only when the first thumbnail renders, so it stays out of your initial bundle.
import * as pdfjs from 'pdfjs-dist' import Worker from 'pdfjs-dist/build/pdf.worker.min.mjs?worker' // Vite pdfjs.GlobalWorkerOptions.workerPort = new Worker() const thumbs = createPdfjsThumbnailer(pdfjs) // <BstPdfThumbnailerProvider renderer={thumbs}> … </BstPdfThumbnailerProvider>
function createPdfjsThumbnailer(pdfjs: PdfjsLike | (() => Promise<PdfjsLike | {
default: PdfjsLike;
}>), opts?: PdfThumbnailerOptions): PdfThumbnailRenderer;
createRuntime
function createRuntime<TData extends RowData>(initialCtx: RuntimeCtx<TData>): BstRuntime<TData>;
createServerDataSource
Wrap a fetch function as a DataSource (the server tier). Thin by design — your function is the source; this just names + types it.
const source = createServerDataSource(async (q, signal) => { const res = await fetch('/api/rows?' + toParams(q), { signal }) const { rows, total } = await res.json() return { rows, totalCount: total } })
function createServerDataSource<TData>(fetcher: (query: DataSourceQuery, signal?: AbortSignal) => Promise<DataSourcePage<TData>>): DataSource<TData>;
createStore
A minimal immutable external store. Every setState produces a new object.
function createStore<S>(initial: S): Store<S>;
defineCellType
Identity helper for authoring a CellType with full inference.
function defineCellType<TValue, TMeta = unknown, TData extends RowData = any>(ct: CellType<TValue, TMeta, TData>): CellType<TValue, TMeta, TData>;
defineFieldFormat
Identity helper for authoring a custom format with inference + a stable ref.
function defineFieldFormat(fmt: FieldFormat): FieldFormat;
downloadBlob
Trigger a browser download of data as fileName. No-ops under SSR / jsdom (no document / URL.createObjectURL), so callers never need to guard.
function downloadBlob(fileName: string, mime: string, data: string | Uint8Array): void;
emptyGridState
An "empty" snapshot — the cleared view (no sort / filter, default column layout). By default omits pagination and rowSelection (clearing those is rarely what "reset view" means); request them via include if you want them.
function emptyGridState(select?: BstGridStateSelect): BstGridState;
ensureExtension
Ensure name ends with .ext (case-insensitive), appending if missing.
function ensureExtension(name: string, ext: string): string;
escapeHtml
Dependency-free rich-text helpers for the richText cell type. Stored value is sanitized HTML; read mode shows a plain-text preview (htmlToText), edit mode is a contentEditable surface whose output is run through sanitizeHtml (an allow-list, so no script/style/handlers survive) on commit — no DOMPurify.
function escapeHtml(s: string): string;
evalCellFormat
Merge all matching cell-scope rules for one cell.
function evalCellFormat<TData = any>(rules: ReadonlyArray<BstFormatRule<TData>>, ctx: BstFormatContext<TData>, getValue: (columnId: string) => unknown): FormatResult;
evalCondition
Evaluate a { op, value } condition (or a bare value → contains) on a cell.
function evalCondition(cell: unknown, raw: unknown): boolean;
evalRowFormat
Merge all matching row-scope rules for one row.
function evalRowFormat<TData = any>(rules: ReadonlyArray<BstFormatRule<TData>>, row: TData, rowId: string, getValue: (columnId: string) => unknown): FormatResult;
filterFn_bstCondition
TanStack v9 filterFn that interprets a { op, value } condition.
function filterFn_bstCondition(row: any, columnId: string, filterValue: unknown): boolean;
filterSettingsGroups
Filter the sheet's groups by a free-text query (case-insensitive), for the search box. A setting matches on its label or hint; a group whose name matches keeps all its items (so "export" surfaces the whole Export section). An empty / whitespace query returns the groups unchanged. Pure — both adapters share it, like the rest of this model. Returns fresh group objects (item references reused) so callers never mutate the source model.
function filterSettingsGroups(groups: readonly BstSettingsGroup[], query: string): BstSettingsGroup[];
formatShortcutToken
Render one key token for display, platform-aware.
function formatShortcutToken(token: string, isMac: boolean): string;
getBstRuntime
Read the runtime handle a useBstTable render attached to the table.
function getBstRuntime<TData extends RowData>(table: unknown): BstRuntimeHandle<TData>;
getGridState
Snapshot the grid's current view state as plain JSON (X21). Pass include / exclude to capture only some slices (e.g. skip rowSelection / pagination).
function getGridState<TData extends RowData>(table: BstTableInstance<TData>, select?: BstGridStateSelect): BstGridState;
gstinCheckDigit
The GSTIN 15th character (checksum) for a 14-char prefix.
function gstinCheckDigit(first14: string): string;
hasBlockingError
True when any finding is an error (as opposed to a warning).
function hasBlockingError(errors: FieldError[] | undefined): boolean;
htmlToText
Strip tags → readable one-line text (SSR-safe; used for the read preview + copy).
function htmlToText(html: unknown): string;
humanizeKey
fooBar / foo_bar / foo-bar → "Foo Bar".
function humanizeKey(key: string): string;
inferCellType
Built-in cell-type guess from a sample value (X27). Text → undefined (untyped).
function inferCellType(_key: string, value: unknown): string | undefined;
isConditionActive
Whether a { op, value } condition (or bare value) would actually filter anything — i.e. evalCondition wouldn't treat it as inactive. A half-built builder row (operator chosen, no value) is inactive; unary ops (empty / isTrue / …) are always active. Used to strip no-op conditions before sending them to a server (client mode ignores them anyway).
function isConditionActive(raw: unknown): boolean;
isRichTextEmpty
True when the rich text has no visible content.
function isRichTextEmpty(html: unknown): boolean;
isSettingActive
Is a setting effectively active — on itself AND every prerequisite (requires) transitively active? Drives the sheet's dependency cascade: turning a parent off disables its dependents, and re-enabling it brings them back. Pure; exported for testing. props should be the override-applied (effective) props.
function isSettingActive(key: BstSettingKey, props: Record<string, unknown>, seen?: Set<BstSettingKey>): boolean;
isValidAadhaar
Aadhaar (India, UIDAI): 12 digits, first 2–9, valid Verhoeff checksum.
function isValidAadhaar(v: string): boolean;
isValidEsic
ESIC number (India Employees' State Insurance): 17 digits.
function isValidEsic(v: string): boolean;
isValidGstin
GSTIN (India GST): 2-digit state + 10-char PAN + entity + 'Z' + checksum.
function isValidGstin(v: string): boolean;
isValidIban
IBAN: 2-letter country + 2 check digits + BBAN, validated by the mod-97 rule.
function isValidIban(v: string): boolean;
isValidIec
IEC (India Import-Export Code): a PAN since 2017, or a legacy 10-digit code.
function isValidIec(v: string): boolean;
isValidIfsc
IFSC (India bank branch): 4 letters, a '0', 6 alphanumerics.
function isValidIfsc(v: string): boolean;
isValidPan
PAN (India Income Tax): 5 letters, 4 digits, 1 letter.
function isValidPan(v: string): boolean;
isValidPassport
Indian passport: a letter followed by 7 digits (e.g. A1234567).
function isValidPassport(v: string): boolean;
isValidSwift
SWIFT / BIC: 6 letters (bank + country) + 2 alphanumerics, optional 3-char branch.
function isValidSwift(v: string): boolean;
isValidUan
PF UAN (India Universal Account Number): 12 digits.
function isValidUan(v: string): boolean;
loadGridState
Load a persisted snapshot. Returns undefined when absent, unparseable, or from an incompatible schema version — feed the result straight into useBstTable({ initialState }) for a flash-free restore.
function loadGridState(key: string, storage?: BstGridStateStorage): BstGridState | undefined;
luhnValid
Luhn (mod-10) — the checksum credit/debit card numbers (13–19 digits) carry.
function luhnValid(num: string): boolean;
measureTextWidth
Smart column auto-size (D3) — a dep-free content-measurement helper. Measures text with an offscreen canvas.measureText (no layout thrash, no DOM insertion) so a column can be sized to fit its widest sampled value. Sampling (header + the current page's cells) and the trigger (double-click the resize handle) live in the renderer; this module is the pure measurement core, also exported so consumers can auto-size programmatically.
function measureTextWidth(text: string, font: string): number;
operatorArity
Look up an operator's arity (value-input count). Default 1.
function operatorArity(typeId: string | undefined, op: string): 0 | 1 | 2;
operatorsForType
The operators offered for a given meta.type.
function operatorsForType(typeId?: string): FilterOperator[];
pageSizeForChoice
Turn a chosen dropdown value into the page size to apply (setPageSize).
function pageSizeForChoice(value: number): number;
partitionToolbar
Decide which collapsible items stay inline vs move to overflow, given the width available to them and the width the "⋯" button costs once anything overflows. Keeps the highest-priority contiguous run that fits. Pure.
function partitionToolbar(items: readonly ToolbarItemWidth[], available: number, moreWidth: number): {
inline: string[];
overflow: string[];
};
printHtml
Open html in a print view and invoke the browser print dialog. Prefers a new window (self-contained, doesn't touch the host DOM); falls back to a hidden iframe when a popup is blocked. No-ops under SSR / jsdom.
function printHtml(html: string): void;
qrMatrix
Encode text to a QR module matrix. Throws if it doesn't fit in versions 1–10.
function qrMatrix(text: string, level?: QrEcLevel, forceMask?: number): QrMatrix;
resetGridState
Clear the grid's view back to defaults — sort · filter · column layout · grouping · expansion · row pinning. pagination and rowSelection are left as-is unless you pass them via include.
function resetGridState<TData extends RowData>(table: BstTableInstance<TData>, select?: BstGridStateSelect): void;
resolveActiveShortcuts
Filter the registry to the shortcuts whose required flags are all active, grouped by category and optionally narrowed by a free-text query (label or keys). Pass isMac to drop platform-specific entries (e.g. ⌘Y redo on Mac); omit it to keep everything. Pure — the overlay renders exactly this.
function resolveActiveShortcuts(flags: Record<string, boolean | undefined>, query?: string, isMac?: boolean): ResolvedShortcutGroup[];
resolveBstIcons
Merge overrides onto the defaults, skipping undefined/null so a partial map (e.g. an adapter forwarding only the slots its icon set covers) keeps the built-in SVG for every unspecified slot.
function resolveBstIcons(overrides?: BstIconOverrides): BstIcons;
resolveFieldFormat
Resolve a cellMeta.pattern value to a FieldFormat: a built-in name, a RegExp (+ optional message), or a format object. Unknown names → null.
function resolveFieldFormat(pattern: unknown, patternMessage?: string): FieldFormat | null;
resolvePageSizeChoices
Resolve the dropdown's choices plus the value that should read as selected. "All" is shown as selected whenever the current page size is not one of the offered numeric sizes (i.e. it was set to the "All" sentinel, or otherwise exceeds every option) — so picking a normal size like 20 still shows 20 even when there are fewer than 20 rows. Pure — unit-tested.
function resolvePageSizeChoices(options: ReadonlyArray<BstPageSizeOption>, currentPageSize: number): {
choices: BstPageSizeChoice[];
value: number;
};
resolveStickyHeader
Fold enableStickyHeader (boolean | BstStickyHeaderOptions, §12 value shape — an object implies enabled) into one resolved config. maxHeight wins over maxRows; a number is treated as pixels, a string is passed through verbatim. Pure — unit-tested.
function resolveStickyHeader(enable: boolean | BstStickyHeaderOptions | undefined): ResolvedStickyHeader;
resolveVirtualization
Fold enableVirtualization (boolean | VirtualizationOptions, §12 value shape — an object implies enabled) plus the enableColumnVirtualization sub-toggle into one resolved config. Pure — unit-tested.
function resolveVirtualization(enable: boolean | VirtualizationOptions | undefined, enableColumns: boolean | undefined): ResolvedVirtualization;
RichTextEditor
Neutral inline rich-text editor: a contentEditable surface + a small toolbar (bold / italic / underline / lists). Self-commits on blur with the value run through sanitizeHtml (allow-list), so no unsafe markup is stored. Adapters override this with a popup (dialog) version.
function RichTextEditor({ draft, commit, cancel, autoFocus }: CellEditProps<any, string>): React.JSX.Element;
runValidators
Compose the validators for a cell (C3): a required check, then the CellType's built-in validator, then the column-level meta.validate. Returns a synchronous FieldError[] when every validator is sync, otherwise a Promise — the runtime handles async with last-write-wins + AbortSignal.
function runValidators<TData extends RowData>(value: unknown, cellType: CellType<any, any, TData>, ctx: CellValidateContext<TData>): FieldError[] | Promise<FieldError[]>;
sanitizeHtml
Allow-list HTML sanitizer: keeps only formatting tags (no attributes except a safe href), escaping everything else. Uses DOMParser; on the server (no DOM) it degrades to escaped plain text.
function sanitizeHtml(html: unknown): string;
saveGridState
Persist a snapshot under bst-table:state:<key>. No-op when storage is unavailable.
function saveGridState(key: string, state: BstGridState, storage?: BstGridStateStorage): void;
shouldShowSettingsSearch
Resolve whether the sheet shows its search box. false → never; true → always; omitted → auto (only for lists longer than a handful, so a short sheet stays clutter-free). Keeps both adapters' behaviour identical.
function shouldShowSettingsSearch(search: boolean | undefined, itemCount: number): boolean;
splitCellKey
Split a composite key back into its rowId / columnId parts.
function splitCellKey(key: string): {
rowId: string;
columnId: string;
};
toCsv
Serialize a matrix to CSV (RFC 4180: ""-escaped quotes, CRLF, optional BOM).
function toCsv(matrix: BstExportMatrix, opts?: CsvOptions): string;
toXlsx
Serialize a matrix to a real OOXML .xlsx (store-only ZIP, no dependency).
function toXlsx(matrix: BstExportMatrix, opts?: XlsxOptions): Uint8Array;
verhoeffChecksum
The Verhoeff check digit for a numeric payload (append it to make it valid).
function verhoeffChecksum(payload: string): string;
verhoeffValid
True when a numeric string carries a valid Verhoeff check digit (last digit).
function verhoeffValid(num: string): boolean;
virtualizationBypassReason
The first active incompatible feature's label, or null when virtualization can run. Pure — unit-tested; the renderer uses it to decide the windowed vs un-windowed path and to emit a one-time dev-console warning.
function virtualizationBypassReason(compat: VirtualizationCompat): string | null;