rowpine 0.0.0-stage → 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/LICENSE.md +18 -0
- package/README.md +89 -2
- package/dist/build-info.d.ts +2 -0
- package/dist/csv.d.ts +14 -0
- package/dist/download.d.ts +4 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +13 -0
- package/dist/license.d.ts +32 -0
- package/dist/pdf/doc.d.ts +357 -0
- package/dist/pdf/index.d.ts +11 -0
- package/dist/pivot/designer.d.ts +90 -0
- package/dist/pivot/extent.d.ts +7 -0
- package/dist/pivot/pivot.d.ts +106 -0
- package/dist/pivot/tanstack.d.ts +65 -0
- package/dist/table-source.d.ts +147 -0
- package/dist/xlsx/index.d.ts +17 -0
- package/dist/xlsx/ooxml.d.ts +115 -0
- package/package.json +63 -4
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PDF document builder on pdfmake: page size / orientation / margins, a
|
|
3
|
+
* repeated header row on every page, "Page X of Y" + date footer, per-column
|
|
4
|
+
* alignment and widths, group and subtotal rows, an optional title /
|
|
5
|
+
* subtitle / logo, and header and zebra colors.
|
|
6
|
+
*
|
|
7
|
+
* `buildPdfDocDefinition` is a pure function (no pdfmake, no DOM) returning
|
|
8
|
+
* the docDefinition object; `exportPdf` (./index) feeds it to pdfmake.
|
|
9
|
+
*/
|
|
10
|
+
export type PdfPageSize = 'A4' | 'A3' | 'A5' | 'LETTER' | 'LEGAL';
|
|
11
|
+
/** The font files pdfmake reads, by name, as base64. */
|
|
12
|
+
export type PdfVirtualFileSystem = Record<string, string>;
|
|
13
|
+
/**
|
|
14
|
+
* The font map inside whatever `import('pdfmake/build/vfs_fonts')` returned.
|
|
15
|
+
*
|
|
16
|
+
* The file has changed shape across pdfmake releases and bundlers wrap it
|
|
17
|
+
* once more: `{ pdfMake: { vfs } }` (0.1.x), `{ vfs }`, either of those under
|
|
18
|
+
* `default`, and since 0.2.8 the module IS the map (`module.exports = vfs`,
|
|
19
|
+
* so an ESM interop hands it over as `default` with the `.ttf` names as
|
|
20
|
+
* keys). Only the last shape ships today, and it is the one the old lookup
|
|
21
|
+
* did not recognise, which left registration to a side effect of import
|
|
22
|
+
* order (see {@link registerPdfFonts}). Returns `null` when nothing in the
|
|
23
|
+
* module looks like a font map.
|
|
24
|
+
*/
|
|
25
|
+
export declare function resolvePdfVfs(mod: unknown): PdfVirtualFileSystem | null;
|
|
26
|
+
/** The parts of pdfmake's browser API that font registration touches. */
|
|
27
|
+
export type PdfMakeLike = {
|
|
28
|
+
vfs?: PdfVirtualFileSystem;
|
|
29
|
+
addVirtualFileSystem?: (vfs: PdfVirtualFileSystem) => void;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Hand the fonts to the pdfmake instance that will create the document,
|
|
33
|
+
* explicitly. `vfs_fonts` registers itself only as a side effect: at import
|
|
34
|
+
* time it looks for a global `pdfMake` with `addVirtualFileSystem` and calls
|
|
35
|
+
* it. Under a bundler that global is whichever instance happened to load
|
|
36
|
+
* first, and when a dev server re-optimises its dependencies mid-session
|
|
37
|
+
* that is a stale one, so the instance doing the export has no fonts and
|
|
38
|
+
* `createPdf` fails with "File 'Roboto-Medium.ttf' not found in virtual
|
|
39
|
+
* file system". Registering here, on the instance in hand, removes the
|
|
40
|
+
* dependence on import order: `addVirtualFileSystem` where the version has
|
|
41
|
+
* it (0.2.x, where `createPdf` reads that before the `vfs` property) and the
|
|
42
|
+
* `vfs` property as well, which older versions read. Returns whether a map
|
|
43
|
+
* was found to register.
|
|
44
|
+
*/
|
|
45
|
+
/**
|
|
46
|
+
* The PDF as a Blob from either pdfmake line. 0.2 hands it to a callback; 0.3
|
|
47
|
+
* made `getBlob` async and never calls one, so waiting on the callback alone
|
|
48
|
+
* hung every export once pnpm or yarn installed 0.3 for `pnpm add pdfmake`.
|
|
49
|
+
*/
|
|
50
|
+
export declare function pdfBlob(doc: {
|
|
51
|
+
getBlob(cb?: (b: Blob) => void): unknown;
|
|
52
|
+
}): Promise<Blob>;
|
|
53
|
+
export declare function registerPdfFonts(pdfMake: PdfMakeLike, vfsModule: unknown): boolean;
|
|
54
|
+
export type PdfExportOptions = {
|
|
55
|
+
pageSize?: PdfPageSize;
|
|
56
|
+
pageOrientation?: 'portrait' | 'landscape';
|
|
57
|
+
/** [left, top, right, bottom] in pt. Default [28, 34, 28, 34]. */
|
|
58
|
+
margins?: [number, number, number, number];
|
|
59
|
+
/** Document title rendered above the table. */
|
|
60
|
+
title?: string;
|
|
61
|
+
/** Smaller line under the title. */
|
|
62
|
+
subtitle?: string;
|
|
63
|
+
/** Logo as a data URL, rendered top-left above the title. */
|
|
64
|
+
logo?: string;
|
|
65
|
+
/** Logo width in pt (height auto). Default 90. */
|
|
66
|
+
logoWidth?: number;
|
|
67
|
+
/** Base font size in pt. Default 8. */
|
|
68
|
+
fontSize?: number;
|
|
69
|
+
/** Header row fill color. Default '#334155'. */
|
|
70
|
+
headerColor?: string;
|
|
71
|
+
/** Header row text color. Default '#ffffff'. */
|
|
72
|
+
headerTextColor?: string;
|
|
73
|
+
/** Zebra-stripe alternate rows. Default true. */
|
|
74
|
+
zebra?: boolean;
|
|
75
|
+
/** Zebra fill color. Default '#f1f5f9'. */
|
|
76
|
+
zebraColor?: string;
|
|
77
|
+
/** Repeat the header row on every page. Default true. */
|
|
78
|
+
repeatHeader?: boolean;
|
|
79
|
+
/** Show the "Page X of Y" + date footer. Default true. */
|
|
80
|
+
showPageNumbers?: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Column widths: '*' (fill, default), 'auto', or an explicit array of
|
|
83
|
+
* pt numbers / 'auto' / '*' per column.
|
|
84
|
+
*/
|
|
85
|
+
columnWidths?: '*' | 'auto' | Array<number | 'auto' | '*'>;
|
|
86
|
+
/** Switch to landscape automatically when column count exceeds this (and no
|
|
87
|
+
* explicit orientation was set). Default 8. */
|
|
88
|
+
autoLandscapeThreshold?: number;
|
|
89
|
+
/** Group header row fill color. Default '#e2e8f0'. */
|
|
90
|
+
groupColor?: string;
|
|
91
|
+
/** Group header row text color. Default '#0f172a'. */
|
|
92
|
+
groupTextColor?: string;
|
|
93
|
+
/** Subtotal row fill color. Default '#f8fafc'. */
|
|
94
|
+
subtotalColor?: string;
|
|
95
|
+
/**
|
|
96
|
+
* Charts to print with the table, as image data URLs, each with an
|
|
97
|
+
* optional title and caption. Printed at the content width unless `width`
|
|
98
|
+
* (pt) says otherwise.
|
|
99
|
+
*/
|
|
100
|
+
charts?: ReadonlyArray<PdfChartImage>;
|
|
101
|
+
/** Where the charts go: above the table (default) or below it. */
|
|
102
|
+
chartsPosition?: 'above' | 'below';
|
|
103
|
+
/**
|
|
104
|
+
* A strip of headline numbers above the table: a label, the value, and an
|
|
105
|
+
* optional delta line in its own colour ("+4.2%" in green).
|
|
106
|
+
*/
|
|
107
|
+
kpis?: ReadonlyArray<PdfKpi>;
|
|
108
|
+
/** KPI box fill. Default '#f8fafc'. */
|
|
109
|
+
kpiColor?: string;
|
|
110
|
+
/** Body text colour. Default black. */
|
|
111
|
+
textColor?: string;
|
|
112
|
+
/** Table line colour. Default '#e2e8f0'. */
|
|
113
|
+
borderColor?: string;
|
|
114
|
+
/**
|
|
115
|
+
* What to do when the columns do not fit across the page.
|
|
116
|
+
* - `'auto'` (default): shrink the table text, down to three quarters of
|
|
117
|
+
* `fontSize`; past that, split the columns over several pages.
|
|
118
|
+
* - `'shrink'`: only shrink, down to 4 pt.
|
|
119
|
+
* - `'split'`: only split.
|
|
120
|
+
* - `'none'`: leave it to pdfmake, which lets a wide table run off the page.
|
|
121
|
+
*/
|
|
122
|
+
fit?: 'auto' | 'shrink' | 'split' | 'none';
|
|
123
|
+
/** When the columns are split over pages, how many leading columns repeat on every page. Default 1. */
|
|
124
|
+
repeatColumns?: number;
|
|
125
|
+
/** A grouped export ends with a total row over every row. Default true. */
|
|
126
|
+
grandTotal?: boolean;
|
|
127
|
+
/**
|
|
128
|
+
* The words the PDF prints itself, for another language or house style:
|
|
129
|
+
* the page footer, the footer date, a group's subtotal label, and the total
|
|
130
|
+
* row's label.
|
|
131
|
+
*/
|
|
132
|
+
labels?: {
|
|
133
|
+
page?: (page: number, pages: number) => string;
|
|
134
|
+
date?: (now: Date) => string;
|
|
135
|
+
subtotal?: (group: string) => string;
|
|
136
|
+
total?: string;
|
|
137
|
+
};
|
|
138
|
+
/** Text printed large and faint across every page: "DRAFT", "CONFIDENTIAL". */
|
|
139
|
+
watermark?: string | {
|
|
140
|
+
text: string;
|
|
141
|
+
color?: string;
|
|
142
|
+
opacity?: number;
|
|
143
|
+
};
|
|
144
|
+
/** Author recorded in the PDF's document properties. */
|
|
145
|
+
author?: string;
|
|
146
|
+
/**
|
|
147
|
+
* A font to print with instead of Roboto, which has no Chinese, Japanese,
|
|
148
|
+
* Korean, Arabic, Hebrew, Thai or Indic characters (they print as empty
|
|
149
|
+
* boxes). Each file is the font's base64 data or a URL pdfmake can fetch.
|
|
150
|
+
* It must cover Latin too: the whole document uses it.
|
|
151
|
+
*/
|
|
152
|
+
font?: PdfFont;
|
|
153
|
+
};
|
|
154
|
+
/** A TrueType / OpenType font for the PDF. Missing styles fall back to `normal`. */
|
|
155
|
+
export type PdfFont = {
|
|
156
|
+
name: string;
|
|
157
|
+
normal: string;
|
|
158
|
+
bold?: string;
|
|
159
|
+
italics?: string;
|
|
160
|
+
bolditalics?: string;
|
|
161
|
+
};
|
|
162
|
+
/**
|
|
163
|
+
* Register `font` with pdfmake next to Roboto. Base64 files go into the
|
|
164
|
+
* virtual file system, merged with what is there (`addVirtualFileSystem`
|
|
165
|
+
* replaces the map rather than adding to it); URLs are passed through for
|
|
166
|
+
* pdfmake to fetch.
|
|
167
|
+
*/
|
|
168
|
+
export declare function registerPdfFont(pdfMake: PdfMakeLike & {
|
|
169
|
+
fonts?: Record<string, unknown>;
|
|
170
|
+
addFonts?: (fonts: Record<string, unknown>) => void;
|
|
171
|
+
}, font: PdfFont): void;
|
|
172
|
+
/**
|
|
173
|
+
* Text Roboto has no glyphs for: CJK, Hangul, Arabic, Hebrew, Thai, the Indic
|
|
174
|
+
* scripts, dingbats (check marks, stars) and emoji. Exported so an export can
|
|
175
|
+
* warn that it needs `pdf.font`.
|
|
176
|
+
*/
|
|
177
|
+
export declare const PDF_ROBOTO_MISSING: RegExp;
|
|
178
|
+
/** One cell's look, the same keys `ExportCellStyle` has. */
|
|
179
|
+
export type PdfCellStyle = {
|
|
180
|
+
color?: string;
|
|
181
|
+
backgroundColor?: string;
|
|
182
|
+
fontWeight?: 'normal' | 'bold' | number;
|
|
183
|
+
fontStyle?: 'normal' | 'italic';
|
|
184
|
+
fontSize?: number | string;
|
|
185
|
+
textAlign?: 'left' | 'right' | 'center';
|
|
186
|
+
};
|
|
187
|
+
/** Blanket styles for the PDF table: what `ExportOptions.styles` carries. */
|
|
188
|
+
export type PdfStyles = {
|
|
189
|
+
headerRow?: PdfCellStyle;
|
|
190
|
+
rows?: PdfCellStyle;
|
|
191
|
+
rowAlternate?: PdfCellStyle;
|
|
192
|
+
/** Per-cell overrides by Excel-style reference: `A1` is the first header cell, `A2` the first data cell. */
|
|
193
|
+
cells?: Record<string, PdfCellStyle>;
|
|
194
|
+
};
|
|
195
|
+
/** A line above or below the table: what `ExportOptions.header` / `footer` carry. */
|
|
196
|
+
export type PdfPageLine = {
|
|
197
|
+
text: string;
|
|
198
|
+
style?: PdfCellStyle;
|
|
199
|
+
} | {
|
|
200
|
+
image: string;
|
|
201
|
+
width?: number;
|
|
202
|
+
height?: number;
|
|
203
|
+
} | {
|
|
204
|
+
left?: string;
|
|
205
|
+
center?: string;
|
|
206
|
+
right?: string;
|
|
207
|
+
};
|
|
208
|
+
/** A merged block of data cells, zero-based within the data rows. */
|
|
209
|
+
export type PdfMerge = {
|
|
210
|
+
row: number;
|
|
211
|
+
col: number;
|
|
212
|
+
rowSpan?: number;
|
|
213
|
+
colSpan?: number;
|
|
214
|
+
};
|
|
215
|
+
/** A color theme for the PDF table; explicit options and `styles` win over it. */
|
|
216
|
+
export type PdfTheme = Partial<Pick<PdfExportOptions, 'headerColor' | 'headerTextColor' | 'textColor' | 'zebraColor' | 'borderColor' | 'groupColor' | 'groupTextColor' | 'subtotalColor'>>;
|
|
217
|
+
/** `rgb(...)` / `rgba(...)` / `#hex` -> `#rrggbb`, or null for transparent and anything else. */
|
|
218
|
+
export declare function cssColorToHex(value: string | null | undefined): string | null;
|
|
219
|
+
/** Relative luminance of a `#rrggbb`, 0 (black) to 1 (white). */
|
|
220
|
+
export declare function hexLuminance(hex: string): number;
|
|
221
|
+
/** One box of the KPI strip. */
|
|
222
|
+
export type PdfKpi = {
|
|
223
|
+
label: string;
|
|
224
|
+
value: string;
|
|
225
|
+
/** A second line under the value: a change, a target, a period. */
|
|
226
|
+
delta?: string;
|
|
227
|
+
/** Colour of the delta line. Default '#64748b'. */
|
|
228
|
+
color?: string;
|
|
229
|
+
};
|
|
230
|
+
/** A chart whose image is ready: what `buildPdfDocDefinition` consumes. */
|
|
231
|
+
export type PdfChartImage = {
|
|
232
|
+
image: string;
|
|
233
|
+
title?: string;
|
|
234
|
+
caption?: string;
|
|
235
|
+
width?: number;
|
|
236
|
+
};
|
|
237
|
+
/**
|
|
238
|
+
* A body row for the PDF table. `data` is a normal row; `group` is a bold,
|
|
239
|
+
* full-width group header (from grid grouping); `subtotal` is a per-group
|
|
240
|
+
* summary row (bold, aligned like the data).
|
|
241
|
+
*/
|
|
242
|
+
export type PdfBodyRow = {
|
|
243
|
+
kind: 'data';
|
|
244
|
+
cells: ReadonlyArray<string>; /** The row's index in the exported rows. */
|
|
245
|
+
source?: number;
|
|
246
|
+
} | {
|
|
247
|
+
kind: 'group';
|
|
248
|
+
label: string;
|
|
249
|
+
level?: number;
|
|
250
|
+
} | {
|
|
251
|
+
kind: 'subtotal';
|
|
252
|
+
cells: ReadonlyArray<string>;
|
|
253
|
+
};
|
|
254
|
+
/** A table's look, as data; {@link hydratePdfDoc} turns it into pdfmake's layout callbacks. */
|
|
255
|
+
export type PdfLayoutSpec = {
|
|
256
|
+
kind: 'table';
|
|
257
|
+
stripes: number[];
|
|
258
|
+
zebraColor: string;
|
|
259
|
+
lineColor: string;
|
|
260
|
+
} | {
|
|
261
|
+
kind: 'kpi';
|
|
262
|
+
};
|
|
263
|
+
/** The document as plain data, safe to post to a worker. */
|
|
264
|
+
export type PdfDocData = {
|
|
265
|
+
pageSize: PdfPageSize;
|
|
266
|
+
pageOrientation: 'portrait' | 'landscape';
|
|
267
|
+
pageMargins: [number, number, number, number];
|
|
268
|
+
defaultStyle: {
|
|
269
|
+
fontSize: number;
|
|
270
|
+
font?: string;
|
|
271
|
+
};
|
|
272
|
+
content: unknown[];
|
|
273
|
+
watermark?: {
|
|
274
|
+
text: string;
|
|
275
|
+
color: string;
|
|
276
|
+
opacity: number;
|
|
277
|
+
bold: boolean;
|
|
278
|
+
};
|
|
279
|
+
/** The PDF's document properties, which a viewer shows in place of the file name. */
|
|
280
|
+
info?: {
|
|
281
|
+
title?: string;
|
|
282
|
+
author?: string;
|
|
283
|
+
subject?: string;
|
|
284
|
+
creator: string;
|
|
285
|
+
creationDate: Date;
|
|
286
|
+
};
|
|
287
|
+
/** The footer, as data: margins, the date text, and a page template with `{page}` / `{pages}`. */
|
|
288
|
+
rpFooter?: {
|
|
289
|
+
margin: number[];
|
|
290
|
+
date: string;
|
|
291
|
+
page: string;
|
|
292
|
+
};
|
|
293
|
+
};
|
|
294
|
+
/** The document pdfmake renders: the data with its layouts and footer as functions. */
|
|
295
|
+
export type PdfDocDefinition = PdfDocData & {
|
|
296
|
+
footer?: (currentPage: number, pageCount: number) => unknown;
|
|
297
|
+
};
|
|
298
|
+
/** What the PDF table is built from. `now` is injected for a deterministic footer date in tests. */
|
|
299
|
+
export type PdfDocParams = {
|
|
300
|
+
columns: ReadonlyArray<{
|
|
301
|
+
header: string;
|
|
302
|
+
align?: 'left' | 'center' | 'right';
|
|
303
|
+
}>;
|
|
304
|
+
/** Data rows as pre-formatted strings, in column order (no header row).
|
|
305
|
+
* Convenience for the flat (ungrouped) case. */
|
|
306
|
+
rows?: ReadonlyArray<ReadonlyArray<string>>;
|
|
307
|
+
/** Structured body with group headers + subtotals. Takes precedence over
|
|
308
|
+
* `rows` when provided (used for grouped exports). */
|
|
309
|
+
body?: ReadonlyArray<PdfBodyRow>;
|
|
310
|
+
/** Per-data-cell visual override (conditional formatting). `dataRowIdx` is
|
|
311
|
+
* the 0-based index among data rows. */
|
|
312
|
+
dataCellStyle?: (dataRowIdx: number, colIdx: number) => {
|
|
313
|
+
fill?: string;
|
|
314
|
+
color?: string;
|
|
315
|
+
bold?: boolean;
|
|
316
|
+
icon?: string;
|
|
317
|
+
iconOnly?: boolean;
|
|
318
|
+
} | undefined;
|
|
319
|
+
/** Per-data-cell hyperlink URL. */
|
|
320
|
+
dataCellLink?: (dataRowIdx: number, colIdx: number) => string | undefined;
|
|
321
|
+
opts?: PdfExportOptions;
|
|
322
|
+
/** Chart images; take precedence over `opts.charts`. */
|
|
323
|
+
charts?: ReadonlyArray<PdfChartImage>;
|
|
324
|
+
/** Blanket header / row / zebra / per-cell styles. */
|
|
325
|
+
styles?: PdfStyles;
|
|
326
|
+
/** A color theme; `opts` and `styles` win over it. */
|
|
327
|
+
theme?: PdfTheme;
|
|
328
|
+
/** Lines printed above the table (`ExportOptions.header`). */
|
|
329
|
+
headerLines?: ReadonlyArray<PdfPageLine>;
|
|
330
|
+
/** Lines printed below the table (`ExportOptions.footer`). */
|
|
331
|
+
footerLines?: ReadonlyArray<PdfPageLine>;
|
|
332
|
+
/** Merged data cells; ignored with a structured `body`, whose rows own the layout. */
|
|
333
|
+
merges?: ReadonlyArray<PdfMerge>;
|
|
334
|
+
now?: Date;
|
|
335
|
+
};
|
|
336
|
+
/**
|
|
337
|
+
* Build the pdfmake document from resolved columns + formatted string rows,
|
|
338
|
+
* as plain data: no functions, so it can be posted to a worker. Hand it to
|
|
339
|
+
* {@link hydratePdfDoc} before pdfmake sees it, or use
|
|
340
|
+
* {@link buildPdfDocDefinition}, which does both.
|
|
341
|
+
*/
|
|
342
|
+
export declare function buildPdfDocData(params: PdfDocParams): PdfDocData;
|
|
343
|
+
/**
|
|
344
|
+
* Build the pdfmake document for a table and hand it over ready to render.
|
|
345
|
+
* Same as {@link buildPdfDocData}, with its layouts and footer turned into
|
|
346
|
+
* the functions pdfmake calls.
|
|
347
|
+
*/
|
|
348
|
+
export declare function buildPdfDocDefinition(params: PdfDocParams): PdfDocDefinition;
|
|
349
|
+
/**
|
|
350
|
+
* Give a document from {@link buildPdfDocData} the functions pdfmake calls:
|
|
351
|
+
* each table's fill and line callbacks and the page footer. Runs where the
|
|
352
|
+
* PDF is rendered, which may be a worker the functions could not be sent to.
|
|
353
|
+
* `page` overrides the footer's page template with the caller's own function.
|
|
354
|
+
*/
|
|
355
|
+
export declare function hydratePdfDoc(doc: PdfDocData, opts?: {
|
|
356
|
+
page?: (page: number, pages: number) => string;
|
|
357
|
+
}): PdfDocDefinition;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type ReadOptions, type Sheet, type TableLike } from '../table-source';
|
|
2
|
+
import { type PdfDocParams, type PdfExportOptions } from './doc';
|
|
3
|
+
export type PdfTableExportOptions = ReadOptions & PdfExportOptions & {
|
|
4
|
+
fileName?: string;
|
|
5
|
+
/** Save the file in the browser. Default true. */
|
|
6
|
+
download?: boolean;
|
|
7
|
+
};
|
|
8
|
+
/** The pdfmake document parameters for a sheet. Pure. */
|
|
9
|
+
export declare function sheetToPdfParams(sheet: Sheet, opts?: PdfTableExportOptions, notes?: string[], now?: Date): PdfDocParams;
|
|
10
|
+
/** Export the table to a PDF file. Returns the file as a Blob. */
|
|
11
|
+
export declare function exportPdf(table: TableLike, opts?: PdfTableExportOptions): Promise<Blob>;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pivot designer state: the four wells (rows, columns, values, filters), the
|
|
3
|
+
* rules for moving fields between them, filters, totals switches and presets.
|
|
4
|
+
*
|
|
5
|
+
* Every operation is a pure function from one layout to the next, so any UI
|
|
6
|
+
* (the shadcn `PivotDesigner`, a custom one, a saved-view store) can drive it
|
|
7
|
+
* and persist the layout as plain JSON.
|
|
8
|
+
*/
|
|
9
|
+
import { type PivotTable, type ToColumnsOptions } from './tanstack';
|
|
10
|
+
import type { PivotAggregatorId, PivotConfig } from './pivot';
|
|
11
|
+
/** One field the designer offers. Dimensions land in Rows by default; measures in Values. */
|
|
12
|
+
export type PivotField<T = Record<string, unknown>> = {
|
|
13
|
+
field: keyof T & string;
|
|
14
|
+
/** Name shown in the field list and on chips. */
|
|
15
|
+
label: string;
|
|
16
|
+
kind: 'dimension' | 'measure';
|
|
17
|
+
/** Optional group heading in the field list, e.g. 'Customer'. */
|
|
18
|
+
group?: string;
|
|
19
|
+
/** Display text for this field's values when it is a measure. */
|
|
20
|
+
format?: (value: unknown) => string;
|
|
21
|
+
/** Aggregation used when the field is added to Values. Default 'sum' for measures. */
|
|
22
|
+
defaultAgg?: PivotAggregatorId;
|
|
23
|
+
};
|
|
24
|
+
/** A chip in the Values well. */
|
|
25
|
+
export type PivotValueChip = {
|
|
26
|
+
field: string;
|
|
27
|
+
agg: PivotAggregatorId;
|
|
28
|
+
label?: string;
|
|
29
|
+
};
|
|
30
|
+
/** A chip in the Filters well. `allowed: null` lets every value through. */
|
|
31
|
+
export type PivotFilterChip = {
|
|
32
|
+
field: string;
|
|
33
|
+
allowed: string[] | null;
|
|
34
|
+
};
|
|
35
|
+
/** The whole designer state, as plain JSON. */
|
|
36
|
+
export type PivotLayout = {
|
|
37
|
+
rows: string[];
|
|
38
|
+
cols: string[];
|
|
39
|
+
values: PivotValueChip[];
|
|
40
|
+
filters: PivotFilterChip[];
|
|
41
|
+
/** Hide subtotal rows between groups. */
|
|
42
|
+
hideSubtotals?: boolean;
|
|
43
|
+
/** Hide the grand-total row and column. */
|
|
44
|
+
hideGrandTotals?: boolean;
|
|
45
|
+
};
|
|
46
|
+
/** A saved layout offered in the designer's Presets menu. */
|
|
47
|
+
export type PivotPreset = {
|
|
48
|
+
name: string;
|
|
49
|
+
layout: PivotLayout;
|
|
50
|
+
};
|
|
51
|
+
export type Well = 'rows' | 'cols' | 'values' | 'filters';
|
|
52
|
+
export declare const WELL_LABEL: Record<Well, string>;
|
|
53
|
+
/** Which wells each kind of field may go to. */
|
|
54
|
+
export declare const KIND_WELLS: Record<PivotField['kind'], Well[]>;
|
|
55
|
+
export declare const ALL_AGGREGATORS: PivotAggregatorId[];
|
|
56
|
+
export declare const AGG_LABEL: Record<PivotAggregatorId, string>;
|
|
57
|
+
export declare const EMPTY_LAYOUT: PivotLayout;
|
|
58
|
+
/** First dimension in Rows, first measure in Values. */
|
|
59
|
+
export declare function defaultLayoutFor<T>(fields: ReadonlyArray<PivotField<T>>): PivotLayout;
|
|
60
|
+
/** The well a field goes to when added without one. */
|
|
61
|
+
export declare function defaultWellFor(field: Pick<PivotField, 'kind'>): Well;
|
|
62
|
+
export declare function isFieldInLayout(layout: PivotLayout, field: string): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Add a field to a well at `index` (default: the end). In Rows, Columns and
|
|
65
|
+
* Filters a field appears at most once, so adding it moves it. In Values the
|
|
66
|
+
* same field may appear with different aggregations (sum and average of one
|
|
67
|
+
* amount), but not twice with the same one. `sourceIndex` is the chip's
|
|
68
|
+
* position in Values when it is being moved out of that well.
|
|
69
|
+
*/
|
|
70
|
+
export declare function addToWell<T>(layout: PivotLayout, fields: ReadonlyArray<PivotField<T>>, field: string, well: Well, index?: number, sourceIndex?: number): PivotLayout;
|
|
71
|
+
/** Remove a field from a well. In Values, `valueIndex` picks the one chip. */
|
|
72
|
+
export declare function removeFromWell(layout: PivotLayout, field: string, well: Well, valueIndex?: number): PivotLayout;
|
|
73
|
+
/** Field-list checkbox: take the field out of every well, or add it to its default well. */
|
|
74
|
+
export declare function toggleField<T>(layout: PivotLayout, fields: ReadonlyArray<PivotField<T>>, field: string): PivotLayout;
|
|
75
|
+
/** Change a value chip's aggregation. A change that would duplicate another chip removes this one. */
|
|
76
|
+
export declare function setAggregatorAt<T>(layout: PivotLayout, fields: ReadonlyArray<PivotField<T>>, index: number, agg: PivotAggregatorId): PivotLayout;
|
|
77
|
+
/** Set which values of a filter field pass. `null` lets everything through. */
|
|
78
|
+
export declare function setFilterAllowed(layout: PivotLayout, field: string, allowed: string[] | null): PivotLayout;
|
|
79
|
+
export declare function clearWell(layout: PivotLayout, well: Well): PivotLayout;
|
|
80
|
+
/** Every distinct value of a field in the full data, as text, sorted. */
|
|
81
|
+
export declare function distinctValues<T>(data: ReadonlyArray<T>, field: string): string[];
|
|
82
|
+
/** The rows that pass the layout's filters. */
|
|
83
|
+
export declare function applyLayoutFilters<T>(data: ReadonlyArray<T>, layout: PivotLayout): T[];
|
|
84
|
+
/** The engine config for a layout. */
|
|
85
|
+
export declare function layoutToConfig<T>(layout: PivotLayout, fields: ReadonlyArray<PivotField<T>>): PivotConfig<T>;
|
|
86
|
+
/**
|
|
87
|
+
* Filter, pivot and label in one step. Returns null when the layout has no
|
|
88
|
+
* values yet, which a UI shows as an empty state.
|
|
89
|
+
*/
|
|
90
|
+
export declare function pivotFromLayout<T>(data: ReadonlyArray<T>, layout: PivotLayout, fields: ReadonlyArray<PivotField<T>>, opts?: ToColumnsOptions): PivotTable | null;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The smallest and largest of a list of any length. `Math.max(...values)` passes
|
|
3
|
+
* each value as an argument and throws a RangeError past about 120,000 of them.
|
|
4
|
+
* Like `Math.max`: an empty list gives the start value, and a NaN gives NaN.
|
|
5
|
+
*/
|
|
6
|
+
export declare function maxOf(values: Iterable<number>, start?: number): number;
|
|
7
|
+
export declare function minOf(values: Iterable<number>, start?: number): number;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/** Built-in pivot measures, named in {@link PivotValueConfig}. */
|
|
2
|
+
export type PivotAggregatorId = 'sum' | 'avg' | 'min' | 'max' | 'count' | 'countDistinct' | 'first' | 'last';
|
|
3
|
+
/** A custom measure: reduce the values under one cell to the number to display. */
|
|
4
|
+
export type PivotAggregator = (values: ReadonlyArray<unknown>) => unknown;
|
|
5
|
+
/** One measure: the field to aggregate and how to aggregate it. */
|
|
6
|
+
export type PivotValueConfig<TData> = {
|
|
7
|
+
/** Row field whose values get aggregated. */
|
|
8
|
+
field: keyof TData & string;
|
|
9
|
+
/** Built-in aggregator id, or a custom reducer. */
|
|
10
|
+
agg: PivotAggregatorId | PivotAggregator;
|
|
11
|
+
/** Display name in the column header. Defaults to `${field} (${agg})`. */
|
|
12
|
+
label?: string;
|
|
13
|
+
/** Turns a value cell into display text. Default: numbers with grouping separators. */
|
|
14
|
+
format?: (value: unknown) => string;
|
|
15
|
+
};
|
|
16
|
+
/** A pivot definition: the row axis, the column axis, and the measures at their intersections. */
|
|
17
|
+
export type PivotConfig<TData> = {
|
|
18
|
+
/** Outer-most first. Each entry becomes one level of row grouping. */
|
|
19
|
+
rows: ReadonlyArray<keyof TData & string>;
|
|
20
|
+
/** Outer-most first. Each entry becomes one level of column grouping. */
|
|
21
|
+
cols: ReadonlyArray<keyof TData & string>;
|
|
22
|
+
/** One or more measures aggregated under each column-axis leaf. */
|
|
23
|
+
values: ReadonlyArray<PivotValueConfig<TData>>;
|
|
24
|
+
/** Grand-total row at the bottom. Default `true`. */
|
|
25
|
+
grandTotalRow?: boolean;
|
|
26
|
+
/** Grand-total column on the right. Default `true`. */
|
|
27
|
+
grandTotalCol?: boolean;
|
|
28
|
+
/** Subtotal rows between row groups. Default `true`. */
|
|
29
|
+
rowSubtotals?: boolean;
|
|
30
|
+
/** Optional sort for column-axis values per dim level (defaults to alpha). */
|
|
31
|
+
colSort?: (a: unknown, b: unknown, level: number) => number;
|
|
32
|
+
/** Optional sort for row-axis values per dim level (defaults to alpha). */
|
|
33
|
+
rowSort?: (a: unknown, b: unknown, level: number) => number;
|
|
34
|
+
};
|
|
35
|
+
/** What a pivot row represents - a group header, a subtotal, a leaf, or the grand total. */
|
|
36
|
+
export type PivotRowKind = 'group' | 'subtotal' | 'leaf' | 'grandTotal';
|
|
37
|
+
/**
|
|
38
|
+
* Shape of one entry in `result.rows`. The first-column label lives at
|
|
39
|
+
* `__pivotLabel`; every value cell lives at the column id matching the
|
|
40
|
+
* leaf column generated for its (col-path × measure).
|
|
41
|
+
*
|
|
42
|
+
* `__pivotParentId` is the `__pivotId` of the row's row-axis parent
|
|
43
|
+
* (or `null` for top-level groups, the grand-total row, and the one
|
|
44
|
+
* synthetic "All" row when no row dims are configured). Use it with
|
|
45
|
+
* `filterCollapsedPivotRows` to hide leaves whose ancestor is
|
|
46
|
+
* collapsed - that's how expandable pivots are built on top of the
|
|
47
|
+
* model.
|
|
48
|
+
*
|
|
49
|
+
* `__pivotExpandable` is `true` for `group` rows that have at least
|
|
50
|
+
* one descendant. A renderer uses this to decide whether to show a
|
|
51
|
+
* chevron next to the label.
|
|
52
|
+
*/
|
|
53
|
+
export type PivotRow = {
|
|
54
|
+
__pivotId: string;
|
|
55
|
+
__pivotKind: PivotRowKind;
|
|
56
|
+
__pivotDepth: number;
|
|
57
|
+
__pivotLabel: string;
|
|
58
|
+
__pivotParentId: string | null;
|
|
59
|
+
__pivotExpandable: boolean;
|
|
60
|
+
/** Aggregated value cells - keyed by leaf column id. */
|
|
61
|
+
[columnId: string]: unknown;
|
|
62
|
+
};
|
|
63
|
+
/** One generated column. A leaf has a `measure`; a header group has `columns`. */
|
|
64
|
+
export type PivotColumn = {
|
|
65
|
+
id: string;
|
|
66
|
+
header: string;
|
|
67
|
+
/** Child columns, for a column-axis header group. */
|
|
68
|
+
columns?: PivotColumn[];
|
|
69
|
+
/** Index into `config.values`, for a value leaf. */
|
|
70
|
+
measure?: number;
|
|
71
|
+
/** The measure's formatter, for a value leaf. */
|
|
72
|
+
format?: (value: unknown) => string;
|
|
73
|
+
/** Suggested width in px. */
|
|
74
|
+
width?: number;
|
|
75
|
+
/** True for the first column, which holds each row's label. */
|
|
76
|
+
isRowHeader?: boolean;
|
|
77
|
+
/** True for the grand-total column group and its leaves. */
|
|
78
|
+
isTotal?: boolean;
|
|
79
|
+
};
|
|
80
|
+
/** A built pivot: the rows to display, and the generated columns for its column axis. */
|
|
81
|
+
export type PivotResult = {
|
|
82
|
+
rows: PivotRow[];
|
|
83
|
+
columns: PivotColumn[];
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Build a pivot table from flat rows: cross the row axis with the column axis,
|
|
87
|
+
* aggregate each intersection, and return rows plus the generated column tree.
|
|
88
|
+
*/
|
|
89
|
+
export declare function createPivotModel<TData>(data: ReadonlyArray<TData>, config: PivotConfig<TData>): PivotResult;
|
|
90
|
+
/**
|
|
91
|
+
* Public registry of built-in aggregators. Useful for surfacing the
|
|
92
|
+
* available options in a pivot designer UI.
|
|
93
|
+
*/
|
|
94
|
+
export declare const pivotAggregators: Record<PivotAggregatorId, PivotAggregator>;
|
|
95
|
+
/**
|
|
96
|
+
* Filter a `result.rows` array down to only the rows the user should
|
|
97
|
+
* see given the current expansion state. A row stays in the output if
|
|
98
|
+
* its entire ancestor chain (via `__pivotParentId`) is in `expandedIds`.
|
|
99
|
+
*
|
|
100
|
+
* Pass `true` to bypass filtering (everything visible - the default
|
|
101
|
+
* shape `createPivotModel` already returns). Pass an empty array /
|
|
102
|
+
* empty Set to collapse every group to its subtotal row.
|
|
103
|
+
*
|
|
104
|
+
* The result is a new array; the input is not mutated.
|
|
105
|
+
*/
|
|
106
|
+
export declare function filterCollapsedPivotRows(rows: ReadonlyArray<PivotRow>, expandedIds: ReadonlyArray<string> | Set<string> | true): PivotRow[];
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pivot results in TanStack Table's shape: rows nested through `subRows`
|
|
3
|
+
* (so `getSubRows` and the expanded model collapse groups), and column
|
|
4
|
+
* definitions with plain accessor functions.
|
|
5
|
+
*/
|
|
6
|
+
import type { RowpineColumnMeta, TableLike } from '../table-source';
|
|
7
|
+
import { type PivotColumn, type PivotConfig, type PivotRow } from './pivot';
|
|
8
|
+
/** A pivot row with its children attached. */
|
|
9
|
+
export type PivotTreeRow = PivotRow & {
|
|
10
|
+
subRows?: PivotTreeRow[];
|
|
11
|
+
};
|
|
12
|
+
/** Metadata on every generated column. */
|
|
13
|
+
export type PivotColumnMeta = {
|
|
14
|
+
rowpine: RowpineColumnMeta<PivotTreeRow>;
|
|
15
|
+
pivot: {
|
|
16
|
+
isRowHeader: boolean;
|
|
17
|
+
isTotal: boolean;
|
|
18
|
+
measure?: number;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
/** A generated column: an accessor leaf or a header group. Fits TanStack's `ColumnDef<PivotTreeRow>`. */
|
|
22
|
+
export type PivotColumnDef = {
|
|
23
|
+
id: string;
|
|
24
|
+
header: string;
|
|
25
|
+
accessorFn: (row: PivotTreeRow) => unknown;
|
|
26
|
+
size?: number;
|
|
27
|
+
meta: PivotColumnMeta;
|
|
28
|
+
} | {
|
|
29
|
+
id: string;
|
|
30
|
+
header: string;
|
|
31
|
+
columns: PivotColumnDef[];
|
|
32
|
+
meta: PivotColumnMeta;
|
|
33
|
+
};
|
|
34
|
+
/** Nest flat pivot rows under their parents. The grand-total row stays last. */
|
|
35
|
+
export declare function pivotToTree(rows: ReadonlyArray<PivotRow>): PivotTreeRow[];
|
|
36
|
+
export type ToColumnsOptions = {
|
|
37
|
+
/** Excel number format for every value column, e.g. `'#,##0.00'`. */
|
|
38
|
+
numFmt?: string;
|
|
39
|
+
};
|
|
40
|
+
/** Turn a pivot's column tree into TanStack column definitions. */
|
|
41
|
+
export declare function toTanStackColumns(columns: ReadonlyArray<PivotColumn>, opts?: ToColumnsOptions): PivotColumnDef[];
|
|
42
|
+
/** Bold group, subtotal and grand-total rows in exports. Pass as `rowStyle`. */
|
|
43
|
+
export declare function pivotRowStyle(original: unknown): {
|
|
44
|
+
bold?: boolean;
|
|
45
|
+
} | undefined;
|
|
46
|
+
export type PivotTable = {
|
|
47
|
+
/** Rows for `useReactTable({ data })`, with `getSubRows: (r) => r.subRows`. */
|
|
48
|
+
data: PivotTreeRow[];
|
|
49
|
+
/** Columns for `useReactTable({ columns })`. */
|
|
50
|
+
columns: PivotColumnDef[];
|
|
51
|
+
/** The flat rows, as the engine produced them. */
|
|
52
|
+
rows: PivotRow[];
|
|
53
|
+
/** The pivot as a table the exporters read: `exportXlsx(pivot.table)`. */
|
|
54
|
+
table: TableLike;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* A pivot as a read-only table for the exporters, with no TanStack instance
|
|
58
|
+
* behind it, so exporting a pivot works the same under any TanStack version.
|
|
59
|
+
*/
|
|
60
|
+
export declare function pivotAsTable(data: PivotTreeRow[], columns: PivotColumnDef[]): TableLike;
|
|
61
|
+
/**
|
|
62
|
+
* Pivot `data` and return rows and columns ready for TanStack Table.
|
|
63
|
+
* Without a license key it still runs and logs a notice once.
|
|
64
|
+
*/
|
|
65
|
+
export declare function createPivot<TData>(data: ReadonlyArray<TData>, config: PivotConfig<TData>, opts?: ToColumnsOptions): PivotTable;
|