import type { SparklineConfig } from './sparkline'; export type RowData = Record; export type Updater = T | ((prev: T) => T); export type SortingState = Array<{ id: string; desc: boolean; }>; export type ColumnFilter = { id: string; value: unknown; fn?: keyof typeof filterFns; }; export type ColumnFiltersState = Array; export type PaginationState = { pageIndex: number; pageSize: number; }; export type GroupingState = Array; export type ExpandedState = Record; export type RowSelectionState = Record; export type ActiveCellState = { rowIndex: number; colIndex: number; cellId: string | null; }; export type TableFeatures = Record; export type CellData = unknown; export type HeaderContext = { header: Header; column: Column; table: SvGrid; }; export type CellContext = { cell: Cell; row: Row; column: Column; table: SvGrid; getValue: () => unknown; }; /** Params passed to a column's `colSpan(...)` / `rowSpan(...)` callbacks. */ export type CellSpanParams = { /** The row's underlying data object. */ data: TData; /** Display-row index in the current (filtered/sorted) row set. */ rowIndex: number; /** The column's id. */ columnId: string; /** The cell's base value for this column. */ value: unknown; }; /** The raw option list a column's `editorOptions` can supply. */ export type EditorOptionSource = ReadonlyArray; /** Params passed to a column's `valueParser(...)` on edit commit. */ export type ValueParserParams = { /** The value after built-in per-`editorType` coercion. */ newValue: unknown; /** The cell's previous value. */ oldValue: unknown; /** The raw string the editor produced (pre-coercion). */ rawInput: string; /** The row's underlying data object. */ data: TData; /** The column's id. */ columnId: string; }; /** * Context passed to a custom `cellEditor` snippet/component. Three write * helpers cover the lifecycle: * * - `update(next)` - stage `next` as the draft, keep the editor open. * Use this for live-preview controls (sliders, * color pickers) so the user can keep adjusting. * - `commit(next?)` - write the value AND close the editor. The * argument is optional; when omitted, the most * recently `update()`d value is saved. Use this * for "done" gestures (Enter, picking an option). * - `cancel()` - discard the draft and close the editor. */ export type EditorContext = CellContext & { value: unknown; update: (next: unknown) => void; commit: (next?: unknown) => void; cancel: () => void; }; export type CellFormatConfig = { type: 'number'; locales?: string | Array; options?: Intl.NumberFormatOptions; } | { type: 'currency'; /** ISO 4217 (default USD) */ currency?: string; locales?: string | Array; options?: Omit; } | { type: 'percent'; locales?: string | Array; options?: Omit; /** * If true, numeric cell values are 0–100 (e.g. 42 → 42%) instead of Intl’s 0–1 fraction (0.42 → 42%). * Default false. */ valueIsPercentPoints?: boolean; } | { type: 'date' | 'datetime'; locales?: string | Array; /** * Shortcut patterns merged with `options`: * `'d'` short numeric date, `'D'` long date, `'y-m-d'` yyyy/mm/dd-style, * `'short'`|`'medium'`|`'long'` use dateStyle/timeStyle presets. */ pattern?: string; options?: Intl.DateTimeFormatOptions; }; export type CellFormatter = (context: { value: unknown; row: Row; column: Column; table: SvGrid; }) => string; export type ColumnDefTemplate = string | ((context: TContext) => unknown); /** * How a column's value is aggregated for a group row when `columnGrouping` * is active. Built-in reducers cover the common cases; pass a function for * anything custom (weighted average, median, percentile, distinct count). * The function receives the finite numeric values AND the raw leaf rows. */ export type GroupAggregator = 'sum' | 'avg' | 'min' | 'max' | 'count' | 'countDistinct' | 'extent' | 'first' | ((values: number[], rows: Array) => unknown); /** Apply a group aggregator over a bucket's leaf rows for one column. */ export declare function applyGroupAggregate(agg: GroupAggregator, columnId: string, rows: ReadonlyArray>): unknown; /** * A column definition. * * `TFeatures` is a phantom parameter - it is threaded through nested * `columns` groups but no member depends on it, so `{}`, `TableFeatures` and * `typeof features` are all interchangeable here. It is deliberately left * WITHOUT a default: `ColumnDef` would otherwise bind `Row` to this slot * and silently type your data as `RowData`, losing every field-name check. * Prefer {@link GridColumns} / {@link GridColumnDef} for the common case. */ export type ColumnDef = { id?: string; field?: keyof TData & string; fieldFn?: (row: TData) => unknown; header?: ColumnDefTemplate>; footer?: ColumnDefTemplate>; cell?: ColumnDefTemplate>; columns?: Array>; /** * Declarative cell spanning (merged cells). Return how many COLUMNS this * cell spans to the right (1 = no span). Value-driven, AG-Grid-style. Feed * `spansToMerges(rows, columns)` into `spreadsheetLayout` to apply - it uses * the same real `colspan`/`rowspan` merge engine (no separate code path). */ colSpan?: (params: CellSpanParams) => number; /** * Declarative cell spanning (merged cells). Return how many ROWS this cell * spans downward (1 = no span). See `colSpan` for how to apply. */ rowSpan?: (params: CellSpanParams) => number; /** * High-level data type for the column. A convenience that resolves to the * right `editorType`, alignment, date `format`, and filter operators without * setting each by hand: * 'text' → text editor, left-aligned * 'number' → number editor, right-aligned, numeric filter operators * 'boolean' → checkbox editor, centered * 'date' → date editor (Date values), right-aligned, `{ type: 'date' }` format * 'dateString' → date editor for ISO date STRINGS (e.g. '2026-06-27') * Anything you set explicitly (`editorType`, `align`, `format`) still wins - * `cellDataType` only fills the gaps. Grid-level `inferColumnTypes` infers * this from the first data row for columns that declare neither. */ cellDataType?: 'text' | 'number' | 'boolean' | 'date' | 'dateString'; /** * Hide this column when the grid's `responsive` mode is on and the grid is * narrower than this many pixels - drop low-priority columns on small * screens. No effect unless the grid has `responsive` set. */ hideBelow?: number; /** * For a column INSIDE a collapsible column group: `'open'` shows this column * only while the group is expanded, `'closed'` only while collapsed. Omit to * always show it. Setting it on any direct child gives the parent group a * collapse toggle. Pair with `openByDefault` on the group. */ columnGroupShow?: 'open' | 'closed'; /** * For a GROUP column (one with `columns: [...]`): start the group expanded. * Defaults to `false` (collapsed), matching AG Grid - so only the always-on * and `columnGroupShow: 'closed'` children show until the user expands it. */ openByDefault?: boolean; editorType?: 'text' | 'number' | 'date' | 'datetime' | 'time' | 'date-native' | 'datetime-native' | 'time-native' | 'password' | 'checkbox' | 'list' | 'chips' | 'select' | 'rich-select' | 'autocomplete' | 'textarea' | 'color' | 'rating' | (string & {}); /** * Custom in-cell editor. Receives the cell context PLUS a `commit(value)` * and `cancel()` helper. Use when none of the built-in `editorType`s fit; * the snippet's outer element is mounted inside the editing cell and * inherits keyboard handling (Esc cancels, Enter commits unless your * snippet preventDefaults it). * * Coexists with `editorType`: when both are set, `cellEditor` wins and * `editorType` is treated as a hint for parsing the saved value. */ cellEditor?: ColumnDefTemplate>; /** * Per-column tooltip. String shows as a native `title=`; `(ctx) => string` * runs per cell so the tooltip can reflect the value. Returning an empty * string skips the tooltip. */ tooltip?: string | ((ctx: CellContext) => string | null | undefined); /** * Declarative per-cell validation (Handsontable-style). Runs for EVERY * rendered cell - including values already present in `data` on load, not * just on edit - so bad data is flagged immediately. Invalid cells get the * `sv-grid-cell-invalid` class (red highlight) and the returned message as * their tooltip. * * Return value: * - `null` / `undefined` / `true` → valid (no highlight) * - `false` → invalid, no message * - a non-empty `string` → invalid, string is the tooltip * * The value keeps rendering as-is (the grid does NOT roll it back); pair * with `onCellValueChange` if you also want to reject the commit. */ validate?: (params: { value: unknown; row: TData; rowIndex: number; column: Column; }) => string | boolean | null | undefined; /** * Gate editing per column or per cell. * * - `true` (or omitted): the column is fully editable. * - `false`: the column is read-only - double-click, type-to-edit, * fill-handle drag, Delete, and clipboard paste all skip it. * - `(ctx) => boolean`: evaluated for each cell, so you can lock * individual rows (e.g. by role, status, ownership). Returning * `false` opts the cell out of every editing path, identical to * setting `editable: false` on the whole column for that row. * * The grid-wide `enableInlineEditing` prop still wins when set to * `false`. */ editable?: boolean | ((context: CellContext) => boolean); /** * Transform the committed edit value before it is written to the row. * Runs after the built-in per-`editorType` coercion, so `newValue` is * already type-parsed; return the final value to store (e.g. round a * number, uppercase a code, look up an id). AG-Grid-style `valueParser`. */ valueParser?: (params: ValueParserParams) => unknown; /** * Briefly flash / highlight this column's cell when its value changes * (streaming feeds, edits, server pushes). `true` uses the default flash; * pass `{ className }` to apply your own animation class instead. */ cellFlash?: boolean | { className?: string; }; /** * When `false`, this column never shows a sort indicator and clicking * its header is a no-op - `api.setSort(thisColumn, ...)` is also * ignored. Defaults to `true` (the column participates in sorting as * long as `rowSortingFeature` is registered). */ sortable?: boolean; /** * When `false`, this column never shows a filter funnel / menu and * `api.setFilter(thisColumn, ...)` is ignored. Defaults to `true` (the * column is filterable as long as `columnFilteringFeature` is * registered). */ filterable?: boolean; /** * Options for `editorType: 'list' | 'chips'`. Either bare values (the * string is both value and label) or `{ value, label }` objects. * For `chips` this is optional - when omitted, the chips editor becomes * free-form (user types and presses Enter to commit a chip). * * Pass a function `(row) => options` for row-dependent (cascading) * options - e.g. City options that depend on Country in the same row. * * Either form may return a **Promise**, for options that come from the * server. While it resolves, the editor shows a loading state and the cell * renders its raw value. * * Results are cached so reopening an editor does not refetch: a static source * per column, a per-row source per row AND per that row's data - so a cascade * reloads by itself when the cell it depends on is edited. Call * `api.refreshEditorOptions(columnId?)` when the list changes server-side. */ editorOptions?: EditorOptionSource | Promise | ((row: TData) => EditorOptionSource | Promise); /** When true, list/chips allow multiple selections. Cell value becomes an array. */ editorMultiple?: boolean; /** Separator used when joining array values for the readonly cell display. Defaults to ', '. */ editorSeparator?: string; format?: CellFormatConfig; formatter?: CellFormatter; /** * Aggregate this column's values into the group row when grouping is * active. `'sum' | 'avg' | 'min' | 'max' | 'count' | 'countDistinct' | * 'extent' | 'first'`, or a custom `(values, rows) => unknown`. The result * is formatted with this column's `format` and shown in the group header. */ aggregate?: GroupAggregator; /** * Render the cell as an in-cell sparkline chart. The cell value should be * an array of numbers (or a comma/space separated string). Mutually * exclusive with a custom `cell` renderer (a `cell` wins if both are set). * * { sparkline: { type: 'line' } } // default line * { sparkline: { type: 'bar', color: '#16a34a' } } * { sparkline: { type: 'winloss' } } // sign-only up/down * * See `SparklineConfig` for the full option set (type, color, * negativeColor, width, height, fixed min/max). */ sparkline?: SparklineConfig; /** Initial column width in pixels. Falls back to the grid's `columnWidth` prop. */ width?: number; /** * Initial visibility. Set `false` to start the column hidden while still * listing it in the Choose Columns UI for the user to re-enable. Applied * once at mount; after that `api.setColumnVisible` / user toggles win. * On a group column, `false` hides the whole group's leaf columns. */ visible?: boolean; /** * Horizontal alignment for header and body cells. When omitted, the * default is inferred from `editorType`: * - `'number' | 'date' | 'datetime'` → `'right'` * - `'checkbox'` → `'center'` * - everything else → `'left'` */ align?: 'left' | 'center' | 'right'; /** * Per-cell conditional CSS. Two shapes: * * - **String** (or array of strings): class name(s) added to the * cell's `` for every row in this column. * - **Function**: invoked per cell with the same `CellContext` shape * the `cell` renderer receives. Return a string, an array of * strings, or an object mapping class names to booleans. * * Use it for status tinting, conditional bold, "negative number" * coloring - anything that's a function of the row's value. Cells * still receive their format / cell renderer; the class just * augments the rendered ``. */ cellClass?: string | ReadonlyArray | ((ctx: CellContext) => string | ReadonlyArray | Record | undefined | null); }; /** * A column definition keyed only by your row type - the ergonomic form of * {@link ColumnDef}, whose first parameter is a phantom feature bag that is * almost always `{}`. * * ```ts * const columns: GridColumns = [{ field: 'firstName', header: 'Name' }] * ``` * * Interchangeable with `ColumnDef<{}, TData>` and `ColumnDef` in both directions, so it mixes freely with existing code. */ export type GridColumnDef = ColumnDef; /** An array of {@link GridColumnDef} - what you pass to ``. */ export type GridColumns = Array>; export type Column = { id: string; columnDef: ColumnDef; depth: number; parentId?: string; getCanSort: () => boolean; getCanFilter: () => boolean; getIsSorted: () => false | 'asc' | 'desc'; getToggleSortingHandler: () => () => void; }; export type Header = { id: string; isPlaceholder: boolean; colSpan: number; column: Column; getContext: () => HeaderContext; }; export type HeaderGroup = { id: string; headers: Array>; }; export type Cell = { id: string; row: Row; column: Column; getValue: () => unknown; getContext: () => CellContext; }; export type Row = { id: string; index: number; original: TData; depth: number; subRows?: Array>; /** Total leaf (data) rows under this group row. Undefined for data rows. */ leafCount?: number; getCanExpand: () => boolean; getIsExpanded: () => boolean; toggleExpanded: () => void; getIsSelected: () => boolean; toggleSelected: () => void; getAllCells: () => Array>; getCellValueByColumnId: (columnId: string) => unknown; }; export type RowModel = { rows: Array>; }; export type Store = { readonly state: T; setState: (updater: (prev: T) => T) => void; subscribe: (listener: () => void) => () => void; }; export declare const rowSortingFeature: { key: string; }; export declare const columnFilteringFeature: { key: string; }; export declare const rowPaginationFeature: { key: string; }; export declare const columnGroupingFeature: { key: string; }; export declare const rowSelectionFeature: { key: string; }; export declare const rowExpandingFeature: { key: string; }; export declare function tableFeatures(features: T): T; export declare const sortFns: { auto: (a: unknown, b: unknown) => number; number: (a: unknown, b: unknown) => number; date: (a: unknown, b: unknown) => number; }; export declare const filterFns: { includesString: (value: unknown, query: string) => boolean; equals: (value: unknown, query: unknown) => boolean; }; export type RowModelFactory = (args: { table: SvGrid; rows: Array>; }) => Array>; export declare function createCoreRowModel(): RowModelFactory; export declare function createFilteredRowModel(): RowModelFactory; export declare function createPaginatedRowModel(): RowModelFactory; export declare function createGroupedRowModel(): RowModelFactory; export type TreeRowModelOptions = { /** Field holding each row's parent id. Rows with no parent are roots. */ parentField: string; /** Field holding the row's own id. Defaults to `'id'`. */ idField?: string; }; /** * Client-side tree data: nest the grid's own flat rows into a parent/child * hierarchy that `createExpandedRowModel` then walks. * * This works on the rows the grid already built rather than on raw data, so * tree rows keep their cells, editing, selection and formatting - they are real * data rows that happen to have children, not synthetic banners like grouping's. * That is also why the model is parent-id based: nested source arrays never * become rows (the grid only builds rows for `data`), so nested input is * flattened first with {@link flattenTreeData}. One code path, no duplicated * row construction. * * Rows are tagged `__treeRow` so `isGroupRow` does not mistake an expandable * data row for a full-width group banner. */ export declare function createTreeRowModel(options: TreeRowModelOptions): RowModelFactory; export type FlattenTreeOptions = { /** Field holding an array of child objects. */ childrenField: string; /** Field holding each object's id. Defaults to `'id'`. */ idField?: string; /** Field to WRITE the resolved parent id onto. Defaults to `'__parentId'`. */ parentField?: string; }; /** * Flatten nested tree data into the flat parent-id shape `createTreeRowModel` * consumes, stamping each child with its parent's id. * * Children are emitted directly after their parent so the natural order already * matches the rendered tree. The `childrenField` array is left on the objects * (harmless, and callers often still want it); only the parent link is added. */ export declare function flattenTreeData(data: ReadonlyArray, options: FlattenTreeOptions): T[]; export declare function createExpandedRowModel(): RowModelFactory; export declare function createSortedRowModel(localSortFns?: typeof sortFns): RowModelFactory; export type SvGridOptions = { _features: TFeatures; _rowModels?: { coreRowModel?: RowModelFactory; filteredRowModel?: RowModelFactory; sortedRowModel?: RowModelFactory; paginatedRowModel?: RowModelFactory; groupedRowModel?: RowModelFactory; expandedRowModel?: RowModelFactory; }; columns: Array>; data: ReadonlyArray; /** * Optional row-id resolver. When set, the value it returns becomes * `row.id` (and therefore the selection / expansion / edit key). When * omitted, ids fall back to the row's array index as a string. Use a * stable id (database PK, UUID, etc.) so selection survives reorders. */ getRowId?: (row: TData, index: number) => string; state?: Partial>; onSortingChange?: (updater: Updater) => void; onColumnFiltersChange?: (updater: Updater) => void; onPaginationChange?: (updater: Updater) => void; onGroupingChange?: (updater: Updater) => void; onExpandedChange?: (updater: Updater) => void; onRowSelectionChange?: (updater: Updater) => void; onActiveCellChange?: (updater: Updater) => void; }; export type SvGrid = { store: Store>; optionsStore: Store>; state: Record; getState: () => Record; setOptions: (updater: Updater>) => void; setColumnFilters: (updater: Updater) => void; setPagination: (updater: Updater) => void; setGrouping: (updater: Updater) => void; setExpanded: (updater: Updater) => void; setRowSelection: (updater: Updater) => void; setActiveCell: (updater: Updater) => void; moveActiveCell: (next: { rowDelta?: number; colDelta?: number; }) => void; getAllColumns: () => Array>; getHeaderGroups: () => Array>; getFooterGroups: () => Array>; getRowModel: () => RowModel; }; export declare function createSvGridCore(options: SvGridOptions): SvGrid; export declare function isFunction(value: unknown): value is (...args: Array) => any;