pivotkit-report 0.0.0-stage → 0.2.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 +88 -0
- package/LICENSE +21 -0
- package/README.md +44 -2
- package/package.json +78 -4
- package/src/default-template.js +33 -0
- package/src/html-table.js +104 -0
- package/src/index.js +24 -0
- package/src/operations.js +808 -0
- package/src/render.js +279 -0
- package/src/report-css.js +61 -0
- package/src/svg.js +170 -0
- package/src/template.js +324 -0
- package/src/texts.js +63 -0
- package/src/tokens.js +180 -0
- package/types/default-template.d.ts +2 -0
- package/types/html-table.d.ts +36 -0
- package/types/index.d.ts +13 -0
- package/types/operations.d.ts +362 -0
- package/types/render.d.ts +177 -0
- package/types/report-css.d.ts +14 -0
- package/types/svg.d.ts +39 -0
- package/types/template.d.ts +37 -0
- package/types/texts.d.ts +44 -0
- package/types/tokens.d.ts +114 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `pivotkit-report` are documented in this file. The format is based on
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this package adheres to
|
|
5
|
+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.2.0] - 2026-11-14
|
|
10
|
+
|
|
11
|
+
### Upgrading from 0.1
|
|
12
|
+
|
|
13
|
+
What a script that reads the reports checks; the [upgrade page](https://pivotkit.eize.dev/getting-started/upgrading/#report-words) has the details.
|
|
14
|
+
|
|
15
|
+
- Without `labels`, the report's words follow its locale (German for `de-DE`) and, in English, the
|
|
16
|
+
report on a page: "Yes"/"No" instead of "true"/"false", "Distinct count" instead of "Count
|
|
17
|
+
Unique". `labels: DEFAULT_LABELS` keeps the engine's words.
|
|
18
|
+
- The HTML report's details say "Rows: 8" (was "Rows: 8 rows").
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- Operations: `suggest_view` (a draft view file from a source's fields, with its `picks`), and a
|
|
23
|
+
`layout` (a view by field names) instead of a `view` for `render_view` and `validate_view`, with the
|
|
24
|
+
`view` it made in the result; `viewOfLayout` and `LAYOUT_NOTE_SEVERITY`, the one way the operations
|
|
25
|
+
and the command line make a view from a layout ([#253](https://github.com/eizedev/pivotkit/issues/253)).
|
|
26
|
+
- `pivotkit-report/operations`: the one table of operations the command line, the MCP server and a
|
|
27
|
+
REST service share (`OPERATIONS`: list_sources, describe_source, get_view_guide, validate_view,
|
|
28
|
+
describe_view, render_view, create_link, draft_schedule), each with its description, input and
|
|
29
|
+
output JSON Schema, HTTP route and `run(input, env)`; `VIEW_GUIDE`, `exampleViews`, `findingOf`,
|
|
30
|
+
`OperationError` with `OPERATION_ERRORS`; every `run` checks its input against its schema first
|
|
31
|
+
(`checkInput`, `INVALID_INPUT` naming the place) ([#165](https://github.com/eizedev/pivotkit/issues/165)).
|
|
32
|
+
- `summary.keptRows`: the rows the view's filters keep (the CLI warns when it is 0).
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
|
|
36
|
+
- The HTML report's details give the data's rows as a number ("Rows: 8"; it said "Rows: 8 rows",
|
|
37
|
+
"Zeilen: 8 Zeilen"); the template's `{{rows}}` stays "8 rows".
|
|
38
|
+
- Without `labels`, `render()` writes the report's words in the language of its locale
|
|
39
|
+
(pivotkit-core's `reportLabels`): German for a German locale ("Gesamtergebnis", "Summe von Units",
|
|
40
|
+
where it wrote "Grand Total" next to German numbers), and in English the words the report on a page
|
|
41
|
+
shows ("Distinct count", yes/no as "Yes" and "No"). Pass `labels: DEFAULT_LABELS` for the engine's
|
|
42
|
+
own words.
|
|
43
|
+
- Depends on `pivotkit-core` `^0.6.0` (was `^0.5.0`).
|
|
44
|
+
|
|
45
|
+
## [0.1.0] - 2026-11-13
|
|
46
|
+
|
|
47
|
+
First release.
|
|
48
|
+
|
|
49
|
+
### Added
|
|
50
|
+
|
|
51
|
+
- `SIZE_TOKENS`: the HTML report's font sizes as tokens, `--pk-font-size` (14 px), `--pk-small`
|
|
52
|
+
(the details, at least 14 px, were 12 px), `--pk-report-title-size` (20 px at 14 px),
|
|
53
|
+
`--pk-print-size` (10 pt) and `--pk-print-small` (7 pt); a host's `tokens` change them, and the
|
|
54
|
+
chart's text with `--pk-chart-text-size` in px (default 14 px, was ECharts' 12 px). A token's value
|
|
55
|
+
may hold a `calc()`'s `*`, never a comment ([#220](https://github.com/eizedev/pivotkit/issues/220), [#221](https://github.com/eizedev/pivotkit/issues/221)).
|
|
56
|
+
- `SHAPE_TOKENS`: the corner of the HTML report's chart card, `--pk-radius-card` (8 px, pivotkit-react's
|
|
57
|
+
value); a host's `tokens` change it, as the chart's title size (`--pk-chart-title-size`) and its marks'
|
|
58
|
+
corners (`--pk-radius-chart`) in px ([#216](https://github.com/eizedev/pivotkit/issues/216)).
|
|
59
|
+
- A colour scale or data bars without colours of the view's own take the host's `tokens` in the HTML
|
|
60
|
+
report's table and in the workbook (`--pk-scale-low`, `--pk-scale-mid`, `--pk-scale-high`,
|
|
61
|
+
`--pk-chart-1`), as the report on screen does; by default the light theme's heat colours, light
|
|
62
|
+
for low and strong for high ([#212](https://github.com/eizedev/pivotkit/issues/212)).
|
|
63
|
+
- `WEIGHT_TOKENS`: pivotkit-react's font weight scale (`--pk-weight-normal`, `-medium`, `-strong`,
|
|
64
|
+
`-bold`); the report's stylesheet and its chart take their weights from these tokens, so a host's
|
|
65
|
+
`tokens` change them too ([#201](https://github.com/eizedev/pivotkit/issues/201)).
|
|
66
|
+
- `render(view, data, options)`: a view over rows, columnar or encoded data as JSON, CSV, XLSX, SVG or
|
|
67
|
+
a printable HTML report, with the view's issues, `ok` and a summary; a bad view never throws, a
|
|
68
|
+
limit or a refused template throws `PivotkitError` ([#165](https://github.com/eizedev/pivotkit/issues/165)).
|
|
69
|
+
- The HTML report: the table as the screen shows it (merged headers, totals and subtotals, colour
|
|
70
|
+
scales and data bars, collapsed groups), the chart as an image, the details (source, rows, filters,
|
|
71
|
+
generated), print rules (the header on every page, rows not split, landscape for a wide table) and
|
|
72
|
+
a Content-Security-Policy that loads and runs nothing.
|
|
73
|
+
- The print template (`pivotkit-report/template`): `{{placeholders}}` from a fixed table, allowed in
|
|
74
|
+
text and a few attributes only, checked before anything is rendered (`INVALID_TEMPLATE`); the
|
|
75
|
+
`content` of a `<meta>` takes one only for `name="description"` without `http-equiv`.
|
|
76
|
+
- A host's `tokens` are left out when a value holds a line break or `url(` (also in a quoted string,
|
|
77
|
+
which a line break would end), a comment or a function that is not a colour, `calc()`, `min()`,
|
|
78
|
+
`max()`, `clamp()` or `var()`. The HTML report and the SVG leave out bidi controls, as the CSV does (in the SVG also as numeric references).
|
|
79
|
+
- The chart as SVG through ECharts' server-side renderer, checked and cleaned (`cleanSvg`): no
|
|
80
|
+
script, event attribute, link or style attribute; attribute names plain names, namespaces only
|
|
81
|
+
SVG's and xlink's.
|
|
82
|
+
- Options are checked: an unknown one is refused with the closest name, an option of a few values
|
|
83
|
+
(`chart`, `chartEmbed`, `bom`, `strict`, `trusted`) takes only those, and `chart: true` without
|
|
84
|
+
`echarts` is an error. `bom: false` writes CSV without a byte order mark.
|
|
85
|
+
|
|
86
|
+
[Unreleased]: https://github.com/eizedev/pivotkit/compare/pivotkit-report@0.2.0...HEAD
|
|
87
|
+
[0.2.0]: https://github.com/eizedev/pivotkit/compare/pivotkit-report@0.1.0...pivotkit-report@0.2.0
|
|
88
|
+
[0.1.0]: https://github.com/eizedev/pivotkit/releases/tag/pivotkit-report@0.1.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 eizedev
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,45 @@
|
|
|
1
|
-
#
|
|
1
|
+
# pivotkit-report
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Render [pivotkit](https://pivotkit.eize.dev/) views without a browser: a view over rows as an HTML
|
|
4
|
+
report to print or save as PDF, CSV, JSON, an Excel workbook (.xlsx) or the chart as SVG. The same
|
|
5
|
+
table and chart as the report on the screen, from pivotkit-core's engine and exporters; the print
|
|
6
|
+
template is HTML with `{{placeholders}}`, and everything filled into it is escaped. Runs in Node and
|
|
7
|
+
in the browser.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install pivotkit-report echarts
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
ES modules for Node 22 or later and browsers from Chrome 111, Firefox 113 and Safari 16.4. It depends
|
|
16
|
+
on pivotkit-core (a caret range on the minor version it was released with, see its `package.json`). ECharts (`>=6.1.0 <7`) is an optional peer dependency: only
|
|
17
|
+
the SVG and the chart of an HTML report need it, passed in as `options.echarts`. TypeScript
|
|
18
|
+
declarations are included.
|
|
19
|
+
|
|
20
|
+
## Example
|
|
21
|
+
|
|
22
|
+
```js
|
|
23
|
+
import { writeFile } from "node:fs/promises";
|
|
24
|
+
import * as echarts from "echarts";
|
|
25
|
+
import { render } from "pivotkit-report";
|
|
26
|
+
|
|
27
|
+
const view = { rows: ["Region"], values: [{ field: "Units", agg: "sum" }], display: "chart" };
|
|
28
|
+
const result = await render(view, rows, { format: "html", echarts, now: "2026-11-13" });
|
|
29
|
+
if (!result.ok) console.warn(result.issues); // a bad view never throws: its issues come back
|
|
30
|
+
await writeFile("report.html", result.body); // Uint8Array; result.text for the text formats
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Where in the docs
|
|
34
|
+
|
|
35
|
+
| Topic | Page |
|
|
36
|
+
| --- | --- |
|
|
37
|
+
| Getting started: a complete script, rows in code and data files | [Getting started with reports](https://pivotkit.eize.dev/getting-started/report/) |
|
|
38
|
+
| `render()`, its options, formats, limits and errors | [pivotkit-report reference](https://pivotkit.eize.dev/reference/report/) |
|
|
39
|
+
| The print template and its placeholders | [Print template](https://pivotkit.eize.dev/reference/print-template/) |
|
|
40
|
+
| The command line, the REST service and the MCP server on top of it | [pivotkit-cli](https://pivotkit.eize.dev/reference/cli/), [pivotkit-server](https://pivotkit.eize.dev/reference/rest/), [pivotkit-mcp](https://pivotkit.eize.dev/reference/mcp/) |
|
|
41
|
+
| Changes | [CHANGELOG](https://github.com/eizedev/pivotkit/blob/main/packages/pivotkit-report/CHANGELOG.md) |
|
|
42
|
+
|
|
43
|
+
## License
|
|
44
|
+
|
|
45
|
+
[MIT](https://github.com/eizedev/pivotkit/blob/main/LICENSE)
|
package/package.json
CHANGED
|
@@ -1,6 +1,80 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pivotkit-report",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Render pivotkit views without a browser: JSON, CSV, XLSX, SVG charts and a printable HTML report from a template, in Node and in the browser.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "eizedev <oss@eize.dev>",
|
|
7
|
+
"homepage": "https://pivotkit.eize.dev/reference/report/",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/eizedev/pivotkit.git",
|
|
11
|
+
"directory": "packages/pivotkit-report"
|
|
12
|
+
},
|
|
13
|
+
"bugs": "https://github.com/eizedev/pivotkit/issues",
|
|
14
|
+
"keywords": [
|
|
15
|
+
"pivotkit",
|
|
16
|
+
"pivot-table",
|
|
17
|
+
"reporting",
|
|
18
|
+
"report",
|
|
19
|
+
"print",
|
|
20
|
+
"svg",
|
|
21
|
+
"echarts",
|
|
22
|
+
"excel-export",
|
|
23
|
+
"pivot-grid",
|
|
24
|
+
"pivottable",
|
|
25
|
+
"excel"
|
|
26
|
+
],
|
|
27
|
+
"type": "module",
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./types/index.d.ts",
|
|
32
|
+
"default": "./src/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./template": {
|
|
35
|
+
"types": "./types/template.d.ts",
|
|
36
|
+
"default": "./src/template.js"
|
|
37
|
+
},
|
|
38
|
+
"./operations": {
|
|
39
|
+
"types": "./types/operations.d.ts",
|
|
40
|
+
"default": "./src/operations.js"
|
|
41
|
+
},
|
|
42
|
+
"./package.json": "./package.json"
|
|
43
|
+
},
|
|
44
|
+
"files": [
|
|
45
|
+
"src",
|
|
46
|
+
"types",
|
|
47
|
+
"README.md",
|
|
48
|
+
"CHANGELOG.md",
|
|
49
|
+
"LICENSE"
|
|
50
|
+
],
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=22"
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"test": "node --test \"test/*.test.js\"",
|
|
56
|
+
"coverage": "node --test --experimental-test-coverage --test-coverage-include=\"src/**\" \"test/*.test.js\"",
|
|
57
|
+
"types": "tsc -p .",
|
|
58
|
+
"test:types": "tsc -p test/types",
|
|
59
|
+
"prepack": "npm run types && node -e \"require('node:fs').copyFileSync('../../LICENSE', 'LICENSE')\""
|
|
60
|
+
},
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"echarts": ">=6.1.0 <7"
|
|
63
|
+
},
|
|
64
|
+
"peerDependenciesMeta": {
|
|
65
|
+
"echarts": {
|
|
66
|
+
"optional": true
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"dependencies": {
|
|
70
|
+
"pivotkit-core": "^0.6.0"
|
|
71
|
+
},
|
|
72
|
+
"devDependencies": {
|
|
73
|
+
"echarts": "6.1.0",
|
|
74
|
+
"pivotkit-xlsx": "0.3.0"
|
|
75
|
+
},
|
|
76
|
+
"types": "./types/index.d.ts",
|
|
77
|
+
"publishConfig": {
|
|
78
|
+
"access": "public"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The default print template: a document without script, its Content-Security-Policy first in <head>
|
|
3
|
+
* (nothing it shows can load or run anything: styles inline, images as data: addresses only), the
|
|
4
|
+
* title, the chart (an image), the table and the details below it. A host or `--template` replaces it
|
|
5
|
+
* with its own; docs/reference/print-template.md lists the placeholders.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export const REPORT_CSP = "default-src 'none'; style-src 'unsafe-inline'; img-src data:; base-uri 'none'; form-action 'none'";
|
|
9
|
+
|
|
10
|
+
export const DEFAULT_TEMPLATE = `<!doctype html>
|
|
11
|
+
<html lang="{{lang}}" dir="{{dir}}">
|
|
12
|
+
<head>
|
|
13
|
+
<meta http-equiv="Content-Security-Policy" content="${REPORT_CSP}">
|
|
14
|
+
<meta charset="utf-8">
|
|
15
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
16
|
+
<meta name="generator" content="pivotkit">
|
|
17
|
+
<meta name="description" content="{{summary}}">
|
|
18
|
+
<title>{{title}}</title>
|
|
19
|
+
{{style}}
|
|
20
|
+
</head>
|
|
21
|
+
<body>
|
|
22
|
+
<main class="pk-report">
|
|
23
|
+
<header>
|
|
24
|
+
<h1>{{title}}</h1>
|
|
25
|
+
<p class="pk-report-description">{{description}}</p>
|
|
26
|
+
</header>
|
|
27
|
+
{{chart}}
|
|
28
|
+
{{table}}
|
|
29
|
+
{{details}}
|
|
30
|
+
</main>
|
|
31
|
+
</body>
|
|
32
|
+
</html>
|
|
33
|
+
`;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The pivot table as HTML, written from pivotkit-core's grid (toGrid): what the Excel export keeps,
|
|
3
|
+
* as the table shows it. Merged headers and row labels as colspan and rowspan, totals and subtotals
|
|
4
|
+
* as classes (bold, shaded), colour scales and data bars as classes with their colours in a rule of
|
|
5
|
+
* their own (no `style` attribute, so a page with a strict Content-Security-Policy can show it), a
|
|
6
|
+
* collapsed group marked. Every text is escaped; every class comes from the fixed names below; a
|
|
7
|
+
* colour is written only when it is a hex colour.
|
|
8
|
+
*
|
|
9
|
+
* Left out on purpose: the header's sort buttons, the toggles and drill-down (nothing runs in a
|
|
10
|
+
* report). A table over `maxCells` cells is cut after the last whole row that fits: `cut` says how
|
|
11
|
+
* many body rows it shows of how many.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { DEFAULT_THEME } from "pivotkit-core";
|
|
15
|
+
import { scaleColor } from "pivotkit-core/export";
|
|
16
|
+
import { escapeHtml } from "./template.js";
|
|
17
|
+
|
|
18
|
+
const HEX = /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
|
|
19
|
+
|
|
20
|
+
/** The class of a total: subtotal or grand total. */
|
|
21
|
+
const totalClass = (total) => (total === 2 ? " pk-grand" : total === 1 ? " pk-sub" : "");
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @typedef {object} HtmlTable
|
|
25
|
+
* @property {string} html the <table>
|
|
26
|
+
* @property {string} css the rules of the conditional colours, per cell class
|
|
27
|
+
* @property {{ rows: number, of: number } | null} cut body rows shown of all, when the table was cut
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param {import("pivotkit-core").Grid} grid
|
|
32
|
+
* @param {{ maxCells: number, caption?: string, colorScale?: string[], dataBar?: string }} options
|
|
33
|
+
* colorScale, dataBar: the colours of a scale or a bar whose view names none (the host's tokens,
|
|
34
|
+
* tokens.js conditionalColors; else pivotkit-core's DEFAULT_THEME)
|
|
35
|
+
* @returns {HtmlTable}
|
|
36
|
+
*/
|
|
37
|
+
export function htmlTable(grid, { maxCells, caption, colorScale = [...DEFAULT_THEME.colorScale], dataBar = DEFAULT_THEME.dataBar }) {
|
|
38
|
+
const width = Math.max(1, grid.columnCount);
|
|
39
|
+
const bodyCount = grid.rows.length - grid.headerRows;
|
|
40
|
+
// the header always; then whole body rows while the cells fit
|
|
41
|
+
const fit = Math.max(0, Math.min(bodyCount, Math.floor(maxCells / width) - grid.headerRows));
|
|
42
|
+
const rules = [];
|
|
43
|
+
let ruleId = 0;
|
|
44
|
+
const colorRule = (style) => {
|
|
45
|
+
if (!style) {
|
|
46
|
+
return "";
|
|
47
|
+
}
|
|
48
|
+
if (style.bar !== undefined) {
|
|
49
|
+
const color = style.color && HEX.test(style.color) ? style.color : dataBar;
|
|
50
|
+
const bar = Math.round(Math.max(0, Math.min(1, style.bar)) * 1000) / 10;
|
|
51
|
+
const name = `pk-c${ruleId++}`;
|
|
52
|
+
rules.push(`.${name}{--pk-bar:${bar}%;--pk-bar-color:${HEX.test(color) ? color : DEFAULT_THEME.dataBar}}`);
|
|
53
|
+
return ` pk-has-bar ${name}`;
|
|
54
|
+
}
|
|
55
|
+
if (style.scale !== undefined) {
|
|
56
|
+
const t = Math.max(0, Math.min(1, style.scale));
|
|
57
|
+
const color = style.color && HEX.test(style.color) ? style.color : scaleColor(colorScale.filter((c) => HEX.test(c)).length >= 2 ? colorScale.filter((c) => HEX.test(c)) : [...DEFAULT_THEME.colorScale], t);
|
|
58
|
+
const name = `pk-c${ruleId++}`;
|
|
59
|
+
rules.push(`.${name}{--pk-cell-bg:${color}}`);
|
|
60
|
+
return ` pk-has-scale ${name}`;
|
|
61
|
+
}
|
|
62
|
+
return "";
|
|
63
|
+
};
|
|
64
|
+
const span = (cell) => `${(cell.colSpan ?? 1) > 1 ? ` colspan="${cell.colSpan}"` : ""}${(cell.rowSpan ?? 1) > 1 ? ` rowspan="${cell.rowSpan}"` : ""}`;
|
|
65
|
+
const head = [];
|
|
66
|
+
for (let r = 0; r < grid.headerRows; r++) {
|
|
67
|
+
const cells = grid.rows[r]
|
|
68
|
+
.map((cell) => {
|
|
69
|
+
if (!cell) {
|
|
70
|
+
return "";
|
|
71
|
+
}
|
|
72
|
+
const kind = cell.kind === "corner" ? " pk-corner" : "";
|
|
73
|
+
return `<th scope="col" class="pk-th${kind}${totalClass(cell.total)}"${span(cell)}>${escapeHtml(cell.text)}</th>`;
|
|
74
|
+
})
|
|
75
|
+
.join("");
|
|
76
|
+
head.push(`<tr>${cells}</tr>`);
|
|
77
|
+
}
|
|
78
|
+
const body = [];
|
|
79
|
+
for (let r = grid.headerRows; r < grid.headerRows + fit; r++) {
|
|
80
|
+
const cells = grid.rows[r]
|
|
81
|
+
.map((cell) => {
|
|
82
|
+
if (!cell) {
|
|
83
|
+
return "";
|
|
84
|
+
}
|
|
85
|
+
// a row label merged past the rows the cut keeps ends there
|
|
86
|
+
const rowSpan = Math.min(cell.rowSpan ?? 1, grid.headerRows + fit - r);
|
|
87
|
+
const spans = span({ ...cell, rowSpan });
|
|
88
|
+
if (cell.kind === "rowHeader") {
|
|
89
|
+
const collapsed = cell.toggle?.collapsed ? " pk-collapsed" : "";
|
|
90
|
+
return `<th scope="row" class="pk-th pk-row${totalClass(cell.total)}${collapsed}"${spans}>${escapeHtml(cell.text)}</th>`;
|
|
91
|
+
}
|
|
92
|
+
const number = cell.valueType === "text" ? " pk-text" : " pk-num";
|
|
93
|
+
return `<td class="pk-td${number}${totalClass(cell.total)}${colorRule(cell.style)}"${spans}>${escapeHtml(cell.text)}</td>`;
|
|
94
|
+
})
|
|
95
|
+
.join("");
|
|
96
|
+
body.push(`<tr>${cells}</tr>`);
|
|
97
|
+
}
|
|
98
|
+
const captionHtml = caption ? `<caption>${escapeHtml(caption)}</caption>` : "";
|
|
99
|
+
return {
|
|
100
|
+
html: `<table class="pk-table">${captionHtml}<thead>${head.join("")}</thead><tbody>${body.join("")}</tbody></table>`,
|
|
101
|
+
css: rules.join("\n"),
|
|
102
|
+
cut: fit < bodyCount ? { rows: fit, of: bodyCount } : null
|
|
103
|
+
};
|
|
104
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* pivotkit-report: a view over rows as a file without a browser - JSON, CSV, XLSX, SVG and a printable
|
|
3
|
+
* HTML report from a template with placeholders - on pivotkit-core, in Node and in the browser alike.
|
|
4
|
+
*
|
|
5
|
+
* import * as echarts from "echarts";
|
|
6
|
+
* const { body, issues, ok } = await render(view, rows, { format: "html", echarts, now: "2026-11-13" });
|
|
7
|
+
* // import { parseTemplate, fillTemplate, PLACEHOLDERS } from "pivotkit-report/template"
|
|
8
|
+
*
|
|
9
|
+
* Conventions as in pivotkit-core: required inputs first, everything optional in one options object;
|
|
10
|
+
* a bad view never throws (issues), a programmer error, a limit or a refused template throws
|
|
11
|
+
* PivotkitError.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {import("./render.js").RenderOptions} RenderOptions
|
|
16
|
+
* @typedef {import("./render.js").RenderResult} RenderResult
|
|
17
|
+
* @typedef {import("./texts.js").ReportTexts} ReportTexts
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
export { render, RENDER_FORMATS, REPORT_LIMITS, REPORT_ISSUE_CODES, CHART_SIZE } from "./render.js";
|
|
21
|
+
export { cleanSvg } from "./svg.js";
|
|
22
|
+
export { DEFAULT_TEMPLATE, REPORT_CSP } from "./default-template.js";
|
|
23
|
+
export { REPORT_TEXTS } from "./texts.js";
|
|
24
|
+
export { LIGHT_TOKENS, DARK_TOKENS, WEIGHT_TOKENS, SHAPE_TOKENS, SIZE_TOKENS } from "./tokens.js";
|