/** * Data sources for the grid. * * A data source owns the collection and knows how to resolve a QueryState into * a PageResult. The grid only knows the duck-typed contract: * * dataSource.load(query, { signal }) -> Promise */ export type SortState = { field: string; direction: "asc" | "desc"; }; export type FilterOperator = "eq" | "neq" | "contains" | "notContains" | "startsWith" | "notStartsWith" | "endsWith" | "notEndsWith" | "lt" | "lte" | "gt" | "gte" | "between" | "in" | "empty" | "notEmpty"; export type FilterState = { operator: FilterOperator; value?: any; }; export type FilterInput = FilterState | String | Number | Boolean; export type QueryState = { page: number; pageSize: number; /** * Global search (server decides which fields it covers) */ search: string; sort: SortState[]; filters: Record; }; export type PageResult = { rows: Array>; /** * Number of rows matching the current query (used for pagination). Null or absent in pager "more" mode when the backend skips COUNT(*) */ total?: number | null; /** * Authoritative continuation signal for pager "more" mode (LIMIT+1 style). Wins over total when both are present */ hasMore?: boolean; /** * Additional information (ex: total unfiltered) */ meta?: Record; }; export type FilterOption = { value: string | number | boolean; text: string; }; export type DataSource = { load: (query: QueryState, options: { signal?: AbortSignal; }) => Promise; }; /** * Sort state * @typedef {Object} SortState * @property {String} field * @property {"asc"|"desc"} direction */ /** * Supported filter operators * @typedef {"eq"|"neq"|"contains"|"notContains"|"startsWith"|"notStartsWith"|"endsWith"|"notEndsWith"|"lt"|"lte"|"gt"|"gte"|"between"|"in"|"empty"|"notEmpty"} FilterOperator */ /** * Filter state * @typedef {Object} FilterState * @property {FilterOperator} operator * @property {any} [value] */ /** * Accepted public filter values. A scalar is a shorthand for * `{ operator: "contains", value }`; the structured form allows choosing * the operator (`empty`/`notEmpty` have no value). * @typedef {FilterState | String | Number | Boolean} FilterInput */ /** * Runtime query state. Single source of truth for pagination, search, sort and * filters. * @typedef {Object} QueryState * @property {Number} page * @property {Number} pageSize * @property {String} search Global search (server decides which fields it covers) * @property {SortState[]} sort * @property {Record} filters */ /** * Result of a data source load * @typedef {Object} PageResult * @property {Array>} rows * @property {Number|null} [total] Number of rows matching the current query (used for pagination). Null or absent in pager "more" mode when the backend skips COUNT(*) * @property {Boolean} [hasMore] Authoritative continuation signal for pager "more" mode (LIMIT+1 style). Wins over total when both are present * @property {Record} [meta] Additional information (ex: total unfiltered) */ /** * A selectable value for a select filter, as provided by meta.filters * @typedef {Object} FilterOption * @property {String|Number|Boolean} value * @property {String} text */ /** * Data source contract (duck typing, no abstract class) * @typedef {Object} DataSource * @property {(query: QueryState, options: {signal?: AbortSignal}) => Promise} load */ /** * Encode a nested structure into bracket-style URL search params. * Generic helper, it has no knowledge of QueryState. * Conventions: * - string / number -> string * - boolean -> "true" / "false" * - null / undefined -> omitted * - array -> indexed notation a[0]=x * - object -> recursive bracket notation a[b]=x * @param {any} value * @param {String} prefix * @param {URLSearchParams} out * @returns {URLSearchParams} */ export declare function encodeSearchParams(value: any, prefix?: string, out?: URLSearchParams): URLSearchParams; /** * Apply structured filters to an array. * Semantics: * - empty := null | undefined | "" (0 and false are NOT empty) * - contains / notContains / startsWith / notStartsWith / endsWith / * notEndsWith: case- and accent-insensitive string comparison * - eq / neq: a boolean value compares normalized booleans (true matches * 1, "1" and "true"); otherwise scalar comparison after string coercion, * case- and accent-insensitive for text (42 matches "42", "Café" matches * "cafe") * - in: scalar comparison after string coercion, case- and accent-insensitive * for text * - lt/lte/gt/gte/between: numeric comparison when both operands are finite * numeric values, otherwise string comparison * - between requires a 2-value array, in requires a non-empty array * - empty/invalid filter values are ignored, not treated as "match nothing" * @param {Array>} rows * @param {Record} [filters] * @returns {Array>} */ export declare function applyFilters(rows: Array>, filters?: Record): Array>; /** * Apply the first sort state to an array (single sort for now). * @param {Array>} rows * @param {SortState[]} [sort] * @returns {Array>} */ export declare function applySort(rows: Array>, sort?: SortState[]): Array>; /** * Slice a sorted/filtered array to the requested page. * @param {Array>} rows * @param {Number} page * @param {Number} pageSize * @returns {Array>} */ export declare function paginate(rows: Array>, page: number, pageSize: number): Array>; /** * Parse a raw response into a PageResult. * * The canonical server contract is: * ```json * { "rows": [...], "total": 142, "meta": { "unfilteredTotal": 998 } } * ``` * `total` counts the rows matching the current query; `meta.unfilteredTotal` * (optional) counts the population before any search/filter. A backend that * skips COUNT(*) omits `total` (or sends `hasMore` instead): the absence of a * total is preserved as null so pager "more" can fall back to its chunk * heuristic instead of concluding from a fabricated count. Classic pagination * keeps its historic default in applyResult(). * @param {any} json * @returns {PageResult} */ export declare function parseResult(json: any): PageResult; /** * Apply a global search locally: case- and accent-insensitive `contains` over * the scalar values of each row. The search stays a plain string with one * convention: `|` separates OR alternatives (`info|warn` matches rows with * "info" or "warn"), each alternative a literal substring — spaces have no * syntactic role. Alternatives are trimmed, so `info | warn` works; `|` is * reserved (no escaping in v1). A search with no usable alternative (empty, * blank or `|` only) matches everything. This is a convenient default for * client-side data, not a contract for server backends: `QueryState.search` * only means "the user asked for a global search", the server decides which * fields it covers. * @param {Array>} rows * @param {String} search * @returns {Array>} */ export declare function applySearch(rows: Array>, search: string): Array>; /** * Server-side data source (the assumed default path). * Each query is serialized and sent to the server. */ export declare class FetchDataSource { url: string; params: Record; serializeQuery: ((query: QueryState) => any) | undefined; parseResponse: ((response: any) => PageResult) | undefined; fetchOptions: RequestInit; cacheBust: boolean; /** * @param {String} url * @param {Object} [options] * @param {Record} [options.params] Extra constant HTTP params appended to each request * @param {(query: QueryState) => any} [options.serializeQuery] Defaults to identity (QueryState preserved) * @param {(response: any) => PageResult} [options.parseResponse] Defaults to parseResult * @param {RequestInit} [options.fetch] Standard options forwarded to `fetch()` * @param {Boolean} [options.cacheBust] Append a timestamp query parameter (disabled by default) */ constructor(url: string, { params, serializeQuery, parseResponse, fetch: fetchOptions, cacheBust }?: { params?: Record; serializeQuery?: (query: QueryState) => any; parseResponse?: (response: any) => PageResult; fetch?: RequestInit; cacheBust?: boolean; }); /** * @param {QueryState} query * @returns {URL} */ buildUrl(query: QueryState): URL; /** * @param {QueryState} query * @param {{signal?: AbortSignal}} [options] * @returns {Promise} */ load(query: QueryState, { signal }?: { signal?: AbortSignal; }): Promise; } /** * Client-side data source. The whole collection is owned in the browser and * QueryStates are applied locally. */ export declare class ArrayDataSource { rows: Record[]; /** * @param {Array>} [rows] */ constructor(rows?: Array>); /** * Create a local data source by fetching a static file once. * @param {String} url * @param {(response: any) => PageResult} [parseResponse] * @returns {Promise} */ static fromUrl(url: string, parseResponse?: (response: any) => PageResult): Promise; /** * @param {QueryState} query * @returns {Promise} */ load(query: QueryState): Promise; /** * @param {Record} row */ add(row: Record): void; /** * Remove the first row whose `key` field equals `value`. * The key is explicit: there is no magic "first field" fallback. * @param {any} value * @param {String} key Field to match * @returns {Boolean} Whether a row was removed */ remove(value: any, key: string): boolean; } //# sourceMappingURL=data-source.d.ts.map