import { Signal, Value } from '@tempots/dom'; import { FilterBase } from './filter'; /** * Sort direction for a column. */ export type SortDirection = 'asc' | 'desc'; /** * Describes how a column should be sorted. * * @typeParam C - Column identifier type. Defaults to `string`; narrow to a * union of literal column IDs for compile-time safety. */ export interface SortDescriptor { /** Column identifier */ column: C; /** Sort direction */ direction: SortDirection; } /** * An action available when rows are selected. */ export interface BulkAction { /** Display label */ label: string; /** Optional icon identifier */ icon?: string; /** Callback receiving the set of selected row IDs */ onClick: (selected: Set) => void; } /** * Configuration options for {@link createDataSource}. * * @typeParam T - The type of data rows * @typeParam C - Column identifier type (defaults to `string`) */ /** * A group of rows sharing the same group-by value. * * @typeParam T - The type of data rows */ export interface RowGroup { /** The stringified group key */ key: string; /** Rows belonging to this group */ rows: T[]; } export interface DataSourceOptions { /** The source data array (static or reactive) */ data: Value; /** Returns a unique string ID for each row */ rowId: (row: T) => string; /** Maps column IDs to accessor functions that extract the column value from a row */ accessors?: Partial unknown>>; /** Maps column IDs to custom comparator functions for sorting */ comparators?: Partial number>>; /** Initial sort state */ initialSort?: SortDescriptor[]; /** Initial filter state */ initialFilters?: FilterBase[]; /** Page size (undefined = no pagination) */ pageSize?: Value; /** Allow sorting on multiple columns simultaneously. @default false */ multiSort?: Value; /** When true, skips client-side sort/filter/paginate (caller drives rows externally). @default false */ serverSide?: Value; /** Total row count for server-side pagination. When provided (and serverSide is true), overrides the client-side row count for pagination calculations. */ totalRows?: Value; /** Column to group rows by. `undefined` disables grouping. */ groupBy?: Value; /** Called whenever sort state changes */ onSortChange?: (sort: SortDescriptor[]) => void; /** Called whenever filter state changes */ onFilterChange?: (filters: FilterBase[]) => void; /** Called whenever the current page changes */ onPageChange?: (page: number) => void; /** Called whenever selection changes */ onSelectionChange?: (selected: Set) => void; /** Called whenever the group-by column changes */ onGroupByChange?: (column: C | undefined) => void; /** Evaluate a custom (non-builtin) filter kind. Return `true` to include the row. */ evaluateFilter?: (filter: FilterBase, row: T) => boolean; } /** * A headless reactive data source managing sort, filter, selection, and pagination state. * * @typeParam T - The type of data rows * @typeParam C - Column identifier type (defaults to `string`) */ export interface DataSource { /** Final visible rows (sorted, filtered, paginated) */ rows: Signal; /** Total number of rows in source data */ totalRows: Signal; /** Number of rows after filtering (before pagination) */ totalFilteredRows: Signal; /** Current sort descriptors */ sort: Signal[]>; /** Toggle sort on a column: none → asc → desc → none. Pass `{ multi: true }` to add/modify without replacing other sorts. */ toggleSort: (column: C, opts?: { multi?: boolean; }) => void; /** Replace the full sort state */ setSort: (sort: SortDescriptor[]) => void; /** Reactive sort direction for a specific column */ getSortDirection: (column: C) => Signal; /** Clear all sorts */ resetSort: () => void; /** Current filter descriptors */ filters: Signal[]>; /** Set or replace all filters on a column */ setFilter: (filter: FilterBase) => void; /** Add a filter alongside existing ones on the same column */ addFilter: (filter: FilterBase) => void; /** Remove all filters from a column */ removeFilter: (column: C) => void; /** Reactive list of filters for a specific column */ getColumnFilters: (column: C) => Signal[]>; /** Reactive text filter value for a specific column (value from first TextFilter, or '') */ getTextFilterValue: (column: C) => Signal; /** Clear all filters */ resetFilters: () => void; /** Set of selected row IDs */ selected: Signal>; /** Toggle selection of a row */ toggleSelect: (id: string) => void; /** Add rows to selection */ select: (ids: string[]) => void; /** Remove rows from selection */ deselect: (ids: string[]) => void; /** Select all currently visible (filtered) rows */ selectAll: () => void; /** Clear selection */ deselectAll: () => void; /** Whether all filtered rows are selected */ isAllSelected: Signal; /** Whether some (but not all) filtered rows are selected */ isSomeSelected: Signal; /** Count of selected rows */ selectedCount: Signal; /** Current page (1-indexed) */ currentPage: Signal; /** Total number of pages */ totalPages: Signal; /** Current page size */ pageSize: Signal; /** Navigate to a page */ setPage: (page: number) => void; /** Change page size (resets to page 1) */ setPageSize: (size: number) => void; /** Current group-by column (undefined = no grouping) */ groupBy: Signal; /** Set or clear group-by column */ setGroupBy: (column: C | undefined) => void; /** Grouped rows (empty array when groupBy is undefined) */ groups: Signal[]>; /** All rows after sort and filter, before pagination */ allFilteredRows: Signal; /** Swap two rows by ID (only effective when no sort active) */ moveRow: (fromId: string, toId: string) => void; /** Reset sort, filters, selection, page, and groupBy */ resetAll: () => void; /** Clean up all reactive subscriptions */ dispose: () => void; } /** * Creates a headless reactive data source for managing tabular data operations. * * Provides sorting, filtering, selection, pagination, and manual reorder as * reactive signals and imperative methods. All processing happens client-side * unless `serverSide` is true. * * @typeParam T - The type of data rows * @param options - Configuration for the data source * @returns A {@link DataSource} object with reactive state and methods * * @example * ```ts * const ds = createDataSource({ * data: prop([ * { id: '1', name: 'Alice', age: 30 }, * { id: '2', name: 'Bob', age: 25 }, * ]), * rowId: row => row.id, * }) * * ds.toggleSort('name') // sort by name ascending * ds.setFilter(Filter.contains('name', 'ali')) // filter name containing "ali" * ds.rows.value // [{ id: '1', name: 'Alice', age: 30 }] * ds.dispose() * ``` */ export declare function createDataSource(options: DataSourceOptions): DataSource;