@sqlrooms/pivot 0.29.0-rc.2

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.
Files changed (66) hide show
  1. package/README.md +3 -0
  2. package/dist/PivotCellContent.d.ts +10 -0
  3. package/dist/PivotCellContent.d.ts.map +1 -0
  4. package/dist/PivotCellContent.js +37 -0
  5. package/dist/PivotCellContent.js.map +1 -0
  6. package/dist/PivotCoreSlice.d.ts +73 -0
  7. package/dist/PivotCoreSlice.d.ts.map +1 -0
  8. package/dist/PivotCoreSlice.js +175 -0
  9. package/dist/PivotCoreSlice.js.map +1 -0
  10. package/dist/PivotEditor.d.ts +66 -0
  11. package/dist/PivotEditor.d.ts.map +1 -0
  12. package/dist/PivotEditor.js +341 -0
  13. package/dist/PivotEditor.js.map +1 -0
  14. package/dist/PivotResults.d.ts +15 -0
  15. package/dist/PivotResults.d.ts.map +1 -0
  16. package/dist/PivotResults.js +102 -0
  17. package/dist/PivotResults.js.map +1 -0
  18. package/dist/PivotSlice.d.ts +7 -0
  19. package/dist/PivotSlice.d.ts.map +1 -0
  20. package/dist/PivotSlice.js +353 -0
  21. package/dist/PivotSlice.js.map +1 -0
  22. package/dist/PivotView.d.ts +3 -0
  23. package/dist/PivotView.d.ts.map +1 -0
  24. package/dist/PivotView.js +39 -0
  25. package/dist/PivotView.js.map +1 -0
  26. package/dist/TableRenderer.d.ts +14 -0
  27. package/dist/TableRenderer.d.ts.map +1 -0
  28. package/dist/TableRenderer.js +100 -0
  29. package/dist/TableRenderer.js.map +1 -0
  30. package/dist/TsvRenderer.d.ts +7 -0
  31. package/dist/TsvRenderer.d.ts.map +1 -0
  32. package/dist/TsvRenderer.js +26 -0
  33. package/dist/TsvRenderer.js.map +1 -0
  34. package/dist/aggregators.d.ts +24 -0
  35. package/dist/aggregators.d.ts.map +1 -0
  36. package/dist/aggregators.js +232 -0
  37. package/dist/aggregators.js.map +1 -0
  38. package/dist/helpers.d.ts +85 -0
  39. package/dist/helpers.d.ts.map +1 -0
  40. package/dist/helpers.js +348 -0
  41. package/dist/helpers.js.map +1 -0
  42. package/dist/index.d.ts +20 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +17 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/pivotCellRegistryEntry.d.ts +4 -0
  47. package/dist/pivotCellRegistryEntry.d.ts.map +1 -0
  48. package/dist/pivotCellRegistryEntry.js +137 -0
  49. package/dist/pivotCellRegistryEntry.js.map +1 -0
  50. package/dist/pivotCellTypes.d.ts +23 -0
  51. package/dist/pivotCellTypes.d.ts.map +1 -0
  52. package/dist/pivotCellTypes.js +14 -0
  53. package/dist/pivotCellTypes.js.map +1 -0
  54. package/dist/pivotExecution.d.ts +16 -0
  55. package/dist/pivotExecution.d.ts.map +1 -0
  56. package/dist/pivotExecution.js +49 -0
  57. package/dist/pivotExecution.js.map +1 -0
  58. package/dist/sql.d.ts +17 -0
  59. package/dist/sql.d.ts.map +1 -0
  60. package/dist/sql.js +278 -0
  61. package/dist/sql.js.map +1 -0
  62. package/dist/types.d.ts +513 -0
  63. package/dist/types.d.ts.map +1 -0
  64. package/dist/types.js +107 -0
  65. package/dist/types.js.map +1 -0
  66. package/package.json +58 -0
