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.
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Reads a TanStack Table instance into a plain sheet: the visible columns in
3
+ * screen order, and the rows the user is looking at (filtered and sorted,
4
+ * groups and tree rows in place). Every exporter works from this.
5
+ *
6
+ * The table is typed structurally (`TableLike`) so this file is the only
7
+ * place that knows TanStack's API. Both a v8 `Table<TData>` and a v9
8
+ * `Table<TFeatures, TData>` fit it: v9 only has the methods of the features
9
+ * you register, so everything past the core is optional here, and v9's
10
+ * start/end pinning and `table.store` state are read alongside v8's
11
+ * left/right pinning and `getState()`.
12
+ */
13
+ /** Per-column export settings, read from `columnDef.meta.rowpine`. */
14
+ export type RowpineColumnMeta<TData = any> = {
15
+ /** Header text in the file. Default: the column's string header, else its id. */
16
+ header?: string;
17
+ /** Excel number format, e.g. `'"$"#,##0.00'` or `'yyyy-mm-dd'`. */
18
+ numFmt?: string;
19
+ /** Display text for CSV and PDF. Default: numbers and dates in the browser locale. */
20
+ format?: (value: unknown, row: TData) => string;
21
+ /** Column width in px. Default: the column's TanStack size. */
22
+ width?: number;
23
+ /** Horizontal alignment. Default: right for numbers, left otherwise. */
24
+ align?: 'left' | 'center' | 'right';
25
+ /** Cell colors, e.g. for conditional formatting. */
26
+ cellStyle?: (value: unknown, row: TData) => CellStyle | undefined;
27
+ /** Leave this column out of every export. */
28
+ exclude?: boolean;
29
+ };
30
+ export type CellStyle = {
31
+ fill?: string;
32
+ color?: string;
33
+ bold?: boolean;
34
+ };
35
+ /** The parts of a TanStack column the exporters read. */
36
+ export interface ColumnLike {
37
+ id: string;
38
+ columnDef: {
39
+ header?: unknown;
40
+ meta?: unknown;
41
+ aggregationFn?: unknown;
42
+ };
43
+ parent?: ColumnLike;
44
+ getSize?(): number;
45
+ }
46
+ /** The parts of a TanStack row the exporters read. */
47
+ export interface RowLike {
48
+ id: string;
49
+ depth: number;
50
+ original: unknown;
51
+ subRows: RowLike[];
52
+ groupingColumnId?: string;
53
+ groupingValue?: unknown;
54
+ getValue(columnId: string): unknown;
55
+ getIsGrouped?(): boolean;
56
+ }
57
+ type RowModelLike = {
58
+ rows: RowLike[];
59
+ };
60
+ /** The parts of a TanStack table the exporters read. */
61
+ export interface TableLike {
62
+ getAllLeafColumns(): ColumnLike[];
63
+ getVisibleLeafColumns?(): ColumnLike[];
64
+ getLeftVisibleLeafColumns?(): ColumnLike[];
65
+ getRightVisibleLeafColumns?(): ColumnLike[];
66
+ getStartVisibleLeafColumns?(): ColumnLike[];
67
+ getEndVisibleLeafColumns?(): ColumnLike[];
68
+ getCenterVisibleLeafColumns?(): ColumnLike[];
69
+ getCoreRowModel(): RowModelLike;
70
+ getFilteredRowModel?(): RowModelLike;
71
+ getGroupedRowModel?(): RowModelLike;
72
+ getSortedRowModel?(): RowModelLike;
73
+ getRowModel(): RowModelLike;
74
+ getSelectedRowModel?(): RowModelLike;
75
+ /** v8 state. */
76
+ getState?(): {
77
+ grouping?: string[];
78
+ };
79
+ /** v9 state. */
80
+ store?: {
81
+ readonly state: {
82
+ grouping?: string[];
83
+ };
84
+ };
85
+ /** v9 React: the selected state. */
86
+ state?: {
87
+ grouping?: string[];
88
+ };
89
+ }
90
+ /** Which rows to export. */
91
+ export type RowScope =
92
+ /** Every row that passes the filter, on every page, in the current sort (default). */
93
+ 'all'
94
+ /** Only the rows on the current page. */
95
+ | 'page'
96
+ /** Only the selected rows. */
97
+ | 'selected';
98
+ export type ReadOptions = {
99
+ rows?: RowScope;
100
+ /** Style a whole row (e.g. bold totals). Called with the row's original data. */
101
+ rowStyle?: (original: unknown) => CellStyle | undefined;
102
+ };
103
+ /** Aggregations that have an Excel SUBTOTAL equivalent. */
104
+ export type SubtotalAgg = 'sum' | 'mean' | 'min' | 'max';
105
+ export type SheetColumn = {
106
+ id: string;
107
+ header: string;
108
+ width: number;
109
+ align: 'left' | 'center' | 'right' | undefined;
110
+ numFmt?: string;
111
+ format?: (value: unknown, row: unknown) => string;
112
+ cellStyle?: (value: unknown, row: unknown) => CellStyle | undefined;
113
+ /** The column's aggregation when it maps to SUBTOTAL; `auto` resolves per value. */
114
+ subtotal?: SubtotalAgg | 'auto';
115
+ };
116
+ export type SheetRow = {
117
+ /** `group` is a TanStack grouped row; `data` is anything else. */
118
+ kind: 'group' | 'data';
119
+ /** Nesting depth: 0 for top-level rows. */
120
+ depth: number;
121
+ /** Raw values, aligned with the columns. Group rows hold aggregates. */
122
+ values: unknown[];
123
+ /** Which values to leave blank on screen-shaped output (grouped-by cells of leaf rows). */
124
+ blank: boolean[];
125
+ /** Index of the column showing a group row's label, and the label. */
126
+ labelAt?: number;
127
+ label?: string;
128
+ /** How many sheet rows directly follow this one as its descendants. */
129
+ descendants: number;
130
+ /** True when the row has child rows (a group, or a parent in tree data). */
131
+ hasChildren: boolean;
132
+ original: unknown;
133
+ style?: CellStyle;
134
+ };
135
+ export type Sheet = {
136
+ columns: SheetColumn[];
137
+ rows: SheetRow[];
138
+ /** Leading columns pinned left on screen. */
139
+ freezeColumns: number;
140
+ };
141
+ /** Read a TanStack table into a sheet. */
142
+ export declare function readTable(table: TableLike, opts?: ReadOptions): Sheet;
143
+ /** Text for one value, for CSV-free outputs (PDF). */
144
+ export declare function displayText(column: SheetColumn, value: unknown, original: unknown): string;
145
+ /** Alignment for a column: its own, else right when its values are numbers. */
146
+ export declare function alignOf(column: SheetColumn, sheet: Sheet, index: number): 'left' | 'center' | 'right';
147
+ export {};
@@ -0,0 +1,17 @@
1
+ import { type ReadOptions, type Sheet, type TableLike } from '../table-source';
2
+ import { type XlsxModel } from './ooxml';
3
+ export type XlsxExportOptions = ReadOptions & {
4
+ fileName?: string;
5
+ /** Worksheet name. Default 'Sheet1'. */
6
+ sheetName?: string;
7
+ /** Fill color of group rows. Default '#eef2f0'. */
8
+ groupFill?: string;
9
+ /** Save the file in the browser. Default true. */
10
+ download?: boolean;
11
+ };
12
+ /** The workbook model for a sheet. Pure: no zip, no license check. */
13
+ export declare function sheetToXlsxModel(sheet: Sheet, opts?: XlsxExportOptions, notes?: string[]): XlsxModel;
14
+ /** The workbook model for a table. Pure apart from reading the table. */
15
+ export declare function tableToXlsxModel(table: TableLike, opts?: XlsxExportOptions, notes?: string[]): XlsxModel;
16
+ /** Export the table to an .xlsx file. Returns the file as a Blob. */
17
+ export declare function exportXlsx(table: TableLike, opts?: XlsxExportOptions): Promise<Blob>;
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Native OOXML (.xlsx) writer. Builds a real workbook from scratch
3
+ * (styles.xml + sheet.xml, zipped with jszip) with, in one file:
4
+ * - typed number / date cells with a number format (Excel sums + sorts them),
5
+ * - per-cell fills / font color / bold (conditional formatting),
6
+ * - a frozen header (and optional frozen columns),
7
+ * - explicit column widths,
8
+ * - outline rows (row groups Excel can collapse) and formula cells,
9
+ * - real cell hyperlinks.
10
+ *
11
+ * It's split into a pure `buildXlsxParts` (returns the XML parts - fully unit
12
+ * testable) and `packageXlsx` (zips them into a Blob via a provided JSZip).
13
+ */
14
+ /** One worksheet cell, already resolved by the caller. */
15
+ export type XlsxCell = {
16
+ /** 'n' number, 'd' date, 's' string, 'b' boolean. */
17
+ t: 'n' | 'd' | 's' | 'b';
18
+ value: number | string | boolean | Date | null | undefined;
19
+ /**
20
+ * A formula without the leading '=', e.g. `SUBTOTAL(9,C3:C9)`. `value` is
21
+ * written as its cached result, so viewers that do not recalculate still
22
+ * show the number.
23
+ */
24
+ formula?: string;
25
+ /** Excel number-format code (for 'n' / 'd'). */
26
+ numFmt?: string;
27
+ align?: 'left' | 'center' | 'right';
28
+ /** Background fill hex (e.g. '#fee2e2'). */
29
+ fill?: string;
30
+ /** Font color hex. */
31
+ color?: string;
32
+ bold?: boolean;
33
+ /** Hyperlink URL. */
34
+ link?: string;
35
+ };
36
+ /**
37
+ * A native Excel Table (ListObject) over the data: filter dropdowns, banded
38
+ * rows, structured references, and (optionally) a totals row with SUBTOTAL
39
+ * formulas per numeric column.
40
+ */
41
+ export type XlsxTableSpec = {
42
+ /** Table name (no spaces; must start with a letter). E.g. 'Table1'. */
43
+ name: string;
44
+ /** Add a totals row. */
45
+ totalsRow: boolean;
46
+ /** Table style id (default 'TableStyleMedium2'). */
47
+ style?: string;
48
+ /** Per-column totals function, aligned with `header`. */
49
+ totalsFns: ReadonlyArray<'sum' | 'average' | 'count' | undefined>;
50
+ };
51
+ /**
52
+ * Native Excel conditional formatting over a whole column's data range - a
53
+ * data bar or a 2/3-stop color scale. Unlike per-cell fills, Excel computes
54
+ * these itself (and recomputes when the user edits), so they're interactive
55
+ * and higher fidelity.
56
+ */
57
+ export type XlsxCondFormat = {
58
+ kind: 'dataBar';
59
+ colIdx: number;
60
+ color: string;
61
+ } | {
62
+ kind: 'colorScale';
63
+ colIdx: number;
64
+ colors: ReadonlyArray<string>;
65
+ };
66
+ export type XlsxModel = {
67
+ sheetName: string;
68
+ /** Header labels (row 1). */
69
+ header: ReadonlyArray<string>;
70
+ /** Data rows (each an array of cells, column-aligned with `header`). */
71
+ rows: ReadonlyArray<ReadonlyArray<XlsxCell>>;
72
+ /** Per-column widths in pixels (optional). */
73
+ widths?: ReadonlyArray<number>;
74
+ /** Freeze the header row. */
75
+ freezeHeader?: boolean;
76
+ /** Freeze this many leading columns. */
77
+ freezeColumns?: number;
78
+ /** Native column conditional formatting (data bars / color scales). */
79
+ condFormats?: ReadonlyArray<XlsxCondFormat>;
80
+ /** Wrap the data in a native Excel Table. */
81
+ table?: XlsxTableSpec;
82
+ /**
83
+ * Outline level per data row (0 = top level), aligned with `rows`. Rows
84
+ * with a level above 0 form groups Excel can collapse; the summary row sits
85
+ * above its detail rows, as on screen.
86
+ */
87
+ outline?: ReadonlyArray<number>;
88
+ /** Plain text lines written below the data, after one empty row. */
89
+ notes?: ReadonlyArray<string>;
90
+ };
91
+ /** 0-based column index -> Excel letter (0 -> A, 26 -> AA). */
92
+ export declare function colName(index: number): string;
93
+ /**
94
+ * Excel serial date: days since 1899-12-30 on the LOCAL wall clock.
95
+ *
96
+ * An Excel serial has no timezone; it is the date and time a person reads. So
97
+ * it has to be built from the local parts the grid displays. Subtracting a UTC
98
+ * epoch from `getTime()` wrote local midnight on the 15th as 45305.9167 in
99
+ * UTC+2, which Excel shows as the 14th, and west of UTC added a spurious time
100
+ * to every pure date (#103).
101
+ */
102
+ export declare function toSerialDate(d: Date): number;
103
+ /**
104
+ * Build the OOXML package parts for a single-sheet workbook. Returns a map of
105
+ * `path -> xml string` ready to be zipped by {@link packageXlsx}.
106
+ */
107
+ export declare function buildXlsxParts(model: XlsxModel): Record<string, string>;
108
+ /** Zip the parts into an xlsx Blob using a provided JSZip constructor. */
109
+ export declare function packageXlsx(parts: Record<string, string>, JSZip: new () => {
110
+ file(path: string, data: string): void;
111
+ generateAsync(opts: {
112
+ type: 'blob';
113
+ mimeType?: string;
114
+ }): Promise<Blob>;
115
+ }): Promise<Blob>;
package/package.json CHANGED
@@ -1,6 +1,65 @@
1
1
  {
2
2
  "name": "rowpine",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0",
4
+ "description": "Pivot tables and Excel and PDF export for TanStack Table and shadcn/ui data tables.",
5
+ "keywords": [
6
+ "tanstack",
7
+ "tanstack-table",
8
+ "react-table",
9
+ "shadcn",
10
+ "pivot",
11
+ "pivot-table",
12
+ "excel",
13
+ "xlsx",
14
+ "pdf",
15
+ "export",
16
+ "data-table"
17
+ ],
18
+ "homepage": "https://rowpine.com/",
19
+ "bugs": {
20
+ "url": "https://github.com/rowpine/rowpine/issues"
21
+ },
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/rowpine/rowpine.git"
25
+ },
26
+ "license": "SEE LICENSE IN LICENSE.md",
27
+ "author": "Rowpine",
28
+ "type": "module",
29
+ "main": "./dist/index.js",
30
+ "module": "./dist/index.js",
31
+ "types": "./dist/index.d.ts",
32
+ "exports": {
33
+ ".": {
34
+ "types": "./dist/index.d.ts",
35
+ "import": "./dist/index.js"
36
+ }
37
+ },
38
+ "files": [
39
+ "dist",
40
+ "LICENSE.md",
41
+ "README.md"
42
+ ],
43
+ "sideEffects": false,
44
+ "engines": {
45
+ "node": ">=18"
46
+ },
47
+ "scripts": {
48
+ "build": "tsup && tsc -p tsconfig.build.json",
49
+ "test": "vitest run",
50
+ "typecheck": "tsc --noEmit"
51
+ },
52
+ "dependencies": {
53
+ "@noble/ed25519": "^3.2.0",
54
+ "jszip": "^3.10.2",
55
+ "pdfmake": "^0.3.14"
56
+ },
57
+ "devDependencies": {
58
+ "@tanstack/table-core": "^9.3.1",
59
+ "@types/node": "^26.6.6",
60
+ "table-core-v8": "npm:@tanstack/table-core@^8.21.3",
61
+ "tsup": "^9.0.0",
62
+ "typescript": "^7.0.2",
63
+ "vitest": "^5.1.0"
64
+ }
65
+ }