@sentropic/dataviz-core 0.1.0 → 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/dist/aggregate.d.ts +4 -4
- package/dist/aggregate.d.ts.map +1 -1
- package/dist/aggregate.js +13 -7
- package/dist/aggregate.js.map +1 -1
- package/dist/analytics.d.ts +105 -0
- package/dist/analytics.d.ts.map +1 -0
- package/dist/analytics.js +323 -0
- package/dist/analytics.js.map +1 -0
- package/dist/categorical.d.ts +84 -0
- package/dist/categorical.d.ts.map +1 -0
- package/dist/categorical.js +158 -0
- package/dist/categorical.js.map +1 -0
- package/dist/describe.d.ts +11 -0
- package/dist/describe.d.ts.map +1 -0
- package/dist/describe.js +26 -0
- package/dist/describe.js.map +1 -0
- package/dist/distribution.d.ts +109 -0
- package/dist/distribution.d.ts.map +1 -0
- package/dist/distribution.js +231 -0
- package/dist/distribution.js.map +1 -0
- package/dist/fields.d.ts +53 -0
- package/dist/fields.d.ts.map +1 -0
- package/dist/fields.js +108 -0
- package/dist/fields.js.map +1 -0
- package/dist/geo.d.ts +151 -0
- package/dist/geo.d.ts.map +1 -0
- package/dist/geo.js +300 -0
- package/dist/geo.js.map +1 -0
- package/dist/index.d.ts +22 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -2
- package/dist/index.js.map +1 -1
- package/dist/kpi.d.ts +35 -0
- package/dist/kpi.d.ts.map +1 -0
- package/dist/kpi.js +40 -0
- package/dist/kpi.js.map +1 -0
- package/dist/model.d.ts +4 -0
- package/dist/model.d.ts.map +1 -1
- package/dist/model.js +4 -0
- package/dist/model.js.map +1 -1
- package/dist/partOfWhole.d.ts +159 -0
- package/dist/partOfWhole.d.ts.map +1 -0
- package/dist/partOfWhole.js +260 -0
- package/dist/partOfWhole.js.map +1 -0
- package/dist/pivot.d.ts +31 -0
- package/dist/pivot.d.ts.map +1 -0
- package/dist/pivot.js +121 -0
- package/dist/pivot.js.map +1 -0
- package/dist/pivotAdvanced.d.ts +47 -0
- package/dist/pivotAdvanced.d.ts.map +1 -0
- package/dist/pivotAdvanced.js +208 -0
- package/dist/pivotAdvanced.js.map +1 -0
- package/dist/serialize.d.ts +15 -3
- package/dist/serialize.d.ts.map +1 -1
- package/dist/serialize.js +55 -20
- package/dist/serialize.js.map +1 -1
- package/dist/store.d.ts +17 -4
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +120 -15
- package/dist/store.js.map +1 -1
- package/package.json +1 -1
package/dist/pivot.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pivot / matrix aggregation.
|
|
3
|
+
*
|
|
4
|
+
* The output is intentionally presentation-neutral but shaped for DS DataTable:
|
|
5
|
+
* a column list with stable keys and row objects carrying those keys.
|
|
6
|
+
*/
|
|
7
|
+
import type { DataModel, Row } from './model.js';
|
|
8
|
+
export interface PivotConfig {
|
|
9
|
+
rows: string[];
|
|
10
|
+
columns?: string[];
|
|
11
|
+
measures: string[];
|
|
12
|
+
}
|
|
13
|
+
export interface PivotTableColumn {
|
|
14
|
+
key: string;
|
|
15
|
+
label: string;
|
|
16
|
+
kind: 'row' | 'value';
|
|
17
|
+
dimensionId?: string;
|
|
18
|
+
columnKey?: string;
|
|
19
|
+
columnLabel?: string;
|
|
20
|
+
measureId?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface PivotTableRow {
|
|
23
|
+
id: string;
|
|
24
|
+
[key: string]: string | number;
|
|
25
|
+
}
|
|
26
|
+
export interface PivotTable {
|
|
27
|
+
columns: PivotTableColumn[];
|
|
28
|
+
rows: PivotTableRow[];
|
|
29
|
+
}
|
|
30
|
+
export declare function buildPivotTable(model: DataModel, data: readonly Row[], config: PivotConfig): PivotTable;
|
|
31
|
+
//# sourceMappingURL=pivot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pivot.d.ts","sourceRoot":"","sources":["../src/pivot.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAW,GAAG,EAAE,MAAM,YAAY,CAAC;AAE1D,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,KAAK,GAAG,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,IAAI,EAAE,aAAa,EAAE,CAAC;CACvB;AAoCD,wBAAgB,eAAe,CAC7B,KAAK,EAAE,SAAS,EAChB,IAAI,EAAE,SAAS,GAAG,EAAE,EACpB,MAAM,EAAE,WAAW,GAClB,UAAU,CA0FZ"}
|
package/dist/pivot.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pivot / matrix aggregation.
|
|
3
|
+
*
|
|
4
|
+
* The output is intentionally presentation-neutral but shaped for DS DataTable:
|
|
5
|
+
* a column list with stable keys and row objects carrying those keys.
|
|
6
|
+
*/
|
|
7
|
+
import { aggregate } from './aggregate.js';
|
|
8
|
+
import { findDimension, findMeasure } from './model.js';
|
|
9
|
+
function cellKey(value) {
|
|
10
|
+
return value == null ? 'null' : String(value);
|
|
11
|
+
}
|
|
12
|
+
function pathFor(row, dimensions) {
|
|
13
|
+
return dimensions.map((dimensionId) => cellKey(row[dimensionId]));
|
|
14
|
+
}
|
|
15
|
+
function pathKey(path) {
|
|
16
|
+
return path.join('\u001f');
|
|
17
|
+
}
|
|
18
|
+
function pathLabel(path) {
|
|
19
|
+
return path.join(' / ');
|
|
20
|
+
}
|
|
21
|
+
function assertDimensions(model, ids, role) {
|
|
22
|
+
for (const id of ids) {
|
|
23
|
+
if (!findDimension(model, id)) {
|
|
24
|
+
throw new Error(`Unknown pivot ${role} dimension: ${id}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function resolveMeasures(model, ids) {
|
|
29
|
+
return ids.map((id) => {
|
|
30
|
+
const measure = findMeasure(model, id);
|
|
31
|
+
if (!measure) {
|
|
32
|
+
throw new Error(`Unknown pivot measure: ${id}`);
|
|
33
|
+
}
|
|
34
|
+
return measure;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
export function buildPivotTable(model, data, config) {
|
|
38
|
+
if (config.rows.length === 0) {
|
|
39
|
+
throw new Error('Pivot requires at least one row dimension');
|
|
40
|
+
}
|
|
41
|
+
if (config.measures.length === 0) {
|
|
42
|
+
throw new Error('Pivot requires at least one measure');
|
|
43
|
+
}
|
|
44
|
+
const columnDimensions = config.columns ?? [];
|
|
45
|
+
assertDimensions(model, config.rows, 'row');
|
|
46
|
+
assertDimensions(model, columnDimensions, 'column');
|
|
47
|
+
const measures = resolveMeasures(model, config.measures);
|
|
48
|
+
const rowGroups = new Map();
|
|
49
|
+
const columnPaths = new Map();
|
|
50
|
+
for (const row of data) {
|
|
51
|
+
const rowPath = pathFor(row, config.rows);
|
|
52
|
+
const rowId = pathKey(rowPath);
|
|
53
|
+
const rowGroup = rowGroups.get(rowId);
|
|
54
|
+
if (rowGroup) {
|
|
55
|
+
rowGroup.rows.push(row);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
rowGroups.set(rowId, { path: rowPath, rows: [row] });
|
|
59
|
+
}
|
|
60
|
+
if (columnDimensions.length > 0) {
|
|
61
|
+
const columnPath = pathFor(row, columnDimensions);
|
|
62
|
+
const columnKey = pathKey(columnPath);
|
|
63
|
+
if (!columnPaths.has(columnKey)) {
|
|
64
|
+
columnPaths.set(columnKey, columnPath);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const columns = config.rows.map((dimensionId) => ({
|
|
69
|
+
key: `row:${dimensionId}`,
|
|
70
|
+
label: findDimension(model, dimensionId).label,
|
|
71
|
+
kind: 'row',
|
|
72
|
+
}));
|
|
73
|
+
if (columnDimensions.length === 0) {
|
|
74
|
+
for (const measure of measures) {
|
|
75
|
+
columns.push({
|
|
76
|
+
key: `value:${measure.id}`,
|
|
77
|
+
label: measure.label,
|
|
78
|
+
kind: 'value',
|
|
79
|
+
measureId: measure.id,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
for (const [columnKey, columnPath] of columnPaths) {
|
|
85
|
+
const columnLabel = pathLabel(columnPath);
|
|
86
|
+
for (const measure of measures) {
|
|
87
|
+
columns.push({
|
|
88
|
+
key: `value:${columnKey}:${measure.id}`,
|
|
89
|
+
label: `${columnLabel} · ${measure.label}`,
|
|
90
|
+
kind: 'value',
|
|
91
|
+
columnKey,
|
|
92
|
+
columnLabel,
|
|
93
|
+
measureId: measure.id,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const rows = [];
|
|
99
|
+
for (const [rowId, group] of rowGroups) {
|
|
100
|
+
const out = { id: rowId };
|
|
101
|
+
config.rows.forEach((dimensionId, index) => {
|
|
102
|
+
out[`row:${dimensionId}`] = group.path[index] ?? 'null';
|
|
103
|
+
});
|
|
104
|
+
if (columnDimensions.length === 0) {
|
|
105
|
+
for (const measure of measures) {
|
|
106
|
+
out[`value:${measure.id}`] = aggregate(group.rows, measure);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
for (const [columnKey, columnPath] of columnPaths) {
|
|
111
|
+
const bucket = group.rows.filter((row) => pathKey(pathFor(row, columnDimensions)) === columnKey);
|
|
112
|
+
for (const measure of measures) {
|
|
113
|
+
out[`value:${pathKey(columnPath)}:${measure.id}`] = aggregate(bucket, measure);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
rows.push(out);
|
|
118
|
+
}
|
|
119
|
+
return { columns, rows };
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=pivot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pivot.js","sourceRoot":"","sources":["../src/pivot.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Advanced pivot data contracts.
|
|
3
|
+
*
|
|
4
|
+
* This module keeps the existing flat pivot table contract stable and adds a
|
|
5
|
+
* hierarchical, metadata-rich shape for subtotals, collapsed paths, cell heat
|
|
6
|
+
* and in-cell sparklines.
|
|
7
|
+
*/
|
|
8
|
+
import type { DataModel, Row } from './model.js';
|
|
9
|
+
import type { PivotTableColumn } from './pivot.js';
|
|
10
|
+
export interface AdvancedPivotConfig {
|
|
11
|
+
rows: string[];
|
|
12
|
+
columns?: string[];
|
|
13
|
+
measures: string[];
|
|
14
|
+
includeSubtotals?: boolean;
|
|
15
|
+
collapsedRowPaths?: readonly string[];
|
|
16
|
+
heatmap?: boolean;
|
|
17
|
+
sparklineDimension?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface AdvancedPivotSparklinePoint {
|
|
20
|
+
key: string;
|
|
21
|
+
value: number;
|
|
22
|
+
}
|
|
23
|
+
export interface AdvancedPivotCell {
|
|
24
|
+
key: string;
|
|
25
|
+
measureId: string;
|
|
26
|
+
value: number;
|
|
27
|
+
columnKey?: string;
|
|
28
|
+
columnLabel?: string;
|
|
29
|
+
heat?: number;
|
|
30
|
+
sparkline?: AdvancedPivotSparklinePoint[];
|
|
31
|
+
}
|
|
32
|
+
export interface AdvancedPivotRow {
|
|
33
|
+
id: string;
|
|
34
|
+
path: string[];
|
|
35
|
+
labels: Record<string, string>;
|
|
36
|
+
kind: 'leaf' | 'subtotal';
|
|
37
|
+
depth: number;
|
|
38
|
+
expanded: boolean;
|
|
39
|
+
values: Record<string, AdvancedPivotCell>;
|
|
40
|
+
}
|
|
41
|
+
export interface AdvancedPivotTable {
|
|
42
|
+
columns: PivotTableColumn[];
|
|
43
|
+
rows: AdvancedPivotRow[];
|
|
44
|
+
heatDomain?: [number, number];
|
|
45
|
+
}
|
|
46
|
+
export declare function buildAdvancedPivotTable(model: DataModel, data: readonly Row[], config: AdvancedPivotConfig): AdvancedPivotTable;
|
|
47
|
+
//# sourceMappingURL=pivotAdvanced.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pivotAdvanced.d.ts","sourceRoot":"","sources":["../src/pivotAdvanced.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAW,GAAG,EAAE,MAAM,YAAY,CAAC;AAC1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,2BAA2B;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,2BAA2B,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,IAAI,EAAE,gBAAgB,EAAE,CAAC;IACzB,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AA6ND,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,SAAS,EAChB,IAAI,EAAE,SAAS,GAAG,EAAE,EACpB,MAAM,EAAE,mBAAmB,GAC1B,kBAAkB,CA2CpB"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Advanced pivot data contracts.
|
|
3
|
+
*
|
|
4
|
+
* This module keeps the existing flat pivot table contract stable and adds a
|
|
5
|
+
* hierarchical, metadata-rich shape for subtotals, collapsed paths, cell heat
|
|
6
|
+
* and in-cell sparklines.
|
|
7
|
+
*/
|
|
8
|
+
import { aggregate, groupAggregate } from './aggregate.js';
|
|
9
|
+
import { findDimension, findMeasure } from './model.js';
|
|
10
|
+
function cellKey(value) {
|
|
11
|
+
return value == null ? 'null' : String(value);
|
|
12
|
+
}
|
|
13
|
+
function pathFor(row, dimensions) {
|
|
14
|
+
return dimensions.map((dimensionId) => cellKey(row[dimensionId]));
|
|
15
|
+
}
|
|
16
|
+
function pathKey(path) {
|
|
17
|
+
return path.join('\u001f');
|
|
18
|
+
}
|
|
19
|
+
function pathLabel(path) {
|
|
20
|
+
return path.join(' / ');
|
|
21
|
+
}
|
|
22
|
+
function hasPathPrefix(path, prefix) {
|
|
23
|
+
if (path.length <= prefix.length)
|
|
24
|
+
return false;
|
|
25
|
+
return prefix.every((segment, index) => path[index] === segment);
|
|
26
|
+
}
|
|
27
|
+
function assertDimensions(model, ids, role) {
|
|
28
|
+
for (const id of ids) {
|
|
29
|
+
if (!findDimension(model, id)) {
|
|
30
|
+
throw new Error(`Unknown advanced pivot ${role} dimension: ${id}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function resolveMeasures(model, ids) {
|
|
35
|
+
return ids.map((id) => {
|
|
36
|
+
const measure = findMeasure(model, id);
|
|
37
|
+
if (!measure) {
|
|
38
|
+
throw new Error(`Unknown advanced pivot measure: ${id}`);
|
|
39
|
+
}
|
|
40
|
+
return measure;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function assertSparklineDimension(model, id) {
|
|
44
|
+
if (id !== undefined && !findDimension(model, id)) {
|
|
45
|
+
throw new Error(`Unknown advanced pivot sparkline dimension: ${id}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function columnPathsFor(data, columns) {
|
|
49
|
+
const paths = new Map();
|
|
50
|
+
if (columns.length === 0)
|
|
51
|
+
return paths;
|
|
52
|
+
for (const row of data) {
|
|
53
|
+
const path = pathFor(row, columns);
|
|
54
|
+
const id = pathKey(path);
|
|
55
|
+
if (!paths.has(id)) {
|
|
56
|
+
paths.set(id, path);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return paths;
|
|
60
|
+
}
|
|
61
|
+
function buildColumns(model, rowDimensions, columnPaths, measures) {
|
|
62
|
+
const columns = rowDimensions.map((dimensionId) => ({
|
|
63
|
+
key: `row:${dimensionId}`,
|
|
64
|
+
label: findDimension(model, dimensionId).label,
|
|
65
|
+
kind: 'row',
|
|
66
|
+
}));
|
|
67
|
+
if (columnPaths.size === 0) {
|
|
68
|
+
for (const measure of measures) {
|
|
69
|
+
columns.push({
|
|
70
|
+
key: `value:${measure.id}`,
|
|
71
|
+
label: measure.label,
|
|
72
|
+
kind: 'value',
|
|
73
|
+
measureId: measure.id,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return columns;
|
|
77
|
+
}
|
|
78
|
+
for (const [columnKey, columnPath] of columnPaths) {
|
|
79
|
+
const columnLabel = pathLabel(columnPath);
|
|
80
|
+
for (const measure of measures) {
|
|
81
|
+
columns.push({
|
|
82
|
+
key: `value:${columnKey}:${measure.id}`,
|
|
83
|
+
label: `${columnLabel} · ${measure.label}`,
|
|
84
|
+
kind: 'value',
|
|
85
|
+
columnKey,
|
|
86
|
+
columnLabel,
|
|
87
|
+
measureId: measure.id,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return columns;
|
|
92
|
+
}
|
|
93
|
+
function collectRowNodes(data, rowDimensions, includeSubtotals) {
|
|
94
|
+
const nodes = new Map();
|
|
95
|
+
for (const row of data) {
|
|
96
|
+
for (let depth = 1; depth <= rowDimensions.length; depth += 1) {
|
|
97
|
+
if (!includeSubtotals && depth < rowDimensions.length)
|
|
98
|
+
continue;
|
|
99
|
+
const path = pathFor(row, rowDimensions.slice(0, depth));
|
|
100
|
+
const id = pathKey(path);
|
|
101
|
+
const kind = depth === rowDimensions.length ? 'leaf' : 'subtotal';
|
|
102
|
+
const node = nodes.get(id);
|
|
103
|
+
if (node) {
|
|
104
|
+
node.rows.push(row);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
nodes.set(id, { id, path, rows: [row], kind });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return [...nodes.values()];
|
|
112
|
+
}
|
|
113
|
+
function matchesColumnPath(row, columns, path) {
|
|
114
|
+
return pathFor(row, columns).every((value, index) => value === path[index]);
|
|
115
|
+
}
|
|
116
|
+
function labelsFor(path, rowDimensions) {
|
|
117
|
+
const labels = {};
|
|
118
|
+
path.forEach((label, index) => {
|
|
119
|
+
labels[rowDimensions[index]] = label;
|
|
120
|
+
});
|
|
121
|
+
return labels;
|
|
122
|
+
}
|
|
123
|
+
function valueKey(columnKey, measure) {
|
|
124
|
+
return columnKey === undefined ? `value:${measure.id}` : `value:${columnKey}:${measure.id}`;
|
|
125
|
+
}
|
|
126
|
+
function buildValues(rows, columnDimensions, columnPaths, measures, sparklineDimension) {
|
|
127
|
+
const values = {};
|
|
128
|
+
if (columnPaths.size === 0) {
|
|
129
|
+
for (const measure of measures) {
|
|
130
|
+
const key = valueKey(undefined, measure);
|
|
131
|
+
values[key] = {
|
|
132
|
+
key,
|
|
133
|
+
measureId: measure.id,
|
|
134
|
+
value: aggregate([...rows], measure),
|
|
135
|
+
};
|
|
136
|
+
if (sparklineDimension !== undefined) {
|
|
137
|
+
values[key].sparkline = groupAggregate([...rows], sparklineDimension, measure);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return values;
|
|
141
|
+
}
|
|
142
|
+
for (const [columnKey, columnPath] of columnPaths) {
|
|
143
|
+
const bucket = rows.filter((row) => matchesColumnPath(row, columnDimensions, columnPath));
|
|
144
|
+
const columnLabel = pathLabel(columnPath);
|
|
145
|
+
for (const measure of measures) {
|
|
146
|
+
const key = valueKey(columnKey, measure);
|
|
147
|
+
values[key] = {
|
|
148
|
+
key,
|
|
149
|
+
measureId: measure.id,
|
|
150
|
+
columnKey,
|
|
151
|
+
columnLabel,
|
|
152
|
+
value: aggregate(bucket, measure),
|
|
153
|
+
};
|
|
154
|
+
if (sparklineDimension !== undefined) {
|
|
155
|
+
values[key].sparkline = groupAggregate(bucket, sparklineDimension, measure);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return values;
|
|
160
|
+
}
|
|
161
|
+
function applyHeat(rows) {
|
|
162
|
+
const cells = rows.flatMap((row) => Object.values(row.values));
|
|
163
|
+
const finite = cells.map((cell) => cell.value).filter(Number.isFinite);
|
|
164
|
+
if (finite.length === 0)
|
|
165
|
+
return undefined;
|
|
166
|
+
const min = Math.min(...finite);
|
|
167
|
+
const max = Math.max(...finite);
|
|
168
|
+
const span = max - min;
|
|
169
|
+
for (const cell of cells) {
|
|
170
|
+
cell.heat = span === 0 ? 1 : (cell.value - min) / span;
|
|
171
|
+
}
|
|
172
|
+
return [min, max];
|
|
173
|
+
}
|
|
174
|
+
function visibleNodes(nodes, collapsedRowPaths) {
|
|
175
|
+
const collapsed = collapsedRowPaths.map((id) => id.split('\u001f'));
|
|
176
|
+
return nodes.filter((node) => !collapsed.some((path) => hasPathPrefix(node.path, path)));
|
|
177
|
+
}
|
|
178
|
+
export function buildAdvancedPivotTable(model, data, config) {
|
|
179
|
+
if (config.rows.length === 0) {
|
|
180
|
+
throw new Error('Advanced pivot requires at least one row dimension');
|
|
181
|
+
}
|
|
182
|
+
if (config.measures.length === 0) {
|
|
183
|
+
throw new Error('Advanced pivot requires at least one measure');
|
|
184
|
+
}
|
|
185
|
+
const columnDimensions = config.columns ?? [];
|
|
186
|
+
assertDimensions(model, config.rows, 'row');
|
|
187
|
+
assertDimensions(model, columnDimensions, 'column');
|
|
188
|
+
assertSparklineDimension(model, config.sparklineDimension);
|
|
189
|
+
const measures = resolveMeasures(model, config.measures);
|
|
190
|
+
const columnPaths = columnPathsFor(data, columnDimensions);
|
|
191
|
+
const draftRows = visibleNodes(collectRowNodes(data, config.rows, config.includeSubtotals ?? false), config.collapsedRowPaths ?? []);
|
|
192
|
+
const rows = draftRows.map((node) => ({
|
|
193
|
+
id: node.id,
|
|
194
|
+
path: node.path,
|
|
195
|
+
labels: labelsFor(node.path, config.rows),
|
|
196
|
+
kind: node.kind,
|
|
197
|
+
depth: node.path.length - 1,
|
|
198
|
+
expanded: node.kind === 'subtotal' && !(config.collapsedRowPaths ?? []).includes(node.id),
|
|
199
|
+
values: buildValues(node.rows, columnDimensions, columnPaths, measures, config.sparklineDimension),
|
|
200
|
+
}));
|
|
201
|
+
const heatDomain = config.heatmap ? applyHeat(rows) : undefined;
|
|
202
|
+
return {
|
|
203
|
+
columns: buildColumns(model, config.rows, columnPaths, measures),
|
|
204
|
+
rows,
|
|
205
|
+
...(heatDomain ? { heatDomain } : {}),
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=pivotAdvanced.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pivotAdvanced.js","sourceRoot":"","sources":["../src/pivotAdvanced.ts"],"names":[],"mappings":"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"}
|
package/dist/serialize.d.ts
CHANGED
|
@@ -11,9 +11,7 @@
|
|
|
11
11
|
* the value stays human-inspectable and dependency-free.)
|
|
12
12
|
*/
|
|
13
13
|
import type { DataModel } from './model.js';
|
|
14
|
-
import type {
|
|
15
|
-
/** Validate that an unknown value is a well-formed {@link FilterSpec}. */
|
|
16
|
-
export declare function isFilterSpec(value: unknown): value is FilterSpec;
|
|
14
|
+
import type { DrillState, FilterState } from './store.js';
|
|
17
15
|
/**
|
|
18
16
|
* Serialise the filter portion of a state to a URL-safe string.
|
|
19
17
|
* Selections are intentionally *not* serialised here — they are ephemeral
|
|
@@ -28,4 +26,18 @@ export declare function serializeFilters(state: {
|
|
|
28
26
|
* parse error yields an empty state (never throws).
|
|
29
27
|
*/
|
|
30
28
|
export declare function deserializeFilters(encoded: string, model: DataModel): FilterState;
|
|
29
|
+
/**
|
|
30
|
+
* Serialise the drill portion of a state to a URL-safe string.
|
|
31
|
+
* Filters keep their existing bookmark channel; drill is separate so callers
|
|
32
|
+
* can opt in when bookmark semantics include drill navigation.
|
|
33
|
+
*/
|
|
34
|
+
export declare function serializeDrill(state: {
|
|
35
|
+
drill: DrillState;
|
|
36
|
+
}): string;
|
|
37
|
+
/**
|
|
38
|
+
* Parse a serialised drill string back to a {@link DrillState}, validating
|
|
39
|
+
* dimensions against the model. Unknown dimensions and malformed paths are
|
|
40
|
+
* dropped. Any parse error yields an empty state.
|
|
41
|
+
*/
|
|
42
|
+
export declare function deserializeDrill(encoded: string, model: DataModel): DrillState;
|
|
31
43
|
//# sourceMappingURL=serialize.d.ts.map
|
package/dist/serialize.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,KAAK,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,KAAK,EAAE,UAAU,EAAc,WAAW,EAAE,MAAM,YAAY,CAAC;AActE;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE;IAAE,OAAO,EAAE,WAAW,CAAA;CAAE,GAAG,MAAM,CAUxE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,WAAW,CAiBjF;AAUD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE;IAAE,KAAK,EAAE,UAAU,CAAA;CAAE,GAAG,MAAM,CAUnE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,UAAU,CAiB9E"}
|
package/dist/serialize.js
CHANGED
|
@@ -11,25 +11,7 @@
|
|
|
11
11
|
* the value stays human-inspectable and dependency-free.)
|
|
12
12
|
*/
|
|
13
13
|
import { findDimension } from './model.js';
|
|
14
|
-
|
|
15
|
-
return Array.isArray(value) && value.every((v) => typeof v === 'string');
|
|
16
|
-
}
|
|
17
|
-
/** Validate that an unknown value is a well-formed {@link FilterSpec}. */
|
|
18
|
-
export function isFilterSpec(value) {
|
|
19
|
-
if (typeof value !== 'object' || value === null)
|
|
20
|
-
return false;
|
|
21
|
-
const spec = value;
|
|
22
|
-
switch (spec.kind) {
|
|
23
|
-
case 'include':
|
|
24
|
-
case 'exclude':
|
|
25
|
-
return isStringArray(spec.values);
|
|
26
|
-
case 'range':
|
|
27
|
-
return ((spec.min === undefined || typeof spec.min === 'number') &&
|
|
28
|
-
(spec.max === undefined || typeof spec.max === 'number'));
|
|
29
|
-
default:
|
|
30
|
-
return false;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
14
|
+
import { isFilterSpec } from './store.js';
|
|
33
15
|
/** Normalise a spec into a minimal, stable object for serialisation. */
|
|
34
16
|
function normaliseSpec(spec) {
|
|
35
17
|
if (spec.kind === 'include' || spec.kind === 'exclude') {
|
|
@@ -50,7 +32,11 @@ function normaliseSpec(spec) {
|
|
|
50
32
|
export function serializeFilters(state) {
|
|
51
33
|
const normalised = {};
|
|
52
34
|
for (const key of Object.keys(state.filters).sort()) {
|
|
53
|
-
|
|
35
|
+
const spec = state.filters[key];
|
|
36
|
+
if (!isFilterSpec(spec)) {
|
|
37
|
+
throw new TypeError(`Cannot serialize malformed filter spec for dimension ${key}`);
|
|
38
|
+
}
|
|
39
|
+
normalised[key] = normaliseSpec(spec);
|
|
54
40
|
}
|
|
55
41
|
return encodeURIComponent(JSON.stringify(normalised));
|
|
56
42
|
}
|
|
@@ -81,4 +67,53 @@ export function deserializeFilters(encoded, model) {
|
|
|
81
67
|
}
|
|
82
68
|
return result;
|
|
83
69
|
}
|
|
70
|
+
function isDrillPath(value) {
|
|
71
|
+
return Array.isArray(value) && value.length > 0 && value.every((item) => typeof item === 'string');
|
|
72
|
+
}
|
|
73
|
+
function normaliseDrillPath(path) {
|
|
74
|
+
return [...path];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Serialise the drill portion of a state to a URL-safe string.
|
|
78
|
+
* Filters keep their existing bookmark channel; drill is separate so callers
|
|
79
|
+
* can opt in when bookmark semantics include drill navigation.
|
|
80
|
+
*/
|
|
81
|
+
export function serializeDrill(state) {
|
|
82
|
+
const normalised = {};
|
|
83
|
+
for (const key of Object.keys(state.drill).sort()) {
|
|
84
|
+
const path = state.drill[key];
|
|
85
|
+
if (!isDrillPath(path)) {
|
|
86
|
+
throw new TypeError(`Cannot serialize malformed drill path for view ${key}`);
|
|
87
|
+
}
|
|
88
|
+
normalised[key] = normaliseDrillPath(path);
|
|
89
|
+
}
|
|
90
|
+
return encodeURIComponent(JSON.stringify(normalised));
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Parse a serialised drill string back to a {@link DrillState}, validating
|
|
94
|
+
* dimensions against the model. Unknown dimensions and malformed paths are
|
|
95
|
+
* dropped. Any parse error yields an empty state.
|
|
96
|
+
*/
|
|
97
|
+
export function deserializeDrill(encoded, model) {
|
|
98
|
+
if (!encoded)
|
|
99
|
+
return {};
|
|
100
|
+
let raw;
|
|
101
|
+
try {
|
|
102
|
+
raw = JSON.parse(decodeURIComponent(encoded));
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return {};
|
|
106
|
+
}
|
|
107
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
|
|
108
|
+
return {};
|
|
109
|
+
const result = {};
|
|
110
|
+
for (const [viewId, path] of Object.entries(raw)) {
|
|
111
|
+
if (!isDrillPath(path))
|
|
112
|
+
continue;
|
|
113
|
+
if (!path.every((dimensionId) => findDimension(model, dimensionId)))
|
|
114
|
+
continue;
|
|
115
|
+
result[viewId] = normaliseDrillPath(path);
|
|
116
|
+
}
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
84
119
|
//# sourceMappingURL=serialize.js.map
|
package/dist/serialize.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serialize.js","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"serialize.js","sourceRoot":"","sources":["../src/serialize.ts"],"names":[],"mappings":"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"}
|
package/dist/store.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The dashboard store: an immutable, observable container for the shared
|
|
3
|
-
* inter-view state (filters + selections).
|
|
3
|
+
* inter-view state (filters + selections + drill paths).
|
|
4
4
|
*
|
|
5
5
|
* Every mutation produces a brand-new frozen {@link DashboardState} (a different
|
|
6
6
|
* object reference) and then notifies subscribers. The state is fully
|
|
@@ -33,13 +33,14 @@ export type FilterSpec = {
|
|
|
33
33
|
export type FilterState = Record<string, FilterSpec>;
|
|
34
34
|
/** Map of viewId -> selected keys. Serialisable. */
|
|
35
35
|
export type SelectionState = Record<string, string[]>;
|
|
36
|
+
/** Map of viewId -> current drill dimension path. Serialisable. */
|
|
37
|
+
export type DrillState = Record<string, string[]>;
|
|
36
38
|
/** Immutable snapshot of the dashboard's shared state. */
|
|
37
39
|
export interface DashboardState {
|
|
38
40
|
readonly filters: FilterState;
|
|
39
41
|
readonly selections: SelectionState;
|
|
42
|
+
readonly drill: DrillState;
|
|
40
43
|
}
|
|
41
|
-
/** Accepted argument to {@link DashboardStore.setFilter}. */
|
|
42
|
-
export type FilterInput = FilterSpec | ((value: Cell, row: Row) => boolean);
|
|
43
44
|
export interface DashboardStoreConfig {
|
|
44
45
|
model: DataModel;
|
|
45
46
|
data: Row[];
|
|
@@ -58,15 +59,27 @@ export interface DashboardStore {
|
|
|
58
59
|
subscribe(listener: () => void): () => void;
|
|
59
60
|
/** Set (or replace) the filter on a dimension. */
|
|
60
61
|
setFilter(dimensionId: string, spec: FilterSpec): void;
|
|
62
|
+
/** Alias for clearing one filter, matching the public store contract. */
|
|
63
|
+
clear(dimensionId: string): void;
|
|
61
64
|
/** Remove the filter on a dimension. No-op if absent. */
|
|
62
65
|
clearFilter(dimensionId: string): void;
|
|
63
66
|
/** Toggle a single selected key for a view. */
|
|
64
67
|
toggleSelection(viewId: string, key: string): void;
|
|
65
68
|
/** Clear all selections for a view. No-op if absent. */
|
|
66
69
|
clearSelection(viewId: string): void;
|
|
67
|
-
/**
|
|
70
|
+
/** Append one dimension to a view's drill path. */
|
|
71
|
+
drillDown(viewId: string, dimensionId: string): void;
|
|
72
|
+
/** Remove the latest dimension from a view's drill path. No-op if absent. */
|
|
73
|
+
drillUp(viewId: string): void;
|
|
74
|
+
/** Clear all drill state for a view. No-op if absent. */
|
|
75
|
+
clearDrill(viewId: string): void;
|
|
76
|
+
/** Reset all filters, selections and drill state. */
|
|
68
77
|
clearAll(): void;
|
|
78
|
+
/** Apply global filters and this store's cross-filter graph for one view. */
|
|
79
|
+
applyCrossfilter(viewId?: string): Row[];
|
|
69
80
|
}
|
|
81
|
+
/** Validate that an unknown value is a well-formed, serialisable {@link FilterSpec}. */
|
|
82
|
+
export declare function isFilterSpec(value: unknown): value is FilterSpec;
|
|
70
83
|
/**
|
|
71
84
|
* Create an observable, immutable dashboard store.
|
|
72
85
|
*
|