@@ -0,0 +1,100 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { formatAggregatorValue } from './aggregators';
3
+ import { buildPivotCellMap, buildPivotTotalsMap, buildPivotUniqueKeys, compareKeyArrays, keyId, makeHeatColor, spanSize, toNumericValues, } from './helpers';
4
+ import { getColAlias, getRowAlias } from './sql';
5
+ import { useMemo } from 'react';
6
+ export const TableRenderer = ({ config, cellRows, rowTotals, colTotals, grandTotal, heatmapMode, }) => {
7
+ const rowAliases = config.rows.map((_, index) => getRowAlias(index));
8
+ const colAliases = config.cols.map((_, index) => getColAlias(index));
9
+ const rowTotalsMap = useMemo(() => {
10
+ return buildPivotTotalsMap(rowTotals, rowAliases);
11
+ }, [rowAliases, rowTotals]);
12
+ const colTotalsMap = useMemo(() => {
13
+ return buildPivotTotalsMap(colTotals, colAliases);
14
+ }, [colAliases, colTotals]);
15
+ const rowKeys = useMemo(() => {
16
+ const source = (rowTotals?.numRows ?? 0) > 0 ? rowTotals : cellRows;
17
+ const unique = buildPivotUniqueKeys(source, rowAliases);
18
+ if (unique.length === 0) {
19
+ unique.push([]);
20
+ }
21
+ unique.sort((left, right) => {
22
+ if (config.rowOrder === 'key_a_to_z') {
23
+ return compareKeyArrays(left, right);
24
+ }
25
+ const leftValue = Number(rowTotalsMap.get(keyId(left)) ?? 0);
26
+ const rightValue = Number(rowTotalsMap.get(keyId(right)) ?? 0);
27
+ return config.rowOrder === 'value_a_to_z'
28
+ ? leftValue - rightValue
29
+ : rightValue - leftValue;
30
+ });
31
+ return unique;
32
+ }, [cellRows, config.rowOrder, rowAliases, rowTotals, rowTotalsMap]);
33
+ const colKeys = useMemo(() => {
34
+ const source = (colTotals?.numRows ?? 0) > 0 ? colTotals : cellRows;
35
+ const unique = buildPivotUniqueKeys(source, colAliases);
36
+ if (unique.length === 0) {
37
+ unique.push([]);
38
+ }
39
+ unique.sort((left, right) => {
40
+ if (config.colOrder === 'key_a_to_z') {
41
+ return compareKeyArrays(left, right);
42
+ }
43
+ const leftValue = Number(colTotalsMap.get(keyId(left)) ?? 0);
44
+ const rightValue = Number(colTotalsMap.get(keyId(right)) ?? 0);
45
+ return config.colOrder === 'value_a_to_z'
46
+ ? leftValue - rightValue
47
+ : rightValue - leftValue;
48
+ });
49
+ return unique;
50
+ }, [cellRows, colAliases, colTotals, colTotalsMap, config.colOrder]);
51
+ const cellMap = useMemo(() => {
52
+ return buildPivotCellMap(cellRows, rowAliases, colAliases);
53
+ }, [cellRows, colAliases, rowAliases]);
54
+ const valueScale = useMemo(() => toNumericValues(Array.from(cellMap.values())), [cellMap]);
55
+ const colTotalsScale = useMemo(() => toNumericValues(Array.from(colTotalsMap.values())), [colTotalsMap]);
56
+ const rowTotalsScale = useMemo(() => toNumericValues(Array.from(rowTotalsMap.values())), [rowTotalsMap]);
57
+ return (_jsx("div", { className: "bg-background overflow-auto rounded-md border", children: _jsxs("table", { className: "w-full border-collapse text-sm", children: [_jsxs("thead", { className: "bg-muted/60", children: [config.cols.map((label, columnIndex) => (_jsxs("tr", { children: [columnIndex === 0 && config.rows.length > 0 ? (_jsx("th", { colSpan: config.rows.length, rowSpan: config.cols.length, className: "border px-3 py-2" })) : null, _jsx("th", { className: "border px-3 py-2 text-left font-medium", children: label }), colKeys.map((colKey, keyIndex) => {
58
+ const span = spanSize(colKeys, keyIndex, columnIndex);
59
+ if (span === -1) {
60
+ return null;
61
+ }
62
+ return (_jsx("th", { colSpan: span, rowSpan: columnIndex === config.cols.length - 1 &&
63
+ config.rows.length > 0
64
+ ? 2
65
+ : 1, className: "border px-3 py-2 text-center font-medium", children: colKey[columnIndex] || ' ' }, `col-key-${keyIndex}-${columnIndex}`));
66
+ }), columnIndex === 0 ? (_jsx("th", { rowSpan: config.cols.length + (config.rows.length === 0 ? 0 : 1), className: "border px-3 py-2 text-right font-medium", children: "Totals" })) : null] }, `col-attr-${label}`))), config.rows.length > 0 ? (_jsxs("tr", { children: [config.rows.map((label) => (_jsx("th", { className: "border px-3 py-2 text-left font-medium", children: label }, `row-label-${label}`))), _jsx("th", { className: "border px-3 py-2 text-right font-medium", children: config.cols.length === 0 ? 'Totals' : null })] })) : null] }), _jsxs("tbody", { children: [rowKeys.map((rowKey, rowIndex) => {
67
+ const totalValue = rowTotalsMap.get(keyId(rowKey)) ?? grandTotal;
68
+ return (_jsxs("tr", { children: [rowKey.map((segment, segmentIndex) => {
69
+ const span = spanSize(rowKeys, rowIndex, segmentIndex);
70
+ if (span === -1) {
71
+ return null;
72
+ }
73
+ return (_jsx("th", { rowSpan: span, colSpan: segmentIndex === config.rows.length - 1 &&
74
+ config.cols.length > 0
75
+ ? 2
76
+ : 1, className: "bg-muted/40 border px-3 py-2 text-left font-medium", children: segment || ' ' }, `row-key-${rowIndex}-${segmentIndex}`));
77
+ }), colKeys.map((colKey) => {
78
+ const cellValue = cellMap.get(`${keyId(rowKey)}::${keyId(colKey)}`) ?? null;
79
+ const numericValue = Number(cellValue);
80
+ const style = heatmapMode === 'full'
81
+ ? makeHeatColor(Number.isFinite(numericValue) ? numericValue : null, valueScale)
82
+ : heatmapMode === 'row'
83
+ ? makeHeatColor(Number.isFinite(numericValue) ? numericValue : null, toNumericValues(colKeys.map((currentColKey) => cellMap.get(`${keyId(rowKey)}::${keyId(currentColKey)}`) ?? null)))
84
+ : heatmapMode === 'col'
85
+ ? makeHeatColor(Number.isFinite(numericValue)
86
+ ? numericValue
87
+ : null, toNumericValues(rowKeys.map((currentRowKey) => cellMap.get(`${keyId(currentRowKey)}::${keyId(colKey)}`) ?? null)))
88
+ : undefined;
89
+ return (_jsx("td", { className: "border px-3 py-2 text-right", style: style, children: formatAggregatorValue(config.aggregatorName, cellValue) }, `${keyId(rowKey)}-${keyId(colKey)}`));
90
+ }), _jsx("td", { className: "bg-muted/30 border px-3 py-2 text-right font-semibold", style: heatmapMode
91
+ ? makeHeatColor(Number(totalValue), rowTotalsScale)
92
+ : undefined, children: formatAggregatorValue(config.aggregatorName, totalValue) })] }, `row-${keyId(rowKey)}`));
93
+ }), _jsxs("tr", { children: [_jsx("th", { colSpan: config.rows.length + (config.cols.length === 0 ? 0 : 1), className: "bg-muted/40 border px-3 py-2 text-right font-semibold", children: "Totals" }), colKeys.map((colKey) => {
94
+ const totalValue = colTotalsMap.get(keyId(colKey)) ?? grandTotal;
95
+ return (_jsx("td", { className: "bg-muted/30 border px-3 py-2 text-right font-semibold", style: heatmapMode
96
+ ? makeHeatColor(Number(totalValue), colTotalsScale)
97
+ : undefined, children: formatAggregatorValue(config.aggregatorName, totalValue) }, `col-total-${keyId(colKey)}`));
98
+ }), _jsx("td", { className: "bg-muted/30 border px-3 py-2 text-right font-bold", children: formatAggregatorValue(config.aggregatorName, grandTotal) })] })] })] }) }));
99
+ };
100
+ //# sourceMappingURL=TableRenderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TableRenderer.js","sourceRoot":"","sources":["../src/TableRenderer.tsx"],"names":[],"mappings":"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","sourcesContent":["import {formatAggregatorValue} from './aggregators';\nimport {\n buildPivotCellMap,\n buildPivotTotalsMap,\n buildPivotUniqueKeys,\n compareKeyArrays,\n type HeatmapMode,\n keyId,\n makeHeatColor,\n type PivotArrowTable,\n type PivotCellValue,\n spanSize,\n toNumericValues,\n} from './helpers';\nimport {getColAlias, getRowAlias} from './sql';\nimport {PivotConfig} from './types';\nimport React, {useMemo} from 'react';\n\ntype TableRendererProps = {\n config: PivotConfig;\n cellRows: PivotArrowTable;\n rowTotals: PivotArrowTable;\n colTotals: PivotArrowTable;\n grandTotal: PivotCellValue;\n heatmapMode?: HeatmapMode;\n};\n\nexport const TableRenderer: React.FC<TableRendererProps> = ({\n config,\n cellRows,\n rowTotals,\n colTotals,\n grandTotal,\n heatmapMode,\n}) => {\n const rowAliases = config.rows.map((_, index) => getRowAlias(index));\n const colAliases = config.cols.map((_, index) => getColAlias(index));\n\n const rowTotalsMap = useMemo(() => {\n return buildPivotTotalsMap(rowTotals, rowAliases);\n }, [rowAliases, rowTotals]);\n const colTotalsMap = useMemo(() => {\n return buildPivotTotalsMap(colTotals, colAliases);\n }, [colAliases, colTotals]);\n\n const rowKeys = useMemo(() => {\n const source = (rowTotals?.numRows ?? 0) > 0 ? rowTotals : cellRows;\n const unique = buildPivotUniqueKeys(source, rowAliases);\n if (unique.length === 0) {\n unique.push([]);\n }\n unique.sort((left, right) => {\n if (config.rowOrder === 'key_a_to_z') {\n return compareKeyArrays(left, right);\n }\n const leftValue = Number(rowTotalsMap.get(keyId(left)) ?? 0);\n const rightValue = Number(rowTotalsMap.get(keyId(right)) ?? 0);\n return config.rowOrder === 'value_a_to_z'\n ? leftValue - rightValue\n : rightValue - leftValue;\n });\n return unique;\n }, [cellRows, config.rowOrder, rowAliases, rowTotals, rowTotalsMap]);\n\n const colKeys = useMemo(() => {\n const source = (colTotals?.numRows ?? 0) > 0 ? colTotals : cellRows;\n const unique = buildPivotUniqueKeys(source, colAliases);\n if (unique.length === 0) {\n unique.push([]);\n }\n unique.sort((left, right) => {\n if (config.colOrder === 'key_a_to_z') {\n return compareKeyArrays(left, right);\n }\n const leftValue = Number(colTotalsMap.get(keyId(left)) ?? 0);\n const rightValue = Number(colTotalsMap.get(keyId(right)) ?? 0);\n return config.colOrder === 'value_a_to_z'\n ? leftValue - rightValue\n : rightValue - leftValue;\n });\n return unique;\n }, [cellRows, colAliases, colTotals, colTotalsMap, config.colOrder]);\n\n const cellMap = useMemo(() => {\n return buildPivotCellMap(cellRows, rowAliases, colAliases);\n }, [cellRows, colAliases, rowAliases]);\n\n const valueScale = useMemo(\n () => toNumericValues(Array.from(cellMap.values())),\n [cellMap],\n );\n const colTotalsScale = useMemo(\n () => toNumericValues(Array.from(colTotalsMap.values())),\n [colTotalsMap],\n );\n const rowTotalsScale = useMemo(\n () => toNumericValues(Array.from(rowTotalsMap.values())),\n [rowTotalsMap],\n );\n\n return (\n <div className=\"bg-background overflow-auto rounded-md border\">\n <table className=\"w-full border-collapse text-sm\">\n <thead className=\"bg-muted/60\">\n {config.cols.map((label, columnIndex) => (\n <tr key={`col-attr-${label}`}>\n {columnIndex === 0 && config.rows.length > 0 ? (\n <th\n colSpan={config.rows.length}\n rowSpan={config.cols.length}\n className=\"border px-3 py-2\"\n />\n ) : null}\n <th className=\"border px-3 py-2 text-left font-medium\">\n {label}\n </th>\n {colKeys.map((colKey, keyIndex) => {\n const span = spanSize(colKeys, keyIndex, columnIndex);\n if (span === -1) {\n return null;\n }\n return (\n <th\n key={`col-key-${keyIndex}-${columnIndex}`}\n colSpan={span}\n rowSpan={\n columnIndex === config.cols.length - 1 &&\n config.rows.length > 0\n ? 2\n : 1\n }\n className=\"border px-3 py-2 text-center font-medium\"\n >\n {colKey[columnIndex] || ' '}\n </th>\n );\n })}\n {columnIndex === 0 ? (\n <th\n rowSpan={\n config.cols.length + (config.rows.length === 0 ? 0 : 1)\n }\n className=\"border px-3 py-2 text-right font-medium\"\n >\n Totals\n </th>\n ) : null}\n </tr>\n ))}\n {config.rows.length > 0 ? (\n <tr>\n {config.rows.map((label) => (\n <th\n key={`row-label-${label}`}\n className=\"border px-3 py-2 text-left font-medium\"\n >\n {label}\n </th>\n ))}\n <th className=\"border px-3 py-2 text-right font-medium\">\n {config.cols.length === 0 ? 'Totals' : null}\n </th>\n </tr>\n ) : null}\n </thead>\n <tbody>\n {rowKeys.map((rowKey, rowIndex) => {\n const totalValue = rowTotalsMap.get(keyId(rowKey)) ?? grandTotal;\n return (\n <tr key={`row-${keyId(rowKey)}`}>\n {rowKey.map((segment, segmentIndex) => {\n const span = spanSize(rowKeys, rowIndex, segmentIndex);\n if (span === -1) {\n return null;\n }\n return (\n <th\n key={`row-key-${rowIndex}-${segmentIndex}`}\n rowSpan={span}\n colSpan={\n segmentIndex === config.rows.length - 1 &&\n config.cols.length > 0\n ? 2\n : 1\n }\n className=\"bg-muted/40 border px-3 py-2 text-left font-medium\"\n >\n {segment || ' '}\n </th>\n );\n })}\n {colKeys.map((colKey) => {\n const cellValue =\n cellMap.get(`${keyId(rowKey)}::${keyId(colKey)}`) ?? null;\n const numericValue = Number(cellValue);\n const style =\n heatmapMode === 'full'\n ? makeHeatColor(\n Number.isFinite(numericValue) ? numericValue : null,\n valueScale,\n )\n : heatmapMode === 'row'\n ? makeHeatColor(\n Number.isFinite(numericValue) ? numericValue : null,\n toNumericValues(\n colKeys.map(\n (currentColKey) =>\n cellMap.get(\n `${keyId(rowKey)}::${keyId(currentColKey)}`,\n ) ?? null,\n ),\n ),\n )\n : heatmapMode === 'col'\n ? makeHeatColor(\n Number.isFinite(numericValue)\n ? numericValue\n : null,\n toNumericValues(\n rowKeys.map(\n (currentRowKey) =>\n cellMap.get(\n `${keyId(currentRowKey)}::${keyId(colKey)}`,\n ) ?? null,\n ),\n ),\n )\n : undefined;\n return (\n <td\n key={`${keyId(rowKey)}-${keyId(colKey)}`}\n className=\"border px-3 py-2 text-right\"\n style={style}\n >\n {formatAggregatorValue(config.aggregatorName, cellValue)}\n </td>\n );\n })}\n <td\n className=\"bg-muted/30 border px-3 py-2 text-right font-semibold\"\n style={\n heatmapMode\n ? makeHeatColor(Number(totalValue), rowTotalsScale)\n : undefined\n }\n >\n {formatAggregatorValue(config.aggregatorName, totalValue)}\n </td>\n </tr>\n );\n })}\n <tr>\n <th\n colSpan={config.rows.length + (config.cols.length === 0 ? 0 : 1)}\n className=\"bg-muted/40 border px-3 py-2 text-right font-semibold\"\n >\n Totals\n </th>\n {colKeys.map((colKey) => {\n const totalValue = colTotalsMap.get(keyId(colKey)) ?? grandTotal;\n return (\n <td\n key={`col-total-${keyId(colKey)}`}\n className=\"bg-muted/30 border px-3 py-2 text-right font-semibold\"\n style={\n heatmapMode\n ? makeHeatColor(Number(totalValue), colTotalsScale)\n : undefined\n }\n >\n {formatAggregatorValue(config.aggregatorName, totalValue)}\n </td>\n );\n })}\n <td className=\"bg-muted/30 border px-3 py-2 text-right font-bold\">\n {formatAggregatorValue(config.aggregatorName, grandTotal)}\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n );\n};\n"]}
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ type TsvRendererProps = {
3
+ query: string;
4
+ };
5
+ export declare const TsvRenderer: React.FC<TsvRendererProps>;
6
+ export {};
7
+ //# sourceMappingURL=TsvRenderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TsvRenderer.d.ts","sourceRoot":"","sources":["../src/TsvRenderer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAgB,MAAM,OAAO,CAAC;AAIrC,KAAK,gBAAgB,GAAG;IACtB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CA0BlD,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useSql } from '@sqlrooms/duckdb';
3
+ import { ErrorPane, SpinnerPane, Textarea } from '@sqlrooms/ui';
4
+ import { useMemo } from 'react';
5
+ export const TsvRenderer = ({ query }) => {
6
+ const exportResult = useSql({ query, enabled: Boolean(query) });
7
+ const value = useMemo(() => {
8
+ if (!exportResult.data?.arrowTable) {
9
+ return '';
10
+ }
11
+ const headers = exportResult.data.arrowTable.schema.fields.map((field) => field.name);
12
+ const lines = [headers.join('\t')];
13
+ for (const row of exportResult.data) {
14
+ lines.push(headers.map((header) => String(row[header] ?? '')).join('\t'));
15
+ }
16
+ return lines.join('\n');
17
+ }, [exportResult.data]);
18
+ if (exportResult.error) {
19
+ return _jsx(ErrorPane, { error: exportResult.error });
20
+ }
21
+ if (exportResult.isLoading) {
22
+ return _jsx(SpinnerPane, { className: "h-80" });
23
+ }
24
+ return (_jsx(Textarea, { className: "min-h-80 font-mono text-xs", readOnly: true, value: value }));
25
+ };
26
+ //# sourceMappingURL=TsvRenderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TsvRenderer.js","sourceRoot":"","sources":["../src/TsvRenderer.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAC,MAAM,EAAC,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAC,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAC,MAAM,cAAc,CAAC;AAC9D,OAAc,EAAC,OAAO,EAAC,MAAM,OAAO,CAAC;AAQrC,MAAM,CAAC,MAAM,WAAW,GAA+B,CAAC,EAAC,KAAK,EAAC,EAAE,EAAE;IACjE,MAAM,YAAY,GAAG,MAAM,CAAW,EAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,EAAC,CAAC,CAAC;IACxE,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,EAAE;QACzB,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC;YACnC,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAC5D,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CACtB,CAAC;QACF,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACnC,KAAK,MAAM,GAAG,IAAI,YAAY,CAAC,IAAI,EAAE,CAAC;YACpC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC5E,CAAC;QACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;IAExB,IAAI,YAAY,CAAC,KAAK,EAAE,CAAC;QACvB,OAAO,KAAC,SAAS,IAAC,KAAK,EAAE,YAAY,CAAC,KAAK,GAAI,CAAC;IAClD,CAAC;IACD,IAAI,YAAY,CAAC,SAAS,EAAE,CAAC;QAC3B,OAAO,KAAC,WAAW,IAAC,SAAS,EAAC,MAAM,GAAG,CAAC;IAC1C,CAAC;IAED,OAAO,CACL,KAAC,QAAQ,IAAC,SAAS,EAAC,4BAA4B,EAAC,QAAQ,QAAC,KAAK,EAAE,KAAK,GAAI,CAC3E,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import {useSql} from '@sqlrooms/duckdb';\nimport {ErrorPane, SpinnerPane, Textarea} from '@sqlrooms/ui';\nimport React, {useMemo} from 'react';\n\ntype PivotRow = Record<string, unknown>;\n\ntype TsvRendererProps = {\n query: string;\n};\n\nexport const TsvRenderer: React.FC<TsvRendererProps> = ({query}) => {\n const exportResult = useSql<PivotRow>({query, enabled: Boolean(query)});\n const value = useMemo(() => {\n if (!exportResult.data?.arrowTable) {\n return '';\n }\n const headers = exportResult.data.arrowTable.schema.fields.map(\n (field) => field.name,\n );\n const lines = [headers.join('\\t')];\n for (const row of exportResult.data) {\n lines.push(headers.map((header) => String(row[header] ?? '')).join('\\t'));\n }\n return lines.join('\\n');\n }, [exportResult.data]);\n\n if (exportResult.error) {\n return <ErrorPane error={exportResult.error} />;\n }\n if (exportResult.isLoading) {\n return <SpinnerPane className=\"h-80\" />;\n }\n\n return (\n <Textarea className=\"min-h-80 font-mono text-xs\" readOnly value={value} />\n );\n};\n"]}
@@ -0,0 +1,24 @@
1
+ export type PivotAggregatorKind = 'default' | 'fraction_total' | 'fraction_row' | 'fraction_col';
2
+ export type PivotValueRequirement = 'any' | 'numeric';
3
+ export type PivotAggregatorDefinition = {
4
+ name: string;
5
+ numInputs: number;
6
+ kind: PivotAggregatorKind;
7
+ valueRequirement: PivotValueRequirement;
8
+ format: (value: unknown) => string;
9
+ buildSql: (valueColumns: string[]) => string;
10
+ };
11
+ export declare const PIVOT_AGGREGATORS: Record<string, PivotAggregatorDefinition>;
12
+ export declare const DEFAULT_PIVOT_AGGREGATOR = "Count";
13
+ export declare function getPivotAggregator(name: string): PivotAggregatorDefinition;
14
+ export declare function getAggregatorLabel(aggregatorName: string, values: string[]): string;
15
+ export declare function getDefaultValuesForAggregator(args: {
16
+ aggregatorName: string;
17
+ fields: Array<{
18
+ name: string;
19
+ type: string;
20
+ }>;
21
+ currentValues: string[];
22
+ }): string[];
23
+ export declare function formatAggregatorValue(aggregatorName: string, value: unknown): string;
24
+ //# sourceMappingURL=aggregators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aggregators.d.ts","sourceRoot":"","sources":["../src/aggregators.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,mBAAmB,GAC3B,SAAS,GACT,gBAAgB,GAChB,cAAc,GACd,cAAc,CAAC;AAEnB,MAAM,MAAM,qBAAqB,GAAG,KAAK,GAAG,SAAS,CAAC;AAEtD,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,mBAAmB,CAAC;IAC1B,gBAAgB,EAAE,qBAAqB,CAAC;IACxC,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;IACnC,QAAQ,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,MAAM,CAAC;CAC9C,CAAC;AAyCF,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,yBAAyB,CAiLvE,CAAC;AAEF,eAAO,MAAM,wBAAwB,UAAU,CAAC;AAEhD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,yBAAyB,CAI1E;AAED,wBAAgB,kBAAkB,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAM1E;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE;IAClD,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,KAAK,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,CAAC,CAAC;IAC5C,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB,YAwBA;AAED,wBAAgB,qBAAqB,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,UAK3E"}
@@ -0,0 +1,232 @@
1
+ import { escapeId } from '@sqlrooms/duckdb';
2
+ const formatInteger = new Intl.NumberFormat('en-US', {
3
+ maximumFractionDigits: 0,
4
+ });
5
+ const formatDecimal = new Intl.NumberFormat('en-US', {
6
+ minimumFractionDigits: 0,
7
+ maximumFractionDigits: 2,
8
+ });
9
+ const formatPercent = new Intl.NumberFormat('en-US', {
10
+ style: 'percent',
11
+ minimumFractionDigits: 1,
12
+ maximumFractionDigits: 1,
13
+ });
14
+ function formatNumber(value, formatter) {
15
+ const numeric = typeof value === 'bigint' ? Number(value) : Number(value);
16
+ if (!Number.isFinite(numeric)) {
17
+ return '';
18
+ }
19
+ return formatter.format(numeric);
20
+ }
21
+ function formatIdentity(value) {
22
+ if (value === null || value === undefined) {
23
+ return '';
24
+ }
25
+ return String(value);
26
+ }
27
+ function escapeColumn(column) {
28
+ return escapeId(column);
29
+ }
30
+ function requireColumn(column, position) {
31
+ if (!column) {
32
+ throw new Error(`Aggregator requires value column ${position + 1}`);
33
+ }
34
+ return column;
35
+ }
36
+ export const PIVOT_AGGREGATORS = {
37
+ Count: {
38
+ name: 'Count',
39
+ numInputs: 0,
40
+ kind: 'default',
41
+ valueRequirement: 'any',
42
+ format: (value) => formatNumber(value, formatInteger),
43
+ buildSql: () => 'COUNT(*)',
44
+ },
45
+ 'Count Unique Values': {
46
+ name: 'Count Unique Values',
47
+ numInputs: 1,
48
+ kind: 'default',
49
+ valueRequirement: 'any',
50
+ format: (value) => formatNumber(value, formatInteger),
51
+ buildSql: ([column]) => `COUNT(DISTINCT ${escapeColumn(requireColumn(column, 0))})`,
52
+ },
53
+ 'List Unique Values': {
54
+ name: 'List Unique Values',
55
+ numInputs: 1,
56
+ kind: 'default',
57
+ valueRequirement: 'any',
58
+ format: formatIdentity,
59
+ buildSql: ([column]) => `STRING_AGG(DISTINCT CAST(${escapeColumn(requireColumn(column, 0))} AS VARCHAR), ', ' ORDER BY CAST(${escapeColumn(requireColumn(column, 0))} AS VARCHAR))`,
60
+ },
61
+ Sum: {
62
+ name: 'Sum',
63
+ numInputs: 1,
64
+ kind: 'default',
65
+ valueRequirement: 'numeric',
66
+ format: (value) => formatNumber(value, formatDecimal),
67
+ buildSql: ([column]) => `SUM(TRY_CAST(${escapeColumn(requireColumn(column, 0))} AS DOUBLE))`,
68
+ },
69
+ 'Integer Sum': {
70
+ name: 'Integer Sum',
71
+ numInputs: 1,
72
+ kind: 'default',
73
+ valueRequirement: 'numeric',
74
+ format: (value) => formatNumber(value, formatInteger),
75
+ buildSql: ([column]) => `SUM(TRY_CAST(${escapeColumn(requireColumn(column, 0))} AS DOUBLE))`,
76
+ },
77
+ Average: {
78
+ name: 'Average',
79
+ numInputs: 1,
80
+ kind: 'default',
81
+ valueRequirement: 'numeric',
82
+ format: (value) => formatNumber(value, formatDecimal),
83
+ buildSql: ([column]) => `AVG(TRY_CAST(${escapeColumn(requireColumn(column, 0))} AS DOUBLE))`,
84
+ },
85
+ Median: {
86
+ name: 'Median',
87
+ numInputs: 1,
88
+ kind: 'default',
89
+ valueRequirement: 'numeric',
90
+ format: (value) => formatNumber(value, formatDecimal),
91
+ buildSql: ([column]) => `MEDIAN(TRY_CAST(${escapeColumn(requireColumn(column, 0))} AS DOUBLE))`,
92
+ },
93
+ 'Sample Variance': {
94
+ name: 'Sample Variance',
95
+ numInputs: 1,
96
+ kind: 'default',
97
+ valueRequirement: 'numeric',
98
+ format: (value) => formatNumber(value, formatDecimal),
99
+ buildSql: ([column]) => `VAR_SAMP(TRY_CAST(${escapeColumn(requireColumn(column, 0))} AS DOUBLE))`,
100
+ },
101
+ 'Sample Standard Deviation': {
102
+ name: 'Sample Standard Deviation',
103
+ numInputs: 1,
104
+ kind: 'default',
105
+ valueRequirement: 'numeric',
106
+ format: (value) => formatNumber(value, formatDecimal),
107
+ buildSql: ([column]) => `STDDEV_SAMP(TRY_CAST(${escapeColumn(requireColumn(column, 0))} AS DOUBLE))`,
108
+ },
109
+ Minimum: {
110
+ name: 'Minimum',
111
+ numInputs: 1,
112
+ kind: 'default',
113
+ valueRequirement: 'numeric',
114
+ format: (value) => formatNumber(value, formatDecimal),
115
+ buildSql: ([column]) => `MIN(TRY_CAST(${escapeColumn(requireColumn(column, 0))} AS DOUBLE))`,
116
+ },
117
+ Maximum: {
118
+ name: 'Maximum',
119
+ numInputs: 1,
120
+ kind: 'default',
121
+ valueRequirement: 'numeric',
122
+ format: (value) => formatNumber(value, formatDecimal),
123
+ buildSql: ([column]) => `MAX(TRY_CAST(${escapeColumn(requireColumn(column, 0))} AS DOUBLE))`,
124
+ },
125
+ First: {
126
+ name: 'First',
127
+ numInputs: 1,
128
+ kind: 'default',
129
+ valueRequirement: 'any',
130
+ format: formatIdentity,
131
+ buildSql: ([column]) => `FIRST(CAST(${escapeColumn(requireColumn(column, 0))} AS VARCHAR))`,
132
+ },
133
+ Last: {
134
+ name: 'Last',
135
+ numInputs: 1,
136
+ kind: 'default',
137
+ valueRequirement: 'any',
138
+ format: formatIdentity,
139
+ buildSql: ([column]) => `LAST(CAST(${escapeColumn(requireColumn(column, 0))} AS VARCHAR))`,
140
+ },
141
+ 'Sum over Sum': {
142
+ name: 'Sum over Sum',
143
+ numInputs: 2,
144
+ kind: 'default',
145
+ valueRequirement: 'numeric',
146
+ format: (value) => formatNumber(value, formatDecimal),
147
+ buildSql: ([numerator, denominator]) => `SUM(TRY_CAST(${escapeColumn(requireColumn(numerator, 0))} AS DOUBLE)) / NULLIF(SUM(TRY_CAST(${escapeColumn(requireColumn(denominator, 1))} AS DOUBLE)), 0)`,
148
+ },
149
+ 'Sum as Fraction of Total': {
150
+ name: 'Sum as Fraction of Total',
151
+ numInputs: 1,
152
+ kind: 'fraction_total',
153
+ valueRequirement: 'numeric',
154
+ format: (value) => formatNumber(value, formatPercent),
155
+ buildSql: ([column]) => `SUM(TRY_CAST(${escapeColumn(requireColumn(column, 0))} AS DOUBLE))`,
156
+ },
157
+ 'Sum as Fraction of Rows': {
158
+ name: 'Sum as Fraction of Rows',
159
+ numInputs: 1,
160
+ kind: 'fraction_row',
161
+ valueRequirement: 'numeric',
162
+ format: (value) => formatNumber(value, formatPercent),
163
+ buildSql: ([column]) => `SUM(TRY_CAST(${escapeColumn(requireColumn(column, 0))} AS DOUBLE))`,
164
+ },
165
+ 'Sum as Fraction of Columns': {
166
+ name: 'Sum as Fraction of Columns',
167
+ numInputs: 1,
168
+ kind: 'fraction_col',
169
+ valueRequirement: 'numeric',
170
+ format: (value) => formatNumber(value, formatPercent),
171
+ buildSql: ([column]) => `SUM(TRY_CAST(${escapeColumn(requireColumn(column, 0))} AS DOUBLE))`,
172
+ },
173
+ 'Count as Fraction of Total': {
174
+ name: 'Count as Fraction of Total',
175
+ numInputs: 0,
176
+ kind: 'fraction_total',
177
+ valueRequirement: 'any',
178
+ format: (value) => formatNumber(value, formatPercent),
179
+ buildSql: () => 'COUNT(*)',
180
+ },
181
+ 'Count as Fraction of Rows': {
182
+ name: 'Count as Fraction of Rows',
183
+ numInputs: 0,
184
+ kind: 'fraction_row',
185
+ valueRequirement: 'any',
186
+ format: (value) => formatNumber(value, formatPercent),
187
+ buildSql: () => 'COUNT(*)',
188
+ },
189
+ 'Count as Fraction of Columns': {
190
+ name: 'Count as Fraction of Columns',
191
+ numInputs: 0,
192
+ kind: 'fraction_col',
193
+ valueRequirement: 'any',
194
+ format: (value) => formatNumber(value, formatPercent),
195
+ buildSql: () => 'COUNT(*)',
196
+ },
197
+ };
198
+ export const DEFAULT_PIVOT_AGGREGATOR = 'Count';
199
+ export function getPivotAggregator(name) {
200
+ return (PIVOT_AGGREGATORS[name] || PIVOT_AGGREGATORS[DEFAULT_PIVOT_AGGREGATOR]);
201
+ }
202
+ export function getAggregatorLabel(aggregatorName, values) {
203
+ const aggregator = getPivotAggregator(aggregatorName);
204
+ if (aggregator.numInputs <= 0) {
205
+ return aggregator.name;
206
+ }
207
+ return `${aggregator.name} of ${values.slice(0, aggregator.numInputs).join(', ')}`;
208
+ }
209
+ export function getDefaultValuesForAggregator(args) {
210
+ const { aggregatorName, fields, currentValues } = args;
211
+ const aggregator = getPivotAggregator(aggregatorName);
212
+ const candidates = aggregator.valueRequirement === 'numeric'
213
+ ? fields.filter((field) => /INT|DECIMAL|FLOAT|DOUBLE|REAL|HUGEINT|BIGINT/i.test(field.type))
214
+ : fields;
215
+ const selected = currentValues.filter((value) => candidates.some((field) => field.name === value));
216
+ for (const field of candidates) {
217
+ if (selected.length >= aggregator.numInputs) {
218
+ break;
219
+ }
220
+ if (!selected.includes(field.name)) {
221
+ selected.push(field.name);
222
+ }
223
+ }
224
+ return selected.slice(0, aggregator.numInputs);
225
+ }
226
+ export function formatAggregatorValue(aggregatorName, value) {
227
+ if (value === null || value === undefined) {
228
+ return '';
229
+ }
230
+ return getPivotAggregator(aggregatorName).format(value);
231
+ }
232
+ //# sourceMappingURL=aggregators.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aggregators.js","sourceRoot":"","sources":["../src/aggregators.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,kBAAkB,CAAC;AAmB1C,MAAM,aAAa,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE;IACnD,qBAAqB,EAAE,CAAC;CACzB,CAAC,CAAC;AACH,MAAM,aAAa,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE;IACnD,qBAAqB,EAAE,CAAC;IACxB,qBAAqB,EAAE,CAAC;CACzB,CAAC,CAAC;AACH,MAAM,aAAa,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE;IACnD,KAAK,EAAE,SAAS;IAChB,qBAAqB,EAAE,CAAC;IACxB,qBAAqB,EAAE,CAAC;CACzB,CAAC,CAAC;AAEH,SAAS,YAAY,CAAC,KAAc,EAAE,SAA4B;IAChE,MAAM,OAAO,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC1E,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9B,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AACnC,CAAC;AAED,SAAS,cAAc,CAAC,KAAc;IACpC,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1C,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED,SAAS,YAAY,CAAC,MAAc;IAClC,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,aAAa,CAAC,MAA0B,EAAE,QAAgB;IACjE,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,oCAAoC,QAAQ,GAAG,CAAC,EAAE,CAAC,CAAC;IACtE,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,MAAM,iBAAiB,GAA8C;IAC1E,KAAK,EAAE;QACL,IAAI,EAAE,OAAO;QACb,SAAS,EAAE,CAAC;QACZ,IAAI,EAAE,SAAS;QACf,gBAAgB,EAAE,KAAK;QACvB,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,aAAa,CAAC;QACrD,QAAQ,EAAE,GAAG,EAAE,CAAC,UAAU;KAC3B;IACD,qBAAqB,EAAE;QACrB,IAAI,EAAE,qBAAqB;QAC3B,SAAS,EAAE,CAAC;QACZ,IAAI,EAAE,SAAS;QACf,gBAAgB,EAAE,KAAK;QACvB,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,aAAa,CAAC;QACrD,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CACrB,kBAAkB,YAAY,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,GAAG;KAC9D;IACD,oBAAoB,EAAE;QACpB,IAAI,EAAE,oBAAoB;QAC1B,SAAS,EAAE,CAAC;QACZ,IAAI,EAAE,SAAS;QACf,gBAAgB,EAAE,KAAK;QACvB,MAAM,EAAE,cAAc;QACtB,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CACrB,4BAA4B,YAAY,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,oCAAoC,YAAY,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,eAAe;KAC9J;IACD,GAAG,EAAE;QACH,IAAI,EAAE,KAAK;QACX,SAAS,EAAE,CAAC;QACZ,IAAI,EAAE,SAAS;QACf,gBAAgB,EAAE,SAAS;QAC3B,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,aAAa,CAAC;QACrD,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CACrB,gBAAgB,YAAY,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,cAAc;KACvE;IACD,aAAa,EAAE;QACb,IAAI,EAAE,aAAa;QACnB,SAAS,EAAE,CAAC;QACZ,IAAI,EAAE,SAAS;QACf,gBAAgB,EAAE,SAAS;QAC3B,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,aAAa,CAAC;QACrD,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CACrB,gBAAgB,YAAY,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,cAAc;KACvE;IACD,OAAO,EAAE;QACP,IAAI,EAAE,SAAS;QACf,SAAS,EAAE,CAAC;QACZ,IAAI,EAAE,SAAS;QACf,gBAAgB,EAAE,SAAS;QAC3B,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,aAAa,CAAC;QACrD,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CACrB,gBAAgB,YAAY,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,cAAc;KACvE;IACD,MAAM,EAAE;QACN,IAAI,EAAE,QAAQ;QACd,SAAS,EAAE,CAAC;QACZ,IAAI,EAAE,SAAS;QACf,gBAAgB,EAAE,SAAS;QAC3B,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,aAAa,CAAC;QACrD,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CACrB,mBAAmB,YAAY,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,cAAc;KAC1E;IACD,iBAAiB,EAAE;QACjB,IAAI,EAAE,iBAAiB;QACvB,SAAS,EAAE,CAAC;QACZ,IAAI,EAAE,SAAS;QACf,gBAAgB,EAAE,SAAS;QAC3B,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,aAAa,CAAC;QACrD,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CACrB,qBAAqB,YAAY,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,cAAc;KAC5E;IACD,2BAA2B,EAAE;QAC3B,IAAI,EAAE,2BAA2B;QACjC,SAAS,EAAE,CAAC;QACZ,IAAI,EAAE,SAAS;QACf,gBAAgB,EAAE,SAAS;QAC3B,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,aAAa,CAAC;QACrD,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CACrB,wBAAwB,YAAY,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,cAAc;KAC/E;IACD,OAAO,EAAE;QACP,IAAI,EAAE,SAAS;QACf,SAAS,EAAE,CAAC;QACZ,IAAI,EAAE,SAAS;QACf,gBAAgB,EAAE,SAAS;QAC3B,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,aAAa,CAAC;QACrD,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CACrB,gBAAgB,YAAY,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,cAAc;KACvE;IACD,OAAO,EAAE;QACP,IAAI,EAAE,SAAS;QACf,SAAS,EAAE,CAAC;QACZ,IAAI,EAAE,SAAS;QACf,gBAAgB,EAAE,SAAS;QAC3B,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,aAAa,CAAC;QACrD,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CACrB,gBAAgB,YAAY,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,cAAc;KACvE;IACD,KAAK,EAAE;QACL,IAAI,EAAE,OAAO;QACb,SAAS,EAAE,CAAC;QACZ,IAAI,EAAE,SAAS;QACf,gBAAgB,EAAE,KAAK;QACvB,MAAM,EAAE,cAAc;QACtB,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CACrB,cAAc,YAAY,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,eAAe;KACtE;IACD,IAAI,EAAE;QACJ,IAAI,EAAE,MAAM;QACZ,SAAS,EAAE,CAAC;QACZ,IAAI,EAAE,SAAS;QACf,gBAAgB,EAAE,KAAK;QACvB,MAAM,EAAE,cAAc;QACtB,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CACrB,aAAa,YAAY,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,eAAe;KACrE;IACD,cAAc,EAAE;QACd,IAAI,EAAE,cAAc;QACpB,SAAS,EAAE,CAAC;QACZ,IAAI,EAAE,SAAS;QACf,gBAAgB,EAAE,SAAS;QAC3B,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,aAAa,CAAC;QACrD,QAAQ,EAAE,CAAC,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,EAAE,CACrC,gBAAgB,YAAY,CAAC,aAAa,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,sCAAsC,YAAY,CAAC,aAAa,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,kBAAkB;KAC/J;IACD,0BAA0B,EAAE;QAC1B,IAAI,EAAE,0BAA0B;QAChC,SAAS,EAAE,CAAC;QACZ,IAAI,EAAE,gBAAgB;QACtB,gBAAgB,EAAE,SAAS;QAC3B,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,aAAa,CAAC;QACrD,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CACrB,gBAAgB,YAAY,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,cAAc;KACvE;IACD,yBAAyB,EAAE;QACzB,IAAI,EAAE,yBAAyB;QAC/B,SAAS,EAAE,CAAC;QACZ,IAAI,EAAE,cAAc;QACpB,gBAAgB,EAAE,SAAS;QAC3B,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,aAAa,CAAC;QACrD,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CACrB,gBAAgB,YAAY,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,cAAc;KACvE;IACD,4BAA4B,EAAE;QAC5B,IAAI,EAAE,4BAA4B;QAClC,SAAS,EAAE,CAAC;QACZ,IAAI,EAAE,cAAc;QACpB,gBAAgB,EAAE,SAAS;QAC3B,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,aAAa,CAAC;QACrD,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CACrB,gBAAgB,YAAY,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,cAAc;KACvE;IACD,4BAA4B,EAAE;QAC5B,IAAI,EAAE,4BAA4B;QAClC,SAAS,EAAE,CAAC;QACZ,IAAI,EAAE,gBAAgB;QACtB,gBAAgB,EAAE,KAAK;QACvB,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,aAAa,CAAC;QACrD,QAAQ,EAAE,GAAG,EAAE,CAAC,UAAU;KAC3B;IACD,2BAA2B,EAAE;QAC3B,IAAI,EAAE,2BAA2B;QACjC,SAAS,EAAE,CAAC;QACZ,IAAI,EAAE,cAAc;QACpB,gBAAgB,EAAE,KAAK;QACvB,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,aAAa,CAAC;QACrD,QAAQ,EAAE,GAAG,EAAE,CAAC,UAAU;KAC3B;IACD,8BAA8B,EAAE;QAC9B,IAAI,EAAE,8BAA8B;QACpC,SAAS,EAAE,CAAC;QACZ,IAAI,EAAE,cAAc;QACpB,gBAAgB,EAAE,KAAK;QACvB,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,aAAa,CAAC;QACrD,QAAQ,EAAE,GAAG,EAAE,CAAC,UAAU;KAC3B;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,wBAAwB,GAAG,OAAO,CAAC;AAEhD,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,OAAO,CACL,iBAAiB,CAAC,IAAI,CAAC,IAAI,iBAAiB,CAAC,wBAAwB,CAAE,CACxE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,cAAsB,EAAE,MAAgB;IACzE,MAAM,UAAU,GAAG,kBAAkB,CAAC,cAAc,CAAC,CAAC;IACtD,IAAI,UAAU,CAAC,SAAS,IAAI,CAAC,EAAE,CAAC;QAC9B,OAAO,UAAU,CAAC,IAAI,CAAC;IACzB,CAAC;IACD,OAAO,GAAG,UAAU,CAAC,IAAI,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AACrF,CAAC;AAED,MAAM,UAAU,6BAA6B,CAAC,IAI7C;IACC,MAAM,EAAC,cAAc,EAAE,MAAM,EAAE,aAAa,EAAC,GAAG,IAAI,CAAC;IACrD,MAAM,UAAU,GAAG,kBAAkB,CAAC,cAAc,CAAC,CAAC;IACtD,MAAM,UAAU,GACd,UAAU,CAAC,gBAAgB,KAAK,SAAS;QACvC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CACtB,+CAA+C,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CACjE;QACH,CAAC,CAAC,MAAM,CAAC;IAEb,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAC9C,UAAU,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,CACjD,CAAC;IAEF,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;QAC/B,IAAI,QAAQ,CAAC,MAAM,IAAI,UAAU,CAAC,SAAS,EAAE,CAAC;YAC5C,MAAM;QACR,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,cAAsB,EAAE,KAAc;IAC1E,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1C,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,kBAAkB,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC","sourcesContent":["import {escapeId} from '@sqlrooms/duckdb';\n\nexport type PivotAggregatorKind =\n | 'default'\n | 'fraction_total'\n | 'fraction_row'\n | 'fraction_col';\n\nexport type PivotValueRequirement = 'any' | 'numeric';\n\nexport type PivotAggregatorDefinition = {\n name: string;\n numInputs: number;\n kind: PivotAggregatorKind;\n valueRequirement: PivotValueRequirement;\n format: (value: unknown) => string;\n buildSql: (valueColumns: string[]) => string;\n};\n\nconst formatInteger = new Intl.NumberFormat('en-US', {\n maximumFractionDigits: 0,\n});\nconst formatDecimal = new Intl.NumberFormat('en-US', {\n minimumFractionDigits: 0,\n maximumFractionDigits: 2,\n});\nconst formatPercent = new Intl.NumberFormat('en-US', {\n style: 'percent',\n minimumFractionDigits: 1,\n maximumFractionDigits: 1,\n});\n\nfunction formatNumber(value: unknown, formatter: Intl.NumberFormat) {\n const numeric = typeof value === 'bigint' ? Number(value) : Number(value);\n if (!Number.isFinite(numeric)) {\n return '';\n }\n return formatter.format(numeric);\n}\n\nfunction formatIdentity(value: unknown) {\n if (value === null || value === undefined) {\n return '';\n }\n return String(value);\n}\n\nfunction escapeColumn(column: string) {\n return escapeId(column);\n}\n\nfunction requireColumn(column: string | undefined, position: number) {\n if (!column) {\n throw new Error(`Aggregator requires value column ${position + 1}`);\n }\n return column;\n}\n\nexport const PIVOT_AGGREGATORS: Record<string, PivotAggregatorDefinition> = {\n Count: {\n name: 'Count',\n numInputs: 0,\n kind: 'default',\n valueRequirement: 'any',\n format: (value) => formatNumber(value, formatInteger),\n buildSql: () => 'COUNT(*)',\n },\n 'Count Unique Values': {\n name: 'Count Unique Values',\n numInputs: 1,\n kind: 'default',\n valueRequirement: 'any',\n format: (value) => formatNumber(value, formatInteger),\n buildSql: ([column]) =>\n `COUNT(DISTINCT ${escapeColumn(requireColumn(column, 0))})`,\n },\n 'List Unique Values': {\n name: 'List Unique Values',\n numInputs: 1,\n kind: 'default',\n valueRequirement: 'any',\n format: formatIdentity,\n buildSql: ([column]) =>\n `STRING_AGG(DISTINCT CAST(${escapeColumn(requireColumn(column, 0))} AS VARCHAR), ', ' ORDER BY CAST(${escapeColumn(requireColumn(column, 0))} AS VARCHAR))`,\n },\n Sum: {\n name: 'Sum',\n numInputs: 1,\n kind: 'default',\n valueRequirement: 'numeric',\n format: (value) => formatNumber(value, formatDecimal),\n buildSql: ([column]) =>\n `SUM(TRY_CAST(${escapeColumn(requireColumn(column, 0))} AS DOUBLE))`,\n },\n 'Integer Sum': {\n name: 'Integer Sum',\n numInputs: 1,\n kind: 'default',\n valueRequirement: 'numeric',\n format: (value) => formatNumber(value, formatInteger),\n buildSql: ([column]) =>\n `SUM(TRY_CAST(${escapeColumn(requireColumn(column, 0))} AS DOUBLE))`,\n },\n Average: {\n name: 'Average',\n numInputs: 1,\n kind: 'default',\n valueRequirement: 'numeric',\n format: (value) => formatNumber(value, formatDecimal),\n buildSql: ([column]) =>\n `AVG(TRY_CAST(${escapeColumn(requireColumn(column, 0))} AS DOUBLE))`,\n },\n Median: {\n name: 'Median',\n numInputs: 1,\n kind: 'default',\n valueRequirement: 'numeric',\n format: (value) => formatNumber(value, formatDecimal),\n buildSql: ([column]) =>\n `MEDIAN(TRY_CAST(${escapeColumn(requireColumn(column, 0))} AS DOUBLE))`,\n },\n 'Sample Variance': {\n name: 'Sample Variance',\n numInputs: 1,\n kind: 'default',\n valueRequirement: 'numeric',\n format: (value) => formatNumber(value, formatDecimal),\n buildSql: ([column]) =>\n `VAR_SAMP(TRY_CAST(${escapeColumn(requireColumn(column, 0))} AS DOUBLE))`,\n },\n 'Sample Standard Deviation': {\n name: 'Sample Standard Deviation',\n numInputs: 1,\n kind: 'default',\n valueRequirement: 'numeric',\n format: (value) => formatNumber(value, formatDecimal),\n buildSql: ([column]) =>\n `STDDEV_SAMP(TRY_CAST(${escapeColumn(requireColumn(column, 0))} AS DOUBLE))`,\n },\n Minimum: {\n name: 'Minimum',\n numInputs: 1,\n kind: 'default',\n valueRequirement: 'numeric',\n format: (value) => formatNumber(value, formatDecimal),\n buildSql: ([column]) =>\n `MIN(TRY_CAST(${escapeColumn(requireColumn(column, 0))} AS DOUBLE))`,\n },\n Maximum: {\n name: 'Maximum',\n numInputs: 1,\n kind: 'default',\n valueRequirement: 'numeric',\n format: (value) => formatNumber(value, formatDecimal),\n buildSql: ([column]) =>\n `MAX(TRY_CAST(${escapeColumn(requireColumn(column, 0))} AS DOUBLE))`,\n },\n First: {\n name: 'First',\n numInputs: 1,\n kind: 'default',\n valueRequirement: 'any',\n format: formatIdentity,\n buildSql: ([column]) =>\n `FIRST(CAST(${escapeColumn(requireColumn(column, 0))} AS VARCHAR))`,\n },\n Last: {\n name: 'Last',\n numInputs: 1,\n kind: 'default',\n valueRequirement: 'any',\n format: formatIdentity,\n buildSql: ([column]) =>\n `LAST(CAST(${escapeColumn(requireColumn(column, 0))} AS VARCHAR))`,\n },\n 'Sum over Sum': {\n name: 'Sum over Sum',\n numInputs: 2,\n kind: 'default',\n valueRequirement: 'numeric',\n format: (value) => formatNumber(value, formatDecimal),\n buildSql: ([numerator, denominator]) =>\n `SUM(TRY_CAST(${escapeColumn(requireColumn(numerator, 0))} AS DOUBLE)) / NULLIF(SUM(TRY_CAST(${escapeColumn(requireColumn(denominator, 1))} AS DOUBLE)), 0)`,\n },\n 'Sum as Fraction of Total': {\n name: 'Sum as Fraction of Total',\n numInputs: 1,\n kind: 'fraction_total',\n valueRequirement: 'numeric',\n format: (value) => formatNumber(value, formatPercent),\n buildSql: ([column]) =>\n `SUM(TRY_CAST(${escapeColumn(requireColumn(column, 0))} AS DOUBLE))`,\n },\n 'Sum as Fraction of Rows': {\n name: 'Sum as Fraction of Rows',\n numInputs: 1,\n kind: 'fraction_row',\n valueRequirement: 'numeric',\n format: (value) => formatNumber(value, formatPercent),\n buildSql: ([column]) =>\n `SUM(TRY_CAST(${escapeColumn(requireColumn(column, 0))} AS DOUBLE))`,\n },\n 'Sum as Fraction of Columns': {\n name: 'Sum as Fraction of Columns',\n numInputs: 1,\n kind: 'fraction_col',\n valueRequirement: 'numeric',\n format: (value) => formatNumber(value, formatPercent),\n buildSql: ([column]) =>\n `SUM(TRY_CAST(${escapeColumn(requireColumn(column, 0))} AS DOUBLE))`,\n },\n 'Count as Fraction of Total': {\n name: 'Count as Fraction of Total',\n numInputs: 0,\n kind: 'fraction_total',\n valueRequirement: 'any',\n format: (value) => formatNumber(value, formatPercent),\n buildSql: () => 'COUNT(*)',\n },\n 'Count as Fraction of Rows': {\n name: 'Count as Fraction of Rows',\n numInputs: 0,\n kind: 'fraction_row',\n valueRequirement: 'any',\n format: (value) => formatNumber(value, formatPercent),\n buildSql: () => 'COUNT(*)',\n },\n 'Count as Fraction of Columns': {\n name: 'Count as Fraction of Columns',\n numInputs: 0,\n kind: 'fraction_col',\n valueRequirement: 'any',\n format: (value) => formatNumber(value, formatPercent),\n buildSql: () => 'COUNT(*)',\n },\n};\n\nexport const DEFAULT_PIVOT_AGGREGATOR = 'Count';\n\nexport function getPivotAggregator(name: string): PivotAggregatorDefinition {\n return (\n PIVOT_AGGREGATORS[name] || PIVOT_AGGREGATORS[DEFAULT_PIVOT_AGGREGATOR]!\n );\n}\n\nexport function getAggregatorLabel(aggregatorName: string, values: string[]) {\n const aggregator = getPivotAggregator(aggregatorName);\n if (aggregator.numInputs <= 0) {\n return aggregator.name;\n }\n return `${aggregator.name} of ${values.slice(0, aggregator.numInputs).join(', ')}`;\n}\n\nexport function getDefaultValuesForAggregator(args: {\n aggregatorName: string;\n fields: Array<{name: string; type: string}>;\n currentValues: string[];\n}) {\n const {aggregatorName, fields, currentValues} = args;\n const aggregator = getPivotAggregator(aggregatorName);\n const candidates =\n aggregator.valueRequirement === 'numeric'\n ? fields.filter((field) =>\n /INT|DECIMAL|FLOAT|DOUBLE|REAL|HUGEINT|BIGINT/i.test(field.type),\n )\n : fields;\n\n const selected = currentValues.filter((value) =>\n candidates.some((field) => field.name === value),\n );\n\n for (const field of candidates) {\n if (selected.length >= aggregator.numInputs) {\n break;\n }\n if (!selected.includes(field.name)) {\n selected.push(field.name);\n }\n }\n\n return selected.slice(0, aggregator.numInputs);\n}\n\nexport function formatAggregatorValue(aggregatorName: string, value: unknown) {\n if (value === null || value === undefined) {\n return '';\n }\n return getPivotAggregator(aggregatorName).format(value);\n}\n"]}
@@ -0,0 +1,85 @@
1
+ import { PivotConfig } from './types';
2
+ import { VisualizationSpec } from '@sqlrooms/vega';
3
+ import * as arrow from 'apache-arrow';
4
+ /**
5
+ * Optional Arrow table input used by pivot renderers while query results are loading.
6
+ */
7
+ export type PivotArrowTable = arrow.Table | undefined;
8
+ /**
9
+ * Cell value type produced by pivot queries.
10
+ *
11
+ * Values may be numeric, strings, bigint-backed Arrow scalars, or null-like values
12
+ * depending on the selected aggregator.
13
+ */
14
+ export type PivotCellValue = unknown;
15
+ /**
16
+ * Supported heatmap rendering modes for the pivot table renderer.
17
+ */
18
+ export type HeatmapMode = 'full' | 'row' | 'col' | undefined;
19
+ /**
20
+ * Sorts two arbitrary values using a null-safe natural string comparison.
21
+ *
22
+ * Numeric substrings are compared numerically so labels like `item2` sort before
23
+ * `item10`.
24
+ */
25
+ export declare function naturalSort(a: unknown, b: unknown): number;
26
+ /**
27
+ * Compares two pivot key tuples segment-by-segment using natural sorting.
28
+ */
29
+ export declare function compareKeyArrays(a: string[], b: string[]): number;
30
+ /**
31
+ * Encodes a composite pivot key as a stable string suitable for `Map` keys.
32
+ */
33
+ export declare function keyId(key: string[]): string;
34
+ /**
35
+ * Builds a lookup of axis totals keyed by the requested row or column aliases.
36
+ *
37
+ * The map values come directly from the Arrow `value` column to avoid converting
38
+ * the result set into row objects first.
39
+ */
40
+ export declare function buildPivotTotalsMap(table: PivotArrowTable, keyAliases: string[]): Map<string, PivotCellValue>;
41
+ /**
42
+ * Extracts the distinct row or column keys present in a pivot result table.
43
+ *
44
+ * Keys are returned in encounter order so callers can apply their own sorting.
45
+ */
46
+ export declare function buildPivotUniqueKeys(table: PivotArrowTable, keyAliases: string[]): string[][];
47
+ /**
48
+ * Builds a lookup of pivot cell values keyed by `rowKey::colKey`.
49
+ *
50
+ * This is the main columnar access path used by `TableRenderer`.
51
+ */
52
+ export declare function buildPivotCellMap(table: PivotArrowTable, rowAliases: string[], colAliases: string[]): Map<string, PivotCellValue>;
53
+ /**
54
+ * Returns the unique stringified values for a single Arrow column.
55
+ *
56
+ * Used by export and chart helpers that only need one label column.
57
+ */
58
+ export declare function getUniqueStringColumnValues(table: PivotArrowTable, columnName: string): string[];
59
+ /**
60
+ * Calculates the rowspan or colspan for a grouped header cell.
61
+ *
62
+ * Returns `-1` when the cell should be skipped because an earlier header already
63
+ * spans across it.
64
+ */
65
+ export declare function spanSize(arr: string[][], rowIndex: number, columnIndex: number): number;
66
+ /**
67
+ * Produces a simple red-tinted heatmap background for a numeric value.
68
+ *
69
+ * The value is normalized against the provided scale values.
70
+ */
71
+ export declare function makeHeatColor(value: number | null, values: number[]): {
72
+ backgroundColor: string;
73
+ } | undefined;
74
+ /**
75
+ * Converts a mixed collection of values into the finite numeric subset.
76
+ */
77
+ export declare function toNumericValues(values: unknown[]): number[];
78
+ /**
79
+ * Builds the Vega-Lite spec for chart-based pivot renderers.
80
+ *
81
+ * The generated spec derives display-friendly series/category labels from the
82
+ * pivot result's `row_label` and `col_label` columns.
83
+ */
84
+ export declare function buildChartSpec(config: PivotConfig, rendererName: PivotConfig['rendererName']): VisualizationSpec;
85
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,WAAW,EAAC,MAAM,SAAS,CAAC;AACpC,OAAO,EAAC,iBAAiB,EAAC,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAEtC;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,KAAK,CAAC,KAAK,GAAG,SAAS,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC;AAIrC;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,SAAS,CAAC;AAE7D;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,UAcjD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,UASxD;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,UAElC;AAMD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,eAAe,EACtB,UAAU,EAAE,MAAM,EAAE,GACnB,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAY7B;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,eAAe,EACtB,UAAU,EAAE,MAAM,EAAE,cAUrB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,eAAe,EACtB,UAAU,EAAE,MAAM,EAAE,EACpB,UAAU,EAAE,MAAM,EAAE,GACnB,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,CAa7B;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,eAAe,EACtB,UAAU,EAAE,MAAM,YASnB;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CACtB,GAAG,EAAE,MAAM,EAAE,EAAE,EACf,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,UA4BpB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE;;cAcnE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,OAAO,EAAE,YAIhD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,WAAW,EACnB,YAAY,EAAE,WAAW,CAAC,cAAc,CAAC,GACxC,iBAAiB,CAoLnB"}