pivotkit-report 0.0.0-stage → 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.
- package/CHANGELOG.md +51 -0
- package/LICENSE +21 -0
- package/README.md +43 -2
- package/package.json +74 -4
- package/src/default-template.js +33 -0
- package/src/html-table.js +104 -0
- package/src/index.js +24 -0
- package/src/render.js +278 -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/render.d.ts +176 -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/src/tokens.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The colours of a report on paper and on screen: the --pk-* tokens of pivotkit-react's styles.css
|
|
3
|
+
* that a printed report uses, with the same names and values (a kept copy: test/tokens.test.js
|
|
4
|
+
* compares them with styles.css, so a change there fails here until it is made here too). The chart of
|
|
5
|
+
* an SVG and a report is drawn with the light ones; a host passes its own as `tokens`.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/** The light theme: print, and a screen in light mode. */
|
|
9
|
+
export const LIGHT_TOKENS = Object.freeze({
|
|
10
|
+
"--pk-surface": "#ffffff",
|
|
11
|
+
"--pk-surface-2": "#f6f7f9",
|
|
12
|
+
"--pk-surface-raised": "#ffffff",
|
|
13
|
+
"--pk-text": "#1a1d23",
|
|
14
|
+
"--pk-muted": "#5a6270",
|
|
15
|
+
"--pk-border": "#dde1e7",
|
|
16
|
+
"--pk-border-strong": "#c3c9d2",
|
|
17
|
+
"--pk-grid": "#edf0f3",
|
|
18
|
+
"--pk-total": "#eef1f5",
|
|
19
|
+
"--pk-total-strong": "#e9edf3",
|
|
20
|
+
"--pk-shadow": "0 1px 2px rgb(16 24 40 / 0.06), 0 4px 12px rgb(16 24 40 / 0.08)",
|
|
21
|
+
"--pk-heat-low": "#eaf1fb",
|
|
22
|
+
"--pk-heat-high": "#2a78d6",
|
|
23
|
+
"--pk-scale-low": "var(--pk-heat-low)",
|
|
24
|
+
"--pk-scale-high": "var(--pk-heat-high)",
|
|
25
|
+
"--pk-chart-1": "#2a78d6",
|
|
26
|
+
"--pk-chart-2": "#eb6834",
|
|
27
|
+
"--pk-chart-3": "#1baf7a",
|
|
28
|
+
"--pk-chart-4": "#eda100",
|
|
29
|
+
"--pk-chart-5": "#e87ba4",
|
|
30
|
+
"--pk-chart-6": "#008300",
|
|
31
|
+
"--pk-chart-7": "#6250d6",
|
|
32
|
+
"--pk-chart-8": "#e34948"
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
/** The dark theme, for a report read on a screen in dark mode (never printed: print is light). */
|
|
36
|
+
export const DARK_TOKENS = Object.freeze({
|
|
37
|
+
"--pk-surface": "#16181d",
|
|
38
|
+
"--pk-surface-2": "#1c1f25",
|
|
39
|
+
"--pk-surface-raised": "#22262d",
|
|
40
|
+
"--pk-text": "#e8eaef",
|
|
41
|
+
"--pk-muted": "#a3aab7",
|
|
42
|
+
"--pk-border": "#2f343e",
|
|
43
|
+
"--pk-border-strong": "#454c58",
|
|
44
|
+
"--pk-grid": "#23272e",
|
|
45
|
+
"--pk-total": "#21252d",
|
|
46
|
+
"--pk-total-strong": "#252a33",
|
|
47
|
+
"--pk-shadow": "0 1px 2px rgb(0 0 0 / 0.4), 0 4px 12px rgb(0 0 0 / 0.3)",
|
|
48
|
+
"--pk-heat-low": "#1d2735",
|
|
49
|
+
"--pk-heat-high": "#3987e5",
|
|
50
|
+
"--pk-scale-low": "var(--pk-heat-low)",
|
|
51
|
+
"--pk-scale-high": "var(--pk-heat-high)",
|
|
52
|
+
"--pk-chart-1": "#3987e5",
|
|
53
|
+
"--pk-chart-2": "#d95926",
|
|
54
|
+
"--pk-chart-3": "#199e70",
|
|
55
|
+
"--pk-chart-4": "#c98500",
|
|
56
|
+
"--pk-chart-5": "#d55181",
|
|
57
|
+
"--pk-chart-6": "#008300",
|
|
58
|
+
"--pk-chart-7": "#9085e9",
|
|
59
|
+
"--pk-chart-8": "#e66767"
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
/** The font weights, the same in both themes: pivotkit-react's scale (test/tokens.test.js compares them). */
|
|
63
|
+
export const WEIGHT_TOKENS = Object.freeze({
|
|
64
|
+
"--pk-weight-normal": "400",
|
|
65
|
+
"--pk-weight-medium": "500",
|
|
66
|
+
"--pk-weight-strong": "600",
|
|
67
|
+
"--pk-weight-bold": "700"
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The corners the report draws, the same in both themes: the chart's card, as pivotkit-react's tooltip
|
|
72
|
+
* (test/tokens.test.js compares them).
|
|
73
|
+
*/
|
|
74
|
+
export const SHAPE_TOKENS = Object.freeze({
|
|
75
|
+
"--pk-radius-card": "8px"
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The report's font sizes, the same in both themes: its text (--pk-font-size, 14 px, the report's own
|
|
80
|
+
* default: pivotkit-react's follows the page, at least 14 px, and its Export menu passes it in px),
|
|
81
|
+
* secondary text (--pk-small, pivotkit-react's: the details, never below 14 px), the title (20 px at a
|
|
82
|
+
* 14 px text) and the printed text, in pt on paper: the table and, for a very wide table, the smaller
|
|
83
|
+
* size (PRINT_WIDE.small, never below 7 pt). A host passes its own as `tokens`.
|
|
84
|
+
*/
|
|
85
|
+
export const SIZE_TOKENS = Object.freeze({
|
|
86
|
+
"--pk-font-size": "14px",
|
|
87
|
+
"--pk-small": "max(14px, 0.93em)",
|
|
88
|
+
"--pk-report-title-size": "calc(var(--pk-font-size) * 10 / 7)",
|
|
89
|
+
"--pk-print-size": "10pt",
|
|
90
|
+
"--pk-print-small": "7pt"
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The report's font: the system's, as the screen's report inherits from most hosts. Without quotes
|
|
95
|
+
* (names of several words are valid unquoted): ECharts 6.1 writes a font into an SVG's style attribute
|
|
96
|
+
* without escaping its quotes, which would end the attribute.
|
|
97
|
+
*/
|
|
98
|
+
export const REPORT_FONT = "system-ui, -apple-system, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif";
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* A token value is written into CSS only when it holds no character that could end the rule, and no
|
|
102
|
+
* comment (a calc()'s * and / are kept apart from each other).
|
|
103
|
+
*/
|
|
104
|
+
const SAFE_VALUE = /^(?![\s\S]*(\/\*|\*\/))[#\w\s.,%()/*+-]*$/;
|
|
105
|
+
/**
|
|
106
|
+
* The CSS functions a token may call: colours, a shadow's lengths and other tokens. Any other, url(),
|
|
107
|
+
* image-set() or src() among them, could load a resource where a report loads none.
|
|
108
|
+
*/
|
|
109
|
+
const SAFE_FUNCTIONS = new Set(["rgb", "rgba", "hsl", "hsla", "hwb", "lab", "lch", "oklab", "oklch", "color", "color-mix", "light-dark", "calc", "min", "max", "clamp", "var"]);
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Whether a value (its quoted strings left out) is plain and calls only SAFE_FUNCTIONS. A line break
|
|
113
|
+
* (LF, CR, FF) is refused before the strings are left out: inside a CSS string it ends the string, so
|
|
114
|
+
* what follows would be read as CSS; and url( is refused anywhere, also inside a string.
|
|
115
|
+
*/
|
|
116
|
+
function safeValue(value) {
|
|
117
|
+
if (/[\n\r\f]|url\(/i.test(value)) return false;
|
|
118
|
+
const plain = value.replace(/"[^"\\<>{};]*"/g, "");
|
|
119
|
+
return SAFE_VALUE.test(plain) && [...plain.matchAll(/([\w-]*)\(/g)].every((m) => SAFE_FUNCTIONS.has(m[1].toLowerCase()));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* The tokens as a style the chart's code reads (getPropertyValue), the host's over the light ones.
|
|
124
|
+
* @param {Record<string, string>} [tokens]
|
|
125
|
+
* @returns {{ getPropertyValue(name: string): string }}
|
|
126
|
+
*/
|
|
127
|
+
export function tokenStyle(tokens = {}) {
|
|
128
|
+
const all = { ...LIGHT_TOKENS, ...WEIGHT_TOKENS, ...SHAPE_TOKENS, ...SIZE_TOKENS, "font-family": REPORT_FONT, ...cleanTokens(tokens) };
|
|
129
|
+
// the chart's font without quotes (see REPORT_FONT)
|
|
130
|
+
all["font-family"] = all["font-family"].replace(/["']/g, "");
|
|
131
|
+
return { getPropertyValue: (name) => (Object.hasOwn(all, name) ? all[name] : "") };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* The host's tokens that may go into CSS: --pk-* names (and font-family) with plain values that call
|
|
136
|
+
* no function but SAFE_FUNCTIONS; others are left out.
|
|
137
|
+
* @param {Record<string, string>} tokens
|
|
138
|
+
*/
|
|
139
|
+
export function cleanTokens(tokens) {
|
|
140
|
+
/** @type {Record<string, string>} */
|
|
141
|
+
const out = {};
|
|
142
|
+
for (const [name, value] of Object.entries(tokens ?? {})) {
|
|
143
|
+
if ((/^--pk-[a-z0-9-]+$/.test(name) || name === "font-family") && typeof value === "string" && value.length <= 200 && safeValue(value)) {
|
|
144
|
+
out[name] = value.trim();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return out;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** A colour a table cell and a workbook take: #rgb or #rrggbb. */
|
|
151
|
+
const HEX_COLOR = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* The colours of a colour scale and of data bars whose view names none, from the host's tokens over
|
|
155
|
+
* the light ones, as the report on screen resolves them: the scale --pk-scale-low, the host's
|
|
156
|
+
* --pk-scale-mid and --pk-scale-high (by default the heat tokens), the bars --pk-chart-1. A value
|
|
157
|
+
* that is no plain hex colour (another token is followed) keeps pivotkit-core's default.
|
|
158
|
+
* @param {Record<string, string>} [tokens]
|
|
159
|
+
* @returns {{ colorScale?: string[], dataBar?: string }}
|
|
160
|
+
*/
|
|
161
|
+
export function conditionalColors(tokens = {}) {
|
|
162
|
+
const all = { ...LIGHT_TOKENS, ...cleanTokens(tokens) };
|
|
163
|
+
const resolve = (/** @type {string} */ name, depth = 0) => {
|
|
164
|
+
const value = Object.hasOwn(all, name) ? all[name] : "";
|
|
165
|
+
const ref = /^var\((--pk-[a-z0-9-]+)\)$/.exec(value);
|
|
166
|
+
return ref && depth < 4 ? resolve(ref[1], depth + 1) : HEX_COLOR.test(value) ? value : null;
|
|
167
|
+
};
|
|
168
|
+
const [low, mid, high, bar] = ["--pk-scale-low", "--pk-scale-mid", "--pk-scale-high", "--pk-chart-1"].map((name) => resolve(name));
|
|
169
|
+
return {
|
|
170
|
+
...(low && high ? { colorScale: mid ? [low, mid, high] : [low, high] } : {}),
|
|
171
|
+
...(bar ? { dataBar: bar } : {})
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Tokens as CSS declarations. */
|
|
176
|
+
export const declarations = (tokens) =>
|
|
177
|
+
Object.entries(tokens)
|
|
178
|
+
.filter(([name]) => name.startsWith("--pk-"))
|
|
179
|
+
.map(([name, value]) => `${name}:${value}`)
|
|
180
|
+
.join(";");
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const REPORT_CSP = "default-src 'none'; style-src 'unsafe-inline'; img-src data:; base-uri 'none'; form-action 'none'";
|
|
2
|
+
export declare const DEFAULT_TEMPLATE = "<!doctype html>\n<html lang=\"{{lang}}\" dir=\"{{dir}}\">\n<head>\n<meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'none'; style-src 'unsafe-inline'; img-src data:; base-uri 'none'; form-action 'none'\">\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<meta name=\"generator\" content=\"pivotkit\">\n<meta name=\"description\" content=\"{{summary}}\">\n<title>{{title}}</title>\n{{style}}\n</head>\n<body>\n<main class=\"pk-report\">\n<header>\n<h1>{{title}}</h1>\n<p class=\"pk-report-description\">{{description}}</p>\n</header>\n{{chart}}\n{{table}}\n{{details}}\n</main>\n</body>\n</html>\n";
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export type HtmlTable = {
|
|
2
|
+
/**
|
|
3
|
+
* the <table>
|
|
4
|
+
*/
|
|
5
|
+
html: string;
|
|
6
|
+
/**
|
|
7
|
+
* the rules of the conditional colours, per cell class
|
|
8
|
+
*/
|
|
9
|
+
css: string;
|
|
10
|
+
/**
|
|
11
|
+
* body rows shown of all, when the table was cut
|
|
12
|
+
*/
|
|
13
|
+
cut: {
|
|
14
|
+
rows: number;
|
|
15
|
+
of: number;
|
|
16
|
+
} | null;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {object} HtmlTable
|
|
20
|
+
* @property {string} html the <table>
|
|
21
|
+
* @property {string} css the rules of the conditional colours, per cell class
|
|
22
|
+
* @property {{ rows: number, of: number } | null} cut body rows shown of all, when the table was cut
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* @param {import("pivotkit-core").Grid} grid
|
|
26
|
+
* @param {{ maxCells: number, caption?: string, colorScale?: string[], dataBar?: string }} options
|
|
27
|
+
* colorScale, dataBar: the colours of a scale or a bar whose view names none (the host's tokens,
|
|
28
|
+
* tokens.js conditionalColors; else pivotkit-core's DEFAULT_THEME)
|
|
29
|
+
* @returns {HtmlTable}
|
|
30
|
+
*/
|
|
31
|
+
export declare function htmlTable(grid: import("pivotkit-core").Grid, { maxCells, caption, colorScale, dataBar }: {
|
|
32
|
+
maxCells: number;
|
|
33
|
+
caption?: string;
|
|
34
|
+
colorScale?: string[];
|
|
35
|
+
dataBar?: string;
|
|
36
|
+
}): HtmlTable;
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type RenderOptions = import("./render.js").RenderOptions;
|
|
2
|
+
export type RenderResult = import("./render.js").RenderResult;
|
|
3
|
+
export type ReportTexts = import("./texts.js").ReportTexts;
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {import("./render.js").RenderOptions} RenderOptions
|
|
6
|
+
* @typedef {import("./render.js").RenderResult} RenderResult
|
|
7
|
+
* @typedef {import("./texts.js").ReportTexts} ReportTexts
|
|
8
|
+
*/
|
|
9
|
+
export { render, RENDER_FORMATS, REPORT_LIMITS, REPORT_ISSUE_CODES, CHART_SIZE } from "./render.js";
|
|
10
|
+
export { cleanSvg } from "./svg.js";
|
|
11
|
+
export { DEFAULT_TEMPLATE, REPORT_CSP } from "./default-template.js";
|
|
12
|
+
export { REPORT_TEXTS } from "./texts.js";
|
|
13
|
+
export { LIGHT_TOKENS, DARK_TOKENS, WEIGHT_TOKENS, SHAPE_TOKENS, SIZE_TOKENS } from "./tokens.js";
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/** The formats: media type, file extension, and whether the body is text (`text` is set). */
|
|
2
|
+
export declare const RENDER_FORMATS: Readonly<{
|
|
3
|
+
json: Readonly<{
|
|
4
|
+
mediaType: "application/json";
|
|
5
|
+
extension: "json";
|
|
6
|
+
text: true;
|
|
7
|
+
}>;
|
|
8
|
+
csv: Readonly<{
|
|
9
|
+
mediaType: "text/csv;charset=utf-8";
|
|
10
|
+
extension: "csv";
|
|
11
|
+
text: true;
|
|
12
|
+
}>;
|
|
13
|
+
xlsx: Readonly<{
|
|
14
|
+
mediaType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
|
15
|
+
extension: "xlsx";
|
|
16
|
+
text: false;
|
|
17
|
+
}>;
|
|
18
|
+
svg: Readonly<{
|
|
19
|
+
mediaType: "image/svg+xml";
|
|
20
|
+
extension: "svg";
|
|
21
|
+
text: true;
|
|
22
|
+
}>;
|
|
23
|
+
html: Readonly<{
|
|
24
|
+
mediaType: "text/html;charset=utf-8";
|
|
25
|
+
extension: "html";
|
|
26
|
+
text: true;
|
|
27
|
+
}>;
|
|
28
|
+
}>;
|
|
29
|
+
/**
|
|
30
|
+
* What a report allows at most, besides pivotkit-core's LIMITS: the characters of a template, the size
|
|
31
|
+
* of a chart in px, the cells of a report's table (a larger table is cut after the last whole row, and
|
|
32
|
+
* says so), the categories of a chart (the first ones, as on the screen).
|
|
33
|
+
*/
|
|
34
|
+
export declare const REPORT_LIMITS: Readonly<{
|
|
35
|
+
template: 262144;
|
|
36
|
+
chartWidth: 8000;
|
|
37
|
+
chartHeight: 8000;
|
|
38
|
+
tableCells: 200000;
|
|
39
|
+
categories: 50;
|
|
40
|
+
}>;
|
|
41
|
+
/** The default size of a chart in px. */
|
|
42
|
+
export declare const CHART_SIZE: Readonly<{
|
|
43
|
+
width: 800;
|
|
44
|
+
height: 480;
|
|
45
|
+
}>;
|
|
46
|
+
/** Issues of a report itself (warnings), besides a view's (VIEW_ISSUES). */
|
|
47
|
+
export declare const REPORT_ISSUE_CODES: readonly string[];
|
|
48
|
+
export type RenderOptions = {
|
|
49
|
+
/**
|
|
50
|
+
* default "html"
|
|
51
|
+
*/
|
|
52
|
+
format?: "json" | "csv" | "xlsx" | "svg" | "html";
|
|
53
|
+
/**
|
|
54
|
+
* used when the view names none; default "en-US"
|
|
55
|
+
*/
|
|
56
|
+
locale?: string;
|
|
57
|
+
/**
|
|
58
|
+
* of rows that are not encoded yet (encode()), and of the "generated" time; default "UTC"
|
|
59
|
+
*/
|
|
60
|
+
timeZone?: string;
|
|
61
|
+
/**
|
|
62
|
+
* "today" of relative date filters and the "generated" time; fix it for output that is the same on every run
|
|
63
|
+
*/
|
|
64
|
+
now?: Date | number | string;
|
|
65
|
+
/**
|
|
66
|
+
* pivotkit-core's limits
|
|
67
|
+
*/
|
|
68
|
+
limits?: Partial<import("pivotkit-core").Limits>;
|
|
69
|
+
/**
|
|
70
|
+
* the view is the host's own: the host's limits instead of outsideLimits() (default false)
|
|
71
|
+
*/
|
|
72
|
+
trusted?: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* no body when the view has an error (default false: the normalized view is rendered)
|
|
75
|
+
*/
|
|
76
|
+
strict?: boolean;
|
|
77
|
+
/**
|
|
78
|
+
* pivotkit-core's texts (totals, value names)
|
|
79
|
+
*/
|
|
80
|
+
labels?: Partial<import("pivotkit-core").Labels>;
|
|
81
|
+
/**
|
|
82
|
+
* the report's words around the table
|
|
83
|
+
*/
|
|
84
|
+
texts?: Partial<import("./texts.js").ReportTexts>;
|
|
85
|
+
/**
|
|
86
|
+
* --pk-* colours over the light ones (the chart and the report)
|
|
87
|
+
*/
|
|
88
|
+
tokens?: Record<string, string>;
|
|
89
|
+
/**
|
|
90
|
+
* the report's title; default the view file's name, the chart's title, "Report"
|
|
91
|
+
*/
|
|
92
|
+
title?: string;
|
|
93
|
+
/**
|
|
94
|
+
* default the view file's description
|
|
95
|
+
*/
|
|
96
|
+
description?: string;
|
|
97
|
+
/**
|
|
98
|
+
* where the data comes from, for the report's details (a file name)
|
|
99
|
+
*/
|
|
100
|
+
source?: string;
|
|
101
|
+
/**
|
|
102
|
+
* html: a print template (parseTemplate), default DEFAULT_TEMPLATE
|
|
103
|
+
*/
|
|
104
|
+
template?: string;
|
|
105
|
+
/**
|
|
106
|
+
* html: with the chart; "auto" (default) when the view shows the chart
|
|
107
|
+
*/
|
|
108
|
+
chart?: boolean | "auto";
|
|
109
|
+
/**
|
|
110
|
+
* html: the chart as a data: image (default) or as inline SVG
|
|
111
|
+
*/
|
|
112
|
+
chartEmbed?: "image" | "inline";
|
|
113
|
+
/**
|
|
114
|
+
* the ECharts module (with its SVG renderer): svg, and html with a chart
|
|
115
|
+
*/
|
|
116
|
+
echarts?: {
|
|
117
|
+
init: Function;
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* of the chart in px, default 800 x 480
|
|
121
|
+
*/
|
|
122
|
+
size?: {
|
|
123
|
+
width?: number;
|
|
124
|
+
height?: number;
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* html: cells of the table at most, default REPORT_LIMITS.tableCells
|
|
128
|
+
*/
|
|
129
|
+
tableCells?: number;
|
|
130
|
+
/**
|
|
131
|
+
* csv: start with a byte order mark, so Excel reads it as UTF-8 (default true)
|
|
132
|
+
*/
|
|
133
|
+
bom?: boolean;
|
|
134
|
+
};
|
|
135
|
+
export type RenderResult = {
|
|
136
|
+
format: string;
|
|
137
|
+
mediaType: string;
|
|
138
|
+
extension: string;
|
|
139
|
+
/**
|
|
140
|
+
* the file; null when `strict` and the view has an error
|
|
141
|
+
*/
|
|
142
|
+
body: Uint8Array | null;
|
|
143
|
+
/**
|
|
144
|
+
* the body as text, for json, csv, svg and html
|
|
145
|
+
*/
|
|
146
|
+
text?: string;
|
|
147
|
+
/**
|
|
148
|
+
* the view's issues, then the report's (REPORT_ISSUE_CODES)
|
|
149
|
+
*/
|
|
150
|
+
issues: import("pivotkit-core").Issue[];
|
|
151
|
+
/**
|
|
152
|
+
* no issue of severity "error"
|
|
153
|
+
*/
|
|
154
|
+
ok: boolean;
|
|
155
|
+
/**
|
|
156
|
+
* the table's body rows and columns, the data's rows, whether a part was cut
|
|
157
|
+
*/
|
|
158
|
+
summary: {
|
|
159
|
+
rows: number;
|
|
160
|
+
columns: number;
|
|
161
|
+
sourceRows: number;
|
|
162
|
+
truncated: boolean;
|
|
163
|
+
} | null;
|
|
164
|
+
/**
|
|
165
|
+
* the normalized view that was rendered
|
|
166
|
+
*/
|
|
167
|
+
view: import("pivotkit-core").View | null;
|
|
168
|
+
};
|
|
169
|
+
/**
|
|
170
|
+
* Renders a view over data as a file.
|
|
171
|
+
* @param {unknown} view a view, a view file or a template { name, view }, as an object or JSON text
|
|
172
|
+
* @param {unknown} data rows, columnar data ({ columns, types, data }) or encode()'s result
|
|
173
|
+
* @param {RenderOptions} [options]
|
|
174
|
+
* @returns {Promise<RenderResult>}
|
|
175
|
+
*/
|
|
176
|
+
export declare function render(view: unknown, data: unknown, options?: RenderOptions): Promise<RenderResult>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Columns from which a printed table is turned to landscape, and from which its font gets smaller. */
|
|
2
|
+
export declare const PRINT_WIDE: Readonly<{
|
|
3
|
+
landscape: 8;
|
|
4
|
+
small: 16;
|
|
5
|
+
}>;
|
|
6
|
+
/**
|
|
7
|
+
* @param {{ columns: number, tokens?: Record<string, string>, extra?: string }} options
|
|
8
|
+
* columns: the table's; tokens: the host's (cleaned), over the light ones; extra: the cells' colour rules
|
|
9
|
+
*/
|
|
10
|
+
export declare function reportCss({ columns, tokens, extra }: {
|
|
11
|
+
columns: number;
|
|
12
|
+
tokens?: Record<string, string>;
|
|
13
|
+
extra?: string;
|
|
14
|
+
}): string;
|
package/types/svg.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks an SVG string and makes it safe to open and to show under a strict Content-Security-Policy:
|
|
3
|
+
* only the elements an ECharts chart draws, no event handler attribute, no link but to a fragment or
|
|
4
|
+
* a data: image, no attribute name but a plain one, no namespace but SVG's and xlink's, no text that is not escaped; every `style` attribute becomes presentation attributes
|
|
5
|
+
* (a policy without 'unsafe-inline' blocks style attributes, not those) and a <style> element (ECharts'
|
|
6
|
+
* hover states) and characters XML does not allow, also as numeric references, are left out. Throws INVALID_ARGUMENT
|
|
7
|
+
* (`details.reason`) for anything else.
|
|
8
|
+
* @param {string} svg
|
|
9
|
+
* @returns {string}
|
|
10
|
+
*/
|
|
11
|
+
export declare function cleanSvg(svg: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* The chart of a pivot as SVG, as pivotkit-react draws it: chartPlan's type (the view's, or what
|
|
14
|
+
* "auto" picks; rows and columns swapped for the chart where it says so), the theme's colours from
|
|
15
|
+
* `style`, the axis labels laid out for the size, at most `maxCategories` categories.
|
|
16
|
+
* @param {import("pivotkit-core").PivotResult} result
|
|
17
|
+
* @param {{ echarts: { init: Function }, style: { getPropertyValue(name: string): string }, width: number, height: number,
|
|
18
|
+
* locale: string, labels?: object, limits?: object, now?: any, maxCategories: number }} options
|
|
19
|
+
* @returns {{ svg: string, categories: number, of: number }}
|
|
20
|
+
*/
|
|
21
|
+
export declare function chartSvg(result: import("pivotkit-core").PivotResult, { echarts, style, width, height, locale, labels, limits, now, maxCategories }: {
|
|
22
|
+
echarts: {
|
|
23
|
+
init: Function;
|
|
24
|
+
};
|
|
25
|
+
style: {
|
|
26
|
+
getPropertyValue(name: string): string;
|
|
27
|
+
};
|
|
28
|
+
width: number;
|
|
29
|
+
height: number;
|
|
30
|
+
locale: string;
|
|
31
|
+
labels?: object;
|
|
32
|
+
limits?: object;
|
|
33
|
+
now?: any;
|
|
34
|
+
maxCategories: number;
|
|
35
|
+
}): {
|
|
36
|
+
svg: string;
|
|
37
|
+
categories: number;
|
|
38
|
+
of: number;
|
|
39
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** Characters a template may have. */
|
|
2
|
+
export declare const TEMPLATE_LIMIT = 262144;
|
|
3
|
+
/**
|
|
4
|
+
* The placeholders: text (escaped) and markup (built by pivotkit from escaped parts).
|
|
5
|
+
* @type {Readonly<Record<string, "text" | "markup">>}
|
|
6
|
+
*/
|
|
7
|
+
export declare const PLACEHOLDERS: Readonly<Record<string, "text" | "markup">>;
|
|
8
|
+
/** Attributes whose quoted value may hold a text placeholder. */
|
|
9
|
+
export declare const ATTRIBUTES: readonly string[];
|
|
10
|
+
export type Slot = {
|
|
11
|
+
name: string;
|
|
12
|
+
kind: "text" | "markup";
|
|
13
|
+
};
|
|
14
|
+
export type Template = {
|
|
15
|
+
parts: Array<string | Slot>;
|
|
16
|
+
names: string[];
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {{ name: string, kind: "text" | "markup" }} Slot
|
|
20
|
+
* @typedef {{ parts: Array<string | Slot>, names: string[] }} Template the template's text cut at its placeholders
|
|
21
|
+
*/
|
|
22
|
+
/** Escapes text for HTML text content and quoted attribute values; bidi controls are left out. */
|
|
23
|
+
export declare function escapeHtml(text: any): string;
|
|
24
|
+
/**
|
|
25
|
+
* Checks a template and cuts it at its placeholders. Throws INVALID_TEMPLATE (see above).
|
|
26
|
+
* @param {string} source
|
|
27
|
+
* @returns {Template}
|
|
28
|
+
*/
|
|
29
|
+
export declare function parseTemplate(source: string): Template;
|
|
30
|
+
/**
|
|
31
|
+
* The template with its placeholders filled: text values escaped, markup values as given (built by
|
|
32
|
+
* pivotkit). A placeholder without a value is empty.
|
|
33
|
+
* @param {Template} template from parseTemplate()
|
|
34
|
+
* @param {Partial<Record<string, string>>} values
|
|
35
|
+
* @returns {string}
|
|
36
|
+
*/
|
|
37
|
+
export declare function fillTemplate(template: Template, values: Partial<Record<string, string>>): string;
|
package/types/texts.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The characters that change the direction of text, as pivotkit-core's import leaves them out: a report
|
|
3
|
+
* writes none of them (HTML, SVG), so no name reads as another one ("invoice\u202Etxt.exe").
|
|
4
|
+
*/
|
|
5
|
+
export declare const BIDI_CONTROLS: RegExp;
|
|
6
|
+
export declare const REPORT_TEXTS: Readonly<{
|
|
7
|
+
en: Readonly<{
|
|
8
|
+
untitled: "Report";
|
|
9
|
+
source: "Source";
|
|
10
|
+
rows: "Rows";
|
|
11
|
+
rowsCount: "{count} rows";
|
|
12
|
+
generated: "Generated";
|
|
13
|
+
filters: "Filters";
|
|
14
|
+
noFilters: "None";
|
|
15
|
+
chart: "Chart";
|
|
16
|
+
tableCut: "The table shows the first {shown} of {count} rows. Export it as Excel or CSV for all of them.";
|
|
17
|
+
chartCut: "The chart shows the first {shown} of {count} categories.";
|
|
18
|
+
chartUnavailable: "The chart is left out: ECharts is not available.";
|
|
19
|
+
}>;
|
|
20
|
+
de: Readonly<{
|
|
21
|
+
untitled: "Bericht";
|
|
22
|
+
source: "Quelle";
|
|
23
|
+
rows: "Zeilen";
|
|
24
|
+
rowsCount: "{count} Zeilen";
|
|
25
|
+
generated: "Erstellt";
|
|
26
|
+
filters: "Filter";
|
|
27
|
+
noFilters: "Keine";
|
|
28
|
+
chart: "Diagramm";
|
|
29
|
+
tableCut: "Die Tabelle zeigt die ersten {shown} von {count} Zeilen. Für alle als Excel oder CSV exportieren.";
|
|
30
|
+
chartCut: "Das Diagramm zeigt die ersten {shown} von {count} Kategorien.";
|
|
31
|
+
chartUnavailable: "Das Diagramm fehlt: ECharts ist nicht verfügbar.";
|
|
32
|
+
}>;
|
|
33
|
+
}>;
|
|
34
|
+
export type ReportTexts = typeof REPORT_TEXTS.en;
|
|
35
|
+
/** @typedef {typeof REPORT_TEXTS.en} ReportTexts */
|
|
36
|
+
/**
|
|
37
|
+
* The texts for a locale, the host's over them.
|
|
38
|
+
* @param {string} locale
|
|
39
|
+
* @param {Partial<ReportTexts>} [own]
|
|
40
|
+
* @returns {ReportTexts}
|
|
41
|
+
*/
|
|
42
|
+
export declare function textsFor(locale: string, own?: Partial<ReportTexts>): ReportTexts;
|
|
43
|
+
/** "{count} rows" with its values. */
|
|
44
|
+
export declare const fill: (text: any, values: any) => any;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/** The light theme: print, and a screen in light mode. */
|
|
2
|
+
export declare const LIGHT_TOKENS: Readonly<{
|
|
3
|
+
"--pk-surface": "#ffffff";
|
|
4
|
+
"--pk-surface-2": "#f6f7f9";
|
|
5
|
+
"--pk-surface-raised": "#ffffff";
|
|
6
|
+
"--pk-text": "#1a1d23";
|
|
7
|
+
"--pk-muted": "#5a6270";
|
|
8
|
+
"--pk-border": "#dde1e7";
|
|
9
|
+
"--pk-border-strong": "#c3c9d2";
|
|
10
|
+
"--pk-grid": "#edf0f3";
|
|
11
|
+
"--pk-total": "#eef1f5";
|
|
12
|
+
"--pk-total-strong": "#e9edf3";
|
|
13
|
+
"--pk-shadow": "0 1px 2px rgb(16 24 40 / 0.06), 0 4px 12px rgb(16 24 40 / 0.08)";
|
|
14
|
+
"--pk-heat-low": "#eaf1fb";
|
|
15
|
+
"--pk-heat-high": "#2a78d6";
|
|
16
|
+
"--pk-scale-low": "var(--pk-heat-low)";
|
|
17
|
+
"--pk-scale-high": "var(--pk-heat-high)";
|
|
18
|
+
"--pk-chart-1": "#2a78d6";
|
|
19
|
+
"--pk-chart-2": "#eb6834";
|
|
20
|
+
"--pk-chart-3": "#1baf7a";
|
|
21
|
+
"--pk-chart-4": "#eda100";
|
|
22
|
+
"--pk-chart-5": "#e87ba4";
|
|
23
|
+
"--pk-chart-6": "#008300";
|
|
24
|
+
"--pk-chart-7": "#6250d6";
|
|
25
|
+
"--pk-chart-8": "#e34948";
|
|
26
|
+
}>;
|
|
27
|
+
/** The dark theme, for a report read on a screen in dark mode (never printed: print is light). */
|
|
28
|
+
export declare const DARK_TOKENS: Readonly<{
|
|
29
|
+
"--pk-surface": "#16181d";
|
|
30
|
+
"--pk-surface-2": "#1c1f25";
|
|
31
|
+
"--pk-surface-raised": "#22262d";
|
|
32
|
+
"--pk-text": "#e8eaef";
|
|
33
|
+
"--pk-muted": "#a3aab7";
|
|
34
|
+
"--pk-border": "#2f343e";
|
|
35
|
+
"--pk-border-strong": "#454c58";
|
|
36
|
+
"--pk-grid": "#23272e";
|
|
37
|
+
"--pk-total": "#21252d";
|
|
38
|
+
"--pk-total-strong": "#252a33";
|
|
39
|
+
"--pk-shadow": "0 1px 2px rgb(0 0 0 / 0.4), 0 4px 12px rgb(0 0 0 / 0.3)";
|
|
40
|
+
"--pk-heat-low": "#1d2735";
|
|
41
|
+
"--pk-heat-high": "#3987e5";
|
|
42
|
+
"--pk-scale-low": "var(--pk-heat-low)";
|
|
43
|
+
"--pk-scale-high": "var(--pk-heat-high)";
|
|
44
|
+
"--pk-chart-1": "#3987e5";
|
|
45
|
+
"--pk-chart-2": "#d95926";
|
|
46
|
+
"--pk-chart-3": "#199e70";
|
|
47
|
+
"--pk-chart-4": "#c98500";
|
|
48
|
+
"--pk-chart-5": "#d55181";
|
|
49
|
+
"--pk-chart-6": "#008300";
|
|
50
|
+
"--pk-chart-7": "#9085e9";
|
|
51
|
+
"--pk-chart-8": "#e66767";
|
|
52
|
+
}>;
|
|
53
|
+
/** The font weights, the same in both themes: pivotkit-react's scale (test/tokens.test.js compares them). */
|
|
54
|
+
export declare const WEIGHT_TOKENS: Readonly<{
|
|
55
|
+
"--pk-weight-normal": "400";
|
|
56
|
+
"--pk-weight-medium": "500";
|
|
57
|
+
"--pk-weight-strong": "600";
|
|
58
|
+
"--pk-weight-bold": "700";
|
|
59
|
+
}>;
|
|
60
|
+
/**
|
|
61
|
+
* The corners the report draws, the same in both themes: the chart's card, as pivotkit-react's tooltip
|
|
62
|
+
* (test/tokens.test.js compares them).
|
|
63
|
+
*/
|
|
64
|
+
export declare const SHAPE_TOKENS: Readonly<{
|
|
65
|
+
"--pk-radius-card": "8px";
|
|
66
|
+
}>;
|
|
67
|
+
/**
|
|
68
|
+
* The report's font sizes, the same in both themes: its text (--pk-font-size, 14 px, the report's own
|
|
69
|
+
* default: pivotkit-react's follows the page, at least 14 px, and its Export menu passes it in px),
|
|
70
|
+
* secondary text (--pk-small, pivotkit-react's: the details, never below 14 px), the title (20 px at a
|
|
71
|
+
* 14 px text) and the printed text, in pt on paper: the table and, for a very wide table, the smaller
|
|
72
|
+
* size (PRINT_WIDE.small, never below 7 pt). A host passes its own as `tokens`.
|
|
73
|
+
*/
|
|
74
|
+
export declare const SIZE_TOKENS: Readonly<{
|
|
75
|
+
"--pk-font-size": "14px";
|
|
76
|
+
"--pk-small": "max(14px, 0.93em)";
|
|
77
|
+
"--pk-report-title-size": "calc(var(--pk-font-size) * 10 / 7)";
|
|
78
|
+
"--pk-print-size": "10pt";
|
|
79
|
+
"--pk-print-small": "7pt";
|
|
80
|
+
}>;
|
|
81
|
+
/**
|
|
82
|
+
* The report's font: the system's, as the screen's report inherits from most hosts. Without quotes
|
|
83
|
+
* (names of several words are valid unquoted): ECharts 6.1 writes a font into an SVG's style attribute
|
|
84
|
+
* without escaping its quotes, which would end the attribute.
|
|
85
|
+
*/
|
|
86
|
+
export declare const REPORT_FONT = "system-ui, -apple-system, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif";
|
|
87
|
+
/**
|
|
88
|
+
* The tokens as a style the chart's code reads (getPropertyValue), the host's over the light ones.
|
|
89
|
+
* @param {Record<string, string>} [tokens]
|
|
90
|
+
* @returns {{ getPropertyValue(name: string): string }}
|
|
91
|
+
*/
|
|
92
|
+
export declare function tokenStyle(tokens?: Record<string, string>): {
|
|
93
|
+
getPropertyValue(name: string): string;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* The host's tokens that may go into CSS: --pk-* names (and font-family) with plain values that call
|
|
97
|
+
* no function but SAFE_FUNCTIONS; others are left out.
|
|
98
|
+
* @param {Record<string, string>} tokens
|
|
99
|
+
*/
|
|
100
|
+
export declare function cleanTokens(tokens: Record<string, string>): Record<string, string>;
|
|
101
|
+
/**
|
|
102
|
+
* The colours of a colour scale and of data bars whose view names none, from the host's tokens over
|
|
103
|
+
* the light ones, as the report on screen resolves them: the scale --pk-scale-low, the host's
|
|
104
|
+
* --pk-scale-mid and --pk-scale-high (by default the heat tokens), the bars --pk-chart-1. A value
|
|
105
|
+
* that is no plain hex colour (another token is followed) keeps pivotkit-core's default.
|
|
106
|
+
* @param {Record<string, string>} [tokens]
|
|
107
|
+
* @returns {{ colorScale?: string[], dataBar?: string }}
|
|
108
|
+
*/
|
|
109
|
+
export declare function conditionalColors(tokens?: Record<string, string>): {
|
|
110
|
+
colorScale?: string[];
|
|
111
|
+
dataBar?: string;
|
|
112
|
+
};
|
|
113
|
+
/** Tokens as CSS declarations. */
|
|
114
|
+
export declare const declarations: (tokens: any) => string;
|