@zakkster/lite-table 1.0.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.
- package/CHANGELOG.md +93 -0
- package/LICENSE.txt +21 -0
- package/README.md +802 -0
- package/Table.d.ts +227 -0
- package/Table.js +1586 -0
- package/llms.txt +262 -0
- package/package.json +71 -0
package/Table.d.ts
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @zakkster/lite-table - type declarations (M1)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { Signal, Computed } from "@zakkster/lite-signal";
|
|
6
|
+
import type { VirtualAxis, ScrollAlign } from "@zakkster/lite-virtual";
|
|
7
|
+
|
|
8
|
+
// --- Column types -----------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
export type PinSide = "left" | "none" | "right";
|
|
11
|
+
export type SortDir = "asc" | "desc";
|
|
12
|
+
|
|
13
|
+
export interface ColumnDef<Row = any, Value = unknown> {
|
|
14
|
+
/** Field key on the row. Used for `row[key]` access and cell ids. */
|
|
15
|
+
key: string;
|
|
16
|
+
/** Header label. Defaults to `key`. */
|
|
17
|
+
header?: string;
|
|
18
|
+
/** Initial pixel width. Default 120. */
|
|
19
|
+
width?: number;
|
|
20
|
+
/** Resize lower bound. Default 40. */
|
|
21
|
+
minWidth?: number;
|
|
22
|
+
/** Resize upper bound. Default 1600. */
|
|
23
|
+
maxWidth?: number;
|
|
24
|
+
/** Initially hidden. Default false. */
|
|
25
|
+
hidden?: boolean;
|
|
26
|
+
/** Initial pin side. Default "none". */
|
|
27
|
+
pin?: PinSide;
|
|
28
|
+
/** Initial flex weight. Default 0 (fixed width). Set > 0 to make the
|
|
29
|
+
* column share leftover horizontal space proportionally: it renders as
|
|
30
|
+
* `minmax(<minWidth>px, <flex>fr)`. When any column has flex > 0, the
|
|
31
|
+
* trailing 1fr filler is dropped so flex columns absorb the space. */
|
|
32
|
+
flex?: number;
|
|
33
|
+
/** Feature flags -- disable per-column interactions. All default true. */
|
|
34
|
+
sortable?: boolean;
|
|
35
|
+
resizable?: boolean;
|
|
36
|
+
pinnable?: boolean;
|
|
37
|
+
hideable?: boolean;
|
|
38
|
+
reorderable?: boolean;
|
|
39
|
+
/** Computed value override; if absent, `row[key]` is used. */
|
|
40
|
+
accessor?: (row: Row) => Value;
|
|
41
|
+
/** Sort comparator; default is null-safe number-or-string compare. */
|
|
42
|
+
compare?: (a: Value, b: Value) => number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Live reactive state for a column, exposed on `table.columns[i]`. */
|
|
46
|
+
export interface ColumnState<Row = any> {
|
|
47
|
+
readonly key: string;
|
|
48
|
+
readonly header: string;
|
|
49
|
+
readonly accessor: ((row: Row) => unknown) | null;
|
|
50
|
+
readonly compare: (a: unknown, b: unknown) => number;
|
|
51
|
+
readonly sortable: boolean;
|
|
52
|
+
readonly resizable: boolean;
|
|
53
|
+
readonly pinnable: boolean;
|
|
54
|
+
readonly hideable: boolean;
|
|
55
|
+
readonly reorderable: boolean;
|
|
56
|
+
readonly minWidth: number;
|
|
57
|
+
readonly maxWidth: number;
|
|
58
|
+
readonly width: Signal<number>;
|
|
59
|
+
readonly hidden: Signal<boolean>;
|
|
60
|
+
readonly pin: Signal<PinSide>;
|
|
61
|
+
readonly flex: Signal<number>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// --- Focus / sort / selection types -----------------------------------------
|
|
65
|
+
|
|
66
|
+
export interface FocusedCell {
|
|
67
|
+
rowId: string | number;
|
|
68
|
+
columnKey: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface SortEntry {
|
|
72
|
+
key: string;
|
|
73
|
+
dir: SortDir;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export type SelectMode = "set" | "add" | "toggle" | "range";
|
|
77
|
+
|
|
78
|
+
/** Selection state is a PREDICATE, not a list of IDs.
|
|
79
|
+
* - `mode: "whitelist"`: `set` contains the selected IDs.
|
|
80
|
+
* - `mode: "all"`: `set` is a blacklist -- every row is selected EXCEPT
|
|
81
|
+
* those in `set`. This makes Ctrl+A across 1M rows O(1) -- no walk of
|
|
82
|
+
* the row source, no per-ID allocation. Materialization (e.g. for
|
|
83
|
+
* Submit/Copy/Export) is on demand via `selectedIds` / `selectedRows`
|
|
84
|
+
* / `forEachSelected`. */
|
|
85
|
+
export interface SelectionState<RowId = string | number> {
|
|
86
|
+
mode: "whitelist" | "all";
|
|
87
|
+
set: Set<RowId>;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export type FocusDirection =
|
|
91
|
+
| "up" | "down" | "left" | "right"
|
|
92
|
+
| "home" | "end" | "rowStart" | "rowEnd"
|
|
93
|
+
| "pageUp" | "pageDown";
|
|
94
|
+
|
|
95
|
+
// --- Config -----------------------------------------------------------------
|
|
96
|
+
|
|
97
|
+
export interface CreateTableConfig<Row = any> {
|
|
98
|
+
rows: readonly Row[] | (() => readonly Row[]);
|
|
99
|
+
columns: readonly ColumnDef<Row>[];
|
|
100
|
+
getRowId: (row: Row) => string | number;
|
|
101
|
+
rowHeight?: number;
|
|
102
|
+
overscan?: number;
|
|
103
|
+
initialFocus?: FocusedCell | null;
|
|
104
|
+
initialSort?: readonly SortEntry[];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// --- Headless core ----------------------------------------------------------
|
|
108
|
+
|
|
109
|
+
export interface TableCore<Row = any> {
|
|
110
|
+
// ---- Static ----
|
|
111
|
+
readonly columns: readonly ColumnState<Row>[];
|
|
112
|
+
readonly rowHeight: number;
|
|
113
|
+
readonly overscan: number;
|
|
114
|
+
getRowId(row: Row): string | number;
|
|
115
|
+
cellId(rowId: string | number, columnKey: string): string;
|
|
116
|
+
|
|
117
|
+
// ---- Reactive: data ----
|
|
118
|
+
rowsGetter(): readonly Row[];
|
|
119
|
+
readonly visibleRows: Computed<readonly Row[]>;
|
|
120
|
+
readonly rowCount: Computed<number>;
|
|
121
|
+
|
|
122
|
+
// ---- Reactive: columns ----
|
|
123
|
+
readonly columnOrder: Signal<readonly string[]>;
|
|
124
|
+
readonly visibleColumns: Computed<readonly ColumnState<Row>[]>;
|
|
125
|
+
/** 0-indexed position of each column within `visibleColumns`. */
|
|
126
|
+
readonly displayIndexByKey: Computed<Map<string, number>>;
|
|
127
|
+
/** 1-indexed CSS grid-column placement for each column. Accounts for the
|
|
128
|
+
* 1fr filler that lives between unpinned and right-pinned columns so
|
|
129
|
+
* right-pinned cells sit flush against the right edge. */
|
|
130
|
+
readonly colPlacement: Computed<Map<string, number>>;
|
|
131
|
+
readonly colTemplate: Computed<string>;
|
|
132
|
+
readonly contentWidth: Computed<number>;
|
|
133
|
+
readonly leftOffsets: Computed<Map<string, number>>;
|
|
134
|
+
readonly rightOffsets: Computed<Map<string, number>>;
|
|
135
|
+
|
|
136
|
+
// ---- Reactive: sort / focus / selection ----
|
|
137
|
+
readonly sortChain: Signal<readonly SortEntry[]>;
|
|
138
|
+
readonly focusedCell: Signal<FocusedCell | null>;
|
|
139
|
+
/** Predicate-based selection state. Reading `.set.has(id)` directly is
|
|
140
|
+
* wrong in all-mode -- use `isSelected(id)` for membership. */
|
|
141
|
+
readonly selection: Signal<SelectionState>;
|
|
142
|
+
readonly selectionAnchor: Signal<string | number | null>;
|
|
143
|
+
/** Reactive O(1) selected count. In all-mode this is
|
|
144
|
+
* `rowCount() - blacklist.size`; otherwise `whitelist.size`. */
|
|
145
|
+
readonly selectedCount: Computed<number>;
|
|
146
|
+
|
|
147
|
+
// ---- Methods: sort ----
|
|
148
|
+
setSort(key: string, dir: SortDir | null): void;
|
|
149
|
+
addSort(key: string, dir: SortDir | null): void;
|
|
150
|
+
toggleSort(key: string, opts?: { additive?: boolean }): void;
|
|
151
|
+
clearSort(): void;
|
|
152
|
+
|
|
153
|
+
// ---- Methods: selection ----
|
|
154
|
+
selectRow(rowId: string | number, mode?: SelectMode): void;
|
|
155
|
+
selectRowRange(anchorId: string | number | null, targetId: string | number): void;
|
|
156
|
+
/** Select every row. O(1) -- flips to all-mode with an empty blacklist. */
|
|
157
|
+
selectAll(): void;
|
|
158
|
+
clearSelection(): void;
|
|
159
|
+
/** O(1) predicate. Handles both whitelist and all-mode transparently. */
|
|
160
|
+
isSelected(rowId: string | number): boolean;
|
|
161
|
+
/** Materialize the selected IDs. O(N) in the row source. Defaults to
|
|
162
|
+
* `visibleRows()`; pass a source for export against the unsorted master. */
|
|
163
|
+
selectedIds<R = Row>(source?: readonly R[]): (string | number)[];
|
|
164
|
+
/** Materialize the selected row objects. O(N) in the row source. */
|
|
165
|
+
selectedRows<R = Row>(source?: readonly R[]): R[];
|
|
166
|
+
/** Stream selected rows without materializing the full list. Returning
|
|
167
|
+
* `false` from `fn` stops iteration. Use this for CSV / network export. */
|
|
168
|
+
forEachSelected<R = Row>(
|
|
169
|
+
fn: (row: R, id: string | number, index: number) => boolean | void,
|
|
170
|
+
source?: readonly R[]
|
|
171
|
+
): void;
|
|
172
|
+
|
|
173
|
+
// ---- Methods: columns ----
|
|
174
|
+
setColumnWidth(key: string, w: number): void;
|
|
175
|
+
setColumnHidden(key: string, hidden: boolean): void;
|
|
176
|
+
setColumnPin(key: string, side: PinSide): void;
|
|
177
|
+
setColumnFlex(key: string, flex: number): void;
|
|
178
|
+
setColumnOrder(keys: readonly string[]): void;
|
|
179
|
+
moveColumn(fromKey: string, toKey: string, opts?: { before?: boolean }): void;
|
|
180
|
+
|
|
181
|
+
// ---- Methods: focus ----
|
|
182
|
+
moveFocus(direction: FocusDirection, opts?: { pageSize?: number }): void;
|
|
183
|
+
|
|
184
|
+
// ---- Lifecycle ----
|
|
185
|
+
dispose(): void;
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* @internal
|
|
189
|
+
* Reactive scope used internally to track signals/computeds/effects
|
|
190
|
+
* created by this table. Exposed so tests can register additional
|
|
191
|
+
* effects whose disposal is tied to `table.dispose()`. Not for
|
|
192
|
+
* application use -- the public surface above covers all intended
|
|
193
|
+
* patterns.
|
|
194
|
+
*/
|
|
195
|
+
readonly _scope: {
|
|
196
|
+
effect(fn: () => void): () => void;
|
|
197
|
+
onCleanup(fn: () => void): void;
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function createTable<Row = any>(
|
|
202
|
+
config: CreateTableConfig<Row>
|
|
203
|
+
): TableCore<Row>;
|
|
204
|
+
|
|
205
|
+
// --- DOM mount --------------------------------------------------------------
|
|
206
|
+
|
|
207
|
+
export interface MountOptions {
|
|
208
|
+
injectStyles?: boolean;
|
|
209
|
+
initialViewportHeight?: number;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export interface TableMount {
|
|
213
|
+
readonly root: HTMLElement;
|
|
214
|
+
readonly viewport: HTMLElement;
|
|
215
|
+
readonly axis: VirtualAxis;
|
|
216
|
+
scrollToIndex(index: number, align?: ScrollAlign): void;
|
|
217
|
+
poolSize(): number;
|
|
218
|
+
dispose(): void;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export function mountTable<Row = any>(
|
|
222
|
+
host: HTMLElement,
|
|
223
|
+
table: TableCore<Row>,
|
|
224
|
+
options?: MountOptions
|
|
225
|
+
): TableMount;
|
|
226
|
+
|
|
227
|
+
export function _resetStylesForTest(): void;
|