@svelte-lean/table 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,41 @@
1
+ import type { TableProps, RowId } from '../types.js';
2
+ declare function $$render<T>(): {
3
+ props: TableProps<T>;
4
+ exports: {
5
+ scrollTo: (config: {
6
+ key?: RowId;
7
+ index?: number;
8
+ top?: number;
9
+ offset?: number;
10
+ align?: ScrollLogicalPosition;
11
+ }) => void;
12
+ };
13
+ bindings: "state";
14
+ slots: {};
15
+ events: {};
16
+ };
17
+ declare class __sveltets_Render<T> {
18
+ props(): ReturnType<typeof $$render<T>>['props'];
19
+ events(): ReturnType<typeof $$render<T>>['events'];
20
+ slots(): ReturnType<typeof $$render<T>>['slots'];
21
+ bindings(): "state";
22
+ exports(): {
23
+ scrollTo: (config: {
24
+ key?: RowId;
25
+ index?: number;
26
+ top?: number;
27
+ offset?: number;
28
+ align?: ScrollLogicalPosition;
29
+ }) => void;
30
+ };
31
+ }
32
+ interface $$IsomorphicComponent {
33
+ new <T>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
34
+ $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
35
+ } & ReturnType<__sveltets_Render<T>['exports']>;
36
+ <T>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
37
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
38
+ }
39
+ declare const DataTable: $$IsomorphicComponent;
40
+ type DataTable<T> = InstanceType<typeof DataTable<T>>;
41
+ export default DataTable;
@@ -0,0 +1,4 @@
1
+ /** The previous or next data cell in the same row. */
2
+ export declare function horizontalCell(cell: HTMLTableCellElement, delta: 1 | -1): HTMLTableCellElement | undefined;
3
+ /** The nearest cell with the same `data-column` in a following or preceding row. */
4
+ export declare function verticalCell(cell: HTMLTableCellElement, delta: 1 | -1): HTMLTableCellElement | undefined;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Keyboard navigation over the rendered `<tbody>`. Cells are found by structure, not by counting
3
+ * columns, so spanning cells and detail rows do not shift the target.
4
+ */
5
+ function dataCells(row) {
6
+ return Array.from(row.children).filter((child) => child.matches('td[data-cell]'));
7
+ }
8
+ /** The previous or next data cell in the same row. */
9
+ export function horizontalCell(cell, delta) {
10
+ const row = cell.parentElement;
11
+ if (!row)
12
+ return undefined;
13
+ const cells = dataCells(row);
14
+ return cells[cells.indexOf(cell) + delta];
15
+ }
16
+ /** The nearest cell with the same `data-column` in a following or preceding row. */
17
+ export function verticalCell(cell, delta) {
18
+ const column = cell.dataset.column;
19
+ const sibling = delta > 0 ? 'nextElementSibling' : 'previousElementSibling';
20
+ for (let row = cell.parentElement?.[sibling]; row; row = row[sibling]) {
21
+ const target = dataCells(row).find((candidate) => candidate.dataset.column === column);
22
+ if (target)
23
+ return target;
24
+ }
25
+ return undefined;
26
+ }
@@ -0,0 +1,92 @@
1
+ import type { Column, FilterOperator, TableState, ModelContext, Pin, RowId, RowModel } from './types.js';
2
+ import { type HeaderCell } from './core.js';
3
+ /** A plain value or a getter. Getters keep the engine reactive to the caller's `$state`. */
4
+ export type MaybeGetter<T> = T | (() => T);
5
+ export type SelectionMode = false | 'single' | 'multiple';
6
+ export interface TableOptions<T> {
7
+ data: MaybeGetter<readonly T[]>;
8
+ columns: MaybeGetter<readonly Column<T>[]>;
9
+ getRowId: (row: T) => RowId;
10
+ getSubRows?: (row: T) => readonly T[] | undefined;
11
+ /**
12
+ * A plain object (or nothing) is the initial state of a table that owns its state.
13
+ * A getter makes the table controlled: it reads the caller's state and reports every
14
+ * change through `onStateChange` without storing anything itself.
15
+ */
16
+ state?: Partial<TableState> | (() => Partial<TableState>);
17
+ onStateChange?: (state: TableState) => void;
18
+ getRowModel?: (context: ModelContext<T>) => RowModel<T>;
19
+ manual?: MaybeGetter<boolean | undefined>;
20
+ rowCount?: MaybeGetter<number | undefined>;
21
+ /** Manual mode: the page count when the total is unknown. */
22
+ pageCount?: MaybeGetter<number | undefined>;
23
+ /** Replaces the searchable text of a row (default: visible searchable columns). */
24
+ searchText?: (row: T) => string;
25
+ locale?: MaybeGetter<string | undefined>;
26
+ /** `false` builds the model with every row on one page. Default `true`. */
27
+ pagination?: MaybeGetter<boolean | undefined>;
28
+ /** Default `'multiple'`. `false` makes every selection helper a no-op. */
29
+ selection?: MaybeGetter<SelectionMode | undefined>;
30
+ isRowSelectable?: (row: T) => boolean;
31
+ /** Rows that `selectAll()` and `allSelected` consider: the current page or every filtered row. */
32
+ selectionScope?: MaybeGetter<'page' | 'filtered' | undefined>;
33
+ }
34
+ export interface Table<T> {
35
+ readonly state: TableState;
36
+ readonly model: RowModel<T>;
37
+ /** Column definitions as given, including groups. */
38
+ readonly columns: readonly Column<T>[];
39
+ readonly leafColumns: Column<T>[];
40
+ /** Leaf columns that are not hidden, in pin zone and user order. */
41
+ readonly visibleColumns: Column<T>[];
42
+ readonly headers: HeaderCell<T>[][];
43
+ /** Ids of rows a selection control may act on, within `selectionScope`. */
44
+ readonly selectable: RowId[];
45
+ readonly allSelected: boolean;
46
+ readonly someSelected: boolean;
47
+ isSelected(id: RowId): boolean;
48
+ isExpanded(id: RowId): boolean;
49
+ /**
50
+ * The sort on a column id, or on a column: then a sort on one of its `sortKeys` counts too.
51
+ * `id` is the sort id that matched.
52
+ */
53
+ sortOf(column: string | Column<T>): {
54
+ id: string;
55
+ desc: boolean;
56
+ index: number;
57
+ } | undefined;
58
+ pinOf(column: Column<T>): Pin | undefined;
59
+ columnWidth(column: Column<T>): number;
60
+ /** Merges a patch into the state, stores it when the table owns its state, and reports it. */
61
+ setState(patch: Partial<TableState>): TableState;
62
+ /**
63
+ * Cycles none → ascending → descending → none (descending first for a column with
64
+ * `sortDescFirst`). `multi` keeps other sorted columns.
65
+ */
66
+ toggleSort(id: string, multi?: boolean): void;
67
+ /** Toggles one row; in `'single'` mode the row replaces the selection. */
68
+ toggleSelection(id: RowId, selected?: boolean): void;
69
+ /** Selects or clears many rows; defaults to `selectable` and to "select unless all are selected". */
70
+ selectAll(ids?: readonly RowId[], selected?: boolean): void;
71
+ /** Returns the new expanded state of the row. */
72
+ toggleExpanded(id: RowId, expanded?: boolean): boolean;
73
+ setSearch(search: string): void;
74
+ /** Replaces the filter for a column; `undefined` removes it. */
75
+ setFilter(id: string, value: unknown, operator?: FilterOperator): void;
76
+ setPage(index: number): void;
77
+ /** `0` shows every row on one page. Resets the page index. */
78
+ setPageSize(size: number): void;
79
+ /** Moves a visible column `delta` places within the visible order. */
80
+ moveColumn(id: string, delta: number): void;
81
+ /** Stores a width clamped to the column's bounds. */
82
+ resizeColumn(id: string, width: number): void;
83
+ pin(id: string, pin: Pin): void;
84
+ toggleColumn(id: string, visible?: boolean): void;
85
+ }
86
+ /**
87
+ * The table engine below `<DataTable />`: derives the row model, visible columns and headers from
88
+ * data, columns and state, and exposes the state transitions a renderer needs. It holds no DOM and
89
+ * no per-row objects beyond the row model. Every option is read lazily, so `data`, `columns` and
90
+ * `state` may be getters over the caller's `$state`.
91
+ */
92
+ export declare function createTable<T>(options: TableOptions<T>): Table<T>;
@@ -0,0 +1,157 @@
1
+ import { clampWidth, columnWidth, createRowModel, defaultState, headerRows, leafColumns, reorder, toggleId, sortOf, toggleSelection, toggleSort, visibleColumns } from './core.js';
2
+ function resolve(value) {
3
+ return typeof value === 'function' ? value() : value;
4
+ }
5
+ /**
6
+ * The table engine below `<DataTable />`: derives the row model, visible columns and headers from
7
+ * data, columns and state, and exposes the state transitions a renderer needs. It holds no DOM and
8
+ * no per-row objects beyond the row model. Every option is read lazily, so `data`, `columns` and
9
+ * `state` may be getters over the caller's `$state`.
10
+ */
11
+ export function createTable(options) {
12
+ const controlled = typeof options.state === 'function';
13
+ let owned = $state.raw(defaultState(controlled ? {} : options.state));
14
+ const state = $derived(controlled ? defaultState(options.state()) : owned);
15
+ const columns = $derived(resolve(options.columns));
16
+ const leaves = $derived(leafColumns(columns));
17
+ const visible = $derived(visibleColumns(columns, state));
18
+ const headers = $derived(headerRows(columns, visible));
19
+ const model = $derived.by(() => {
20
+ const paginated = resolve(options.pagination) ?? true;
21
+ const context = {
22
+ data: resolve(options.data),
23
+ columns,
24
+ state: paginated ? state : { ...state, pageIndex: 0, pageSize: 0 },
25
+ getRowId: options.getRowId,
26
+ getSubRows: options.getSubRows,
27
+ manual: resolve(options.manual) ?? false,
28
+ rowCount: resolve(options.rowCount),
29
+ pageCount: resolve(options.pageCount),
30
+ searchText: options.searchText,
31
+ locale: resolve(options.locale)
32
+ };
33
+ return (options.getRowModel ?? createRowModel)(context);
34
+ });
35
+ const selected = $derived(new Set(state.selection));
36
+ const expanded = $derived(new Set(state.expanded));
37
+ const mode = $derived(resolve(options.selection) ?? 'multiple');
38
+ const selectable = $derived.by(() => {
39
+ if (!mode)
40
+ return [];
41
+ const scope = resolve(options.selectionScope) ?? 'page';
42
+ const eligible = options.isRowSelectable ?? (() => true);
43
+ return (scope === 'filtered' ? model.all : model.rows)
44
+ .filter((node) => node.data !== undefined && eligible(node.data))
45
+ .map((node) => node.id);
46
+ });
47
+ const allSelected = $derived(selectable.length > 0 && selectable.every((id) => selected.has(id)));
48
+ const someSelected = $derived(selectable.some((id) => selected.has(id)));
49
+ function setState(patch) {
50
+ const next = { ...state, ...patch };
51
+ if (!controlled)
52
+ owned = next;
53
+ options.onStateChange?.(next);
54
+ return next;
55
+ }
56
+ return {
57
+ get state() {
58
+ return state;
59
+ },
60
+ get model() {
61
+ return model;
62
+ },
63
+ get columns() {
64
+ return columns;
65
+ },
66
+ get leafColumns() {
67
+ return leaves;
68
+ },
69
+ get visibleColumns() {
70
+ return visible;
71
+ },
72
+ get headers() {
73
+ return headers;
74
+ },
75
+ get selectable() {
76
+ return selectable;
77
+ },
78
+ get allSelected() {
79
+ return allSelected;
80
+ },
81
+ get someSelected() {
82
+ return someSelected;
83
+ },
84
+ isSelected: (id) => selected.has(id),
85
+ isExpanded: (id) => expanded.has(id),
86
+ sortOf: (column) => sortOf(state.sorting, typeof column === 'string' ? { id: column } : column),
87
+ pinOf: (column) => state.pinning[column.id] ?? column.pin,
88
+ columnWidth: (column) => columnWidth(column, state),
89
+ setState,
90
+ toggleSort(id, multi = false) {
91
+ const descFirst = leaves.find((leaf) => leaf.id === id)?.sortDescFirst;
92
+ setState({ sorting: toggleSort(state.sorting, id, multi, descFirst), pageIndex: 0 });
93
+ },
94
+ toggleSelection(id, value) {
95
+ if (!mode)
96
+ return;
97
+ if (mode === 'single') {
98
+ setState({ selection: value === false ? [] : [id] });
99
+ return;
100
+ }
101
+ const has = selected.has(id);
102
+ if ((value ?? !has) !== has)
103
+ setState({ selection: toggleId(state.selection, id) });
104
+ },
105
+ selectAll(ids = selectable, value) {
106
+ if (!mode)
107
+ return;
108
+ setState({ selection: toggleSelection(state.selection, [...ids], value) });
109
+ },
110
+ toggleExpanded(id, value) {
111
+ const has = expanded.has(id);
112
+ const next = value ?? !has;
113
+ if (next !== has)
114
+ setState({ expanded: toggleId(state.expanded, id) });
115
+ return next;
116
+ },
117
+ setSearch(search) {
118
+ setState({ search, pageIndex: 0 });
119
+ },
120
+ setFilter(id, value, operator) {
121
+ const others = state.filters.filter((filter) => filter.id !== id);
122
+ setState({
123
+ filters: value === undefined ? others : [...others, { id, value, operator }],
124
+ pageIndex: 0
125
+ });
126
+ },
127
+ setPage(index) {
128
+ setState({ pageIndex: Math.max(0, Math.min(Math.floor(index), model.pageCount - 1)) });
129
+ },
130
+ setPageSize(size) {
131
+ setState({ pageSize: Math.max(0, Math.floor(size)), pageIndex: 0 });
132
+ },
133
+ moveColumn(id, delta) {
134
+ const ids = visible.map((column) => column.id);
135
+ const from = ids.indexOf(id);
136
+ const target = from < 0 ? undefined : ids[from + delta];
137
+ if (target !== undefined)
138
+ setState({ order: reorder(ids, id, target) });
139
+ },
140
+ resizeColumn(id, width) {
141
+ const column = leaves.find((leaf) => leaf.id === id);
142
+ if (column)
143
+ setState({ widths: { ...state.widths, [id]: clampWidth(column, width) } });
144
+ },
145
+ pin(id, pin) {
146
+ setState({ pinning: { ...state.pinning, [id]: pin } });
147
+ },
148
+ toggleColumn(id, visible) {
149
+ const hidden = state.hidden.includes(id);
150
+ if ((visible ?? hidden) === !hidden)
151
+ return;
152
+ setState({
153
+ hidden: hidden ? state.hidden.filter((item) => item !== id) : [...state.hidden, id]
154
+ });
155
+ }
156
+ };
157
+ }
@@ -0,0 +1,317 @@
1
+ import type { Snippet } from 'svelte';
2
+ export type RowId = string | number;
3
+ export type Sort = {
4
+ id: string;
5
+ desc: boolean;
6
+ };
7
+ export type FilterOperator = 'contains' | 'equals' | 'startsWith' | 'gt' | 'gte' | 'lt' | 'lte' | 'between' | 'in' | 'empty';
8
+ export interface Filter {
9
+ id: string;
10
+ operator?: FilterOperator;
11
+ value: unknown;
12
+ }
13
+ export type Pin = 'left' | 'right' | false;
14
+ export type Aggregate = 'sum' | 'mean' | 'min' | 'max' | 'count' | ((values: unknown[]) => unknown);
15
+ export interface CellContext<T> {
16
+ row: T;
17
+ value: unknown;
18
+ rowId: RowId;
19
+ index: number;
20
+ column: Column<T>;
21
+ }
22
+ /**
23
+ * What a `headerCell` snippet receives: the column, its current sort and the transitions a custom
24
+ * sort control needs. `sort` also reports a sort on one of the column's `sortKeys`.
25
+ */
26
+ export interface HeaderContext<T> {
27
+ column: Column<T>;
28
+ sort: {
29
+ id: string;
30
+ desc: boolean;
31
+ index: number;
32
+ } | undefined;
33
+ sorting: Sort[];
34
+ /** The column's own cycle (the same as a click on the built-in sort button). */
35
+ toggleSort: (multi?: boolean) => void;
36
+ /** Replaces the whole sort state, for controls that sort by arbitrary ids. Resets the page. */
37
+ setSorting: (sorting: Sort[]) => void;
38
+ }
39
+ /** What the `empty` snippet receives, to tell "nothing matches" from "nothing here". */
40
+ export interface EmptyContext {
41
+ search: string;
42
+ /** `true` while a column filter holds a value. */
43
+ filtered: boolean;
44
+ loading: boolean;
45
+ }
46
+ /** Second argument of the `rowDetails` snippet. */
47
+ export interface RowDetailsContext {
48
+ rowId: RowId;
49
+ index: number;
50
+ collapse: () => void;
51
+ }
52
+ export interface CellSpan {
53
+ rows?: number;
54
+ columns?: number;
55
+ }
56
+ export interface Column<T> {
57
+ id: string;
58
+ header: string;
59
+ accessor?: keyof T | string | ((row: T) => unknown);
60
+ children?: Column<T>[];
61
+ width?: number;
62
+ minWidth?: number;
63
+ maxWidth?: number;
64
+ align?: 'start' | 'center' | 'end';
65
+ sortable?: boolean;
66
+ /** The first activation sorts descending: descending → ascending → none. */
67
+ sortDescFirst?: boolean;
68
+ /**
69
+ * Further sort ids that count as this column's sort for `aria-sort` and the sort icon, for a
70
+ * column whose `headerCell` sorts by several fields.
71
+ */
72
+ sortKeys?: string[];
73
+ compare?: (a: unknown, b: unknown, aRow: T, bRow: T) => number;
74
+ filterable?: boolean;
75
+ filter?: (value: unknown, filter: Filter, row: T) => boolean;
76
+ searchable?: boolean;
77
+ hideable?: boolean;
78
+ resizable?: boolean;
79
+ pin?: Pin;
80
+ /** Text of the cell. `null` or `''` renders the table's `placeholder`. */
81
+ format?: (value: unknown, row: T) => string | null;
82
+ cell?: Snippet<[CellContext<T>]>;
83
+ headerCell?: Snippet<[HeaderContext<T>]>;
84
+ editable?: boolean | ((row: T) => boolean);
85
+ editor?: 'text' | 'number' | 'date' | 'select';
86
+ options?: {
87
+ label: string;
88
+ value: string;
89
+ }[];
90
+ parse?: (value: string, row: T) => unknown;
91
+ validate?: (value: unknown, row: T) => string | undefined;
92
+ aggregate?: Aggregate;
93
+ span?: (context: CellContext<T>) => CellSpan | undefined;
94
+ ellipsis?: boolean | {
95
+ showTitle?: boolean;
96
+ };
97
+ /** Class of the body cells: a string, or one computed per row. */
98
+ class?: string | ((row: T) => string);
99
+ /** Class of the header cell. */
100
+ headerClass?: string;
101
+ }
102
+ export interface TableState {
103
+ sorting: Sort[];
104
+ filters: Filter[];
105
+ search: string;
106
+ pageIndex: number;
107
+ pageSize: number;
108
+ selection: RowId[];
109
+ expanded: RowId[];
110
+ hidden: string[];
111
+ order: string[];
112
+ widths: Record<string, number>;
113
+ pinning: Record<string, Pin>;
114
+ rowPinning: {
115
+ top: RowId[];
116
+ bottom: RowId[];
117
+ };
118
+ }
119
+ export interface RowNode<T> {
120
+ id: RowId;
121
+ data?: T;
122
+ depth: number;
123
+ children: RowNode<T>[];
124
+ kind: 'row' | 'group';
125
+ label?: string;
126
+ count?: number;
127
+ aggregates?: Record<string, unknown>;
128
+ }
129
+ export interface RowModel<T> {
130
+ rows: RowNode<T>[];
131
+ /** Filtered root/group count, used for pagination. */
132
+ total: number;
133
+ pageCount: number;
134
+ pageIndex: number;
135
+ /** Filtered data rows, including collapsed descendants. */
136
+ all: RowNode<T>[];
137
+ }
138
+ export interface ModelContext<T> {
139
+ data: readonly T[];
140
+ columns: readonly Column<T>[];
141
+ state: TableState;
142
+ getRowId: (row: T) => RowId;
143
+ getSubRows?: (row: T) => readonly T[] | undefined;
144
+ manual?: boolean;
145
+ rowCount?: number;
146
+ /** Manual mode: the page count when the total is unknown (`rowCount` absent). */
147
+ pageCount?: number;
148
+ /** Replaces the searchable text of a row (default: visible searchable columns). */
149
+ searchText?: (row: T) => string;
150
+ locale?: string;
151
+ }
152
+ export interface EditEvent<T> {
153
+ row: T;
154
+ rowId: RowId;
155
+ columnId: string;
156
+ previous: unknown;
157
+ value: unknown;
158
+ }
159
+ export interface CellPosition {
160
+ row: RowId;
161
+ column: string;
162
+ }
163
+ export interface CellRange {
164
+ start: CellPosition;
165
+ end: CellPosition;
166
+ }
167
+ export interface TableMessages {
168
+ search: string;
169
+ columns: string;
170
+ selectAll: string;
171
+ selectRow: string;
172
+ expand: string;
173
+ collapse: string;
174
+ previous: string;
175
+ next: string;
176
+ first: string;
177
+ last: string;
178
+ pageSize: string;
179
+ loading: string;
180
+ empty: string;
181
+ retry: string;
182
+ save: string;
183
+ cancel: string;
184
+ edit: string;
185
+ resize: string;
186
+ /** Column controls: move a column earlier/later in the order. */
187
+ moveLeft: string;
188
+ moveRight: string;
189
+ /** Row reordering buttons rendered when `onRowMove` is set. */
190
+ moveUp: string;
191
+ moveDown: string;
192
+ /** Accessible name of the column-controls pin select (`"Pin: <column>"`). */
193
+ pin: string;
194
+ unpin: string;
195
+ pinLeft: string;
196
+ pinRight: string;
197
+ /** Page-size option that shows every row (`pageSize: 0`). */
198
+ allRows: string;
199
+ /** Read to assistive technology before a column's multi-sort precedence number. */
200
+ sortOrder: string;
201
+ /** Visually hidden header text of the selection column. */
202
+ selectionHeader: string;
203
+ /** Visually hidden header text of the expand column (`expandColumn`). */
204
+ expandHeader: string;
205
+ /** Accessible name of a column filter input. */
206
+ filter: (column: string) => string;
207
+ /** Pagination status. `rows` is `undefined` when a manual table does not know its total. */
208
+ page: (page: number, pages: number, rows: number | undefined) => string;
209
+ /** Accessible name of a numbered page button. */
210
+ pageNumber: (page: number) => string;
211
+ selected: (count: number) => string;
212
+ }
213
+ export interface TableProps<T> {
214
+ data: readonly T[];
215
+ columns: readonly Column<T>[];
216
+ getRowId: (row: T) => RowId;
217
+ state?: Partial<TableState>;
218
+ onStateChange?: (state: TableState) => void;
219
+ getSubRows?: ModelContext<T>['getSubRows'];
220
+ getRowModel?: (context: ModelContext<T>) => RowModel<T>;
221
+ manual?: boolean;
222
+ rowCount?: number;
223
+ /**
224
+ * Manual mode: the number of pages when the total is unknown, e.g. `pageIndex + 2` while the
225
+ * server reports a next page. Without it the page count follows `rowCount`.
226
+ */
227
+ pageCount?: number;
228
+ /**
229
+ * `id` of the root element. With `selection: 'single'` it also names the row radios
230
+ * (`<id>-selection`) so they form one native radio group; without it they stay unnamed.
231
+ * Authored, never generated (SSR relationships).
232
+ */
233
+ id?: string;
234
+ label?: string;
235
+ caption?: string;
236
+ title?: Snippet<[RowModel<T>]>;
237
+ showHeader?: boolean;
238
+ tableLayout?: 'auto' | 'fixed';
239
+ searchable?: boolean;
240
+ /** Milliseconds between the last keystroke in the search box or a filter input and the state change. */
241
+ searchDelay?: number;
242
+ /** The text a search term is matched against, per row. Default: visible searchable columns. */
243
+ searchText?: (row: T) => string;
244
+ filters?: boolean;
245
+ columnControls?: boolean;
246
+ selection?: false | 'single' | 'multiple';
247
+ isRowSelectable?: (row: T) => boolean;
248
+ selectionScope?: 'page' | 'filtered';
249
+ hideSelectAll?: boolean;
250
+ /** Accessible name of a row in its selection, expand and move controls. Default: the row id. */
251
+ rowLabel?: (row: T) => string;
252
+ rowDetails?: Snippet<[T, RowDetailsContext]>;
253
+ /** Renders the expand toggle in its own leading column instead of the first data column. */
254
+ expandColumn?: boolean;
255
+ rowExpandable?: (row: T) => boolean;
256
+ expandRowByClick?: boolean;
257
+ indentSize?: number;
258
+ onExpand?: (expanded: boolean, row: T) => void;
259
+ onExpandedRowsChange?: (rows: RowId[]) => void;
260
+ loading?: boolean;
261
+ fetching?: boolean;
262
+ /** Skeleton rows drawn while `loading` and no row is present. */
263
+ skeletonRows?: number;
264
+ error?: string;
265
+ onRetry?: () => void;
266
+ empty?: Snippet<[EmptyContext]>;
267
+ toolbar?: Snippet<[RowModel<T>]>;
268
+ footer?: Snippet<[RowModel<T>]>;
269
+ summary?: Record<string, string | number>;
270
+ density?: 'compact' | 'comfortable' | 'spacious';
271
+ striped?: boolean;
272
+ bordered?: boolean;
273
+ /**
274
+ * Keeps the header row in view. With `height` it sticks inside the scroll region; without, it
275
+ * sticks to the page while the table fits its width (`--slean-table-sticky-top` offsets it below
276
+ * a fixed application bar) and inside the region once the table scrolls sideways.
277
+ */
278
+ stickyHeader?: boolean;
279
+ height?: string;
280
+ /**
281
+ * Progressive mounting for large pages. When at least this many new rows arrive after the table
282
+ * has mounted (a fetch, a page change), the first batch renders at once and the rest follow in
283
+ * slices of about one frame each, so the main thread never blocks on the whole set. `true` means
284
+ * 100. The first render (server output, hydration) is always complete. Default `false`.
285
+ */
286
+ progressive?: boolean | number;
287
+ pagination?: boolean;
288
+ /** Page sizes offered by the pager; the current size is always included. */
289
+ pageSizeOptions?: number[];
290
+ /** Text of an empty value (`null`, `undefined`, `''`, or `format` returning `null`/`''`). */
291
+ placeholder?: string;
292
+ rowHoverable?: boolean;
293
+ /**
294
+ * Text direction of the table. When absent the root carries no `dir` attribute and keyboard,
295
+ * resize and pinning follow the nearest `dir` attribute in the document.
296
+ */
297
+ dir?: 'ltr' | 'rtl';
298
+ locale?: string;
299
+ messages?: Partial<TableMessages>;
300
+ onEdit?: (event: EditEvent<T>) => void | Promise<void>;
301
+ /**
302
+ * Click on a row, outside its controls. Rows become focusable and answer Enter and Space unless
303
+ * the table has cell navigation (`onEdit`, `onCellRangeChange`).
304
+ */
305
+ /** `index` is the row's position on the current page, as in `CellContext`. */
306
+ onRowClick?: (row: T, event: MouseEvent | KeyboardEvent, index: number) => void;
307
+ onRowDoubleClick?: (row: T, event: MouseEvent) => void;
308
+ onRowContextMenu?: (row: T, event: MouseEvent) => void;
309
+ onScroll?: (event: Event) => void;
310
+ onAction?: (action: string, row: T, trigger: HTMLElement, index: number) => void;
311
+ rowClass?: (row: T, index: number) => string;
312
+ /** Controlled drag/reorder; disabled while sorting, filtering or using tree/group models. */
313
+ onRowMove?: (source: RowId, target: RowId) => void;
314
+ cellRange?: CellRange | null;
315
+ onCellRangeChange?: (range: CellRange | null) => void;
316
+ class?: string;
317
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};