@databricks/appkit-ui 0.57.0 → 0.59.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/js/format/index.d.ts +38 -0
- package/dist/js/format/index.d.ts.map +1 -0
- package/dist/js/format/index.js +160 -0
- package/dist/js/format/index.js.map +1 -0
- package/dist/js/index.d.ts +5 -1
- package/dist/js/index.js +3 -1
- package/dist/js/metric-filter/index.d.ts +40 -0
- package/dist/js/metric-filter/index.d.ts.map +1 -0
- package/dist/js/metric-filter/index.js +56 -0
- package/dist/js/metric-filter/index.js.map +1 -0
- package/dist/react/charts/base.d.ts +19 -2
- package/dist/react/charts/base.d.ts.map +1 -1
- package/dist/react/charts/base.js +28 -8
- package/dist/react/charts/base.js.map +1 -1
- package/dist/react/charts/index.d.ts +4 -3
- package/dist/react/charts/normalize.d.ts.map +1 -1
- package/dist/react/charts/normalize.js +6 -6
- package/dist/react/charts/normalize.js.map +1 -1
- package/dist/react/charts/options.d.ts +31 -2
- package/dist/react/charts/options.d.ts.map +1 -1
- package/dist/react/charts/options.js +150 -13
- package/dist/react/charts/options.js.map +1 -1
- package/dist/react/charts/types.d.ts +54 -1
- package/dist/react/charts/types.d.ts.map +1 -1
- package/dist/react/charts/types.js.map +1 -1
- package/dist/react/charts/utils.d.ts +8 -9
- package/dist/react/charts/utils.d.ts.map +1 -1
- package/dist/react/charts/utils.js +135 -28
- package/dist/react/charts/utils.js.map +1 -1
- package/dist/react/hooks/analytics-sse.js +117 -0
- package/dist/react/hooks/analytics-sse.js.map +1 -0
- package/dist/react/hooks/index.d.ts +4 -1
- package/dist/react/hooks/types.d.ts +90 -4
- package/dist/react/hooks/types.d.ts.map +1 -1
- package/dist/react/hooks/use-analytics-query.d.ts.map +1 -1
- package/dist/react/hooks/use-analytics-query.js +14 -74
- package/dist/react/hooks/use-analytics-query.js.map +1 -1
- package/dist/react/hooks/use-metric-view.d.ts +39 -0
- package/dist/react/hooks/use-metric-view.d.ts.map +1 -0
- package/dist/react/hooks/use-metric-view.js +178 -0
- package/dist/react/hooks/use-metric-view.js.map +1 -0
- package/dist/react/index.d.ts +9 -5
- package/dist/react/index.js +5 -3
- package/dist/react/lib/format.js +3 -1
- package/dist/react/lib/format.js.map +1 -1
- package/dist/shared/src/index.d.ts +2 -0
- package/dist/shared/src/metric-filter.d.ts +36 -0
- package/dist/shared/src/metric-filter.d.ts.map +1 -0
- package/dist/shared/src/metric-metadata.d.ts +15 -0
- package/dist/shared/src/metric-metadata.d.ts.map +1 -0
- package/docs/development/type-generation.md +7 -6
- package/docs/plugins/ai-search.md +10 -0
- package/docs/plugins/analytics.md +287 -12
- package/package.json +1 -1
- package/sbom.cdx.json +1 -1
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { MetricViewColumnDisplay } from "../../shared/src/metric-metadata.js";
|
|
2
|
+
import "../../shared/src/index.js";
|
|
3
|
+
|
|
4
|
+
//#region src/js/format/index.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Format a raw value using a UC/YAML printf-style format spec.
|
|
7
|
+
*
|
|
8
|
+
*
|
|
9
|
+
* No format spec -> sensible default: numbers via `toLocaleString`, everything
|
|
10
|
+
* else via `String()`. `null`/`undefined` -> `""`. Unrecognized specs fall back
|
|
11
|
+
* to a best-effort result (the number grouped, or `String(value)`).
|
|
12
|
+
*/
|
|
13
|
+
declare function formatValue(value: unknown, format?: string): string;
|
|
14
|
+
declare function formatLabel(name: string, columnMeta?: MetricViewColumnDisplay): string;
|
|
15
|
+
interface D3FormatParts {
|
|
16
|
+
/** Numeric d3-format specifier, without a currency symbol. */
|
|
17
|
+
specifier: string;
|
|
18
|
+
/** Literal currency prefix from the UC format, when present. */
|
|
19
|
+
prefix?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Maps a UC/spreadsheet-style format spec to the pieces consumed by
|
|
23
|
+
* [d3-format](https://d3js.org/d3-format)-based charts.
|
|
24
|
+
*
|
|
25
|
+
* Best-effort mapping for the common specs:
|
|
26
|
+
* - `"$#,##0.00"` -> `{ specifier: ",.2f", prefix: "$" }`
|
|
27
|
+
* - `"€#,##0.00"` -> `{ specifier: ",.2f", prefix: "€" }`
|
|
28
|
+
* - `"#,##0"` -> `{ specifier: ",.0f" }`, `"0.0%"` -> `{ specifier: ".1%" }`
|
|
29
|
+
*
|
|
30
|
+
* A d3 specifier cannot encode an arbitrary currency symbol: its `$` marker is
|
|
31
|
+
* resolved through global locale configuration. Returning the literal prefix
|
|
32
|
+
* separately lets consumers such as Plotly pass it as `tickprefix` instead of
|
|
33
|
+
* silently rendering every currency as `$`.
|
|
34
|
+
*/
|
|
35
|
+
declare function toD3Format(format?: string): D3FormatParts | undefined;
|
|
36
|
+
//#endregion
|
|
37
|
+
export { D3FormatParts, formatLabel, formatValue, toD3Format };
|
|
38
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/js/format/index.ts"],"mappings":";;;;;;AA8JA;;;;;AAuEA;iBAvEgB,WAAA,CAAY,KAAA,WAAgB,MAAA;AAAA,iBAuE5B,WAAA,CACd,IAAA,UACA,UAAA,GAAa,uBAAA;AAAA,UAME,aAAA;EAPf;EASA,SAAA;EARA;EAUA,MAAA;AAAA;AAJF;;;;;AAqBA;;;;;;;;;AArBA,iBAqBgB,UAAA,CAAW,MAAA,YAAkB,aAAA"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
//#region src/js/format/index.ts
|
|
2
|
+
const SPEC_CACHE_MAX = 256;
|
|
3
|
+
const specCache = /* @__PURE__ */ new Map();
|
|
4
|
+
function countDecimals(format) {
|
|
5
|
+
const dotIndex = format.indexOf(".");
|
|
6
|
+
if (dotIndex === -1) return 0;
|
|
7
|
+
const match = format.slice(dotIndex + 1).match(/^[0#]+/);
|
|
8
|
+
return match ? match[0].length : 0;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Best-effort coercion of an arbitrary value to a finite number.
|
|
12
|
+
* Returns null when the value cannot be meaningfully treated as a number.
|
|
13
|
+
*/
|
|
14
|
+
function coerceNumber(value) {
|
|
15
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
|
16
|
+
if (typeof value === "bigint") return Number(value);
|
|
17
|
+
if (typeof value === "string") {
|
|
18
|
+
if (value.trim() === "") return null;
|
|
19
|
+
const n = Number(value);
|
|
20
|
+
return Number.isFinite(n) ? n : null;
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
/** Parse a plain decimal string without passing through a JS number. */
|
|
25
|
+
function parseExactDecimal(value) {
|
|
26
|
+
const match = /^([+-]?)(\d+)(?:\.(\d+))?$/.exec(value.trim());
|
|
27
|
+
if (!match) return null;
|
|
28
|
+
const fraction = match[3] ?? "";
|
|
29
|
+
const coefficient = BigInt(`${match[2]}${fraction}`);
|
|
30
|
+
return {
|
|
31
|
+
coefficient,
|
|
32
|
+
negative: match[1] === "-" && coefficient !== 0n,
|
|
33
|
+
scale: fraction.length
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function powerOfTen(exponent) {
|
|
37
|
+
return 10n ** BigInt(exponent);
|
|
38
|
+
}
|
|
39
|
+
/** Round an unsigned fixed-point coefficient to the requested display scale. */
|
|
40
|
+
function quantizeDecimal(coefficient, sourceScale, targetScale) {
|
|
41
|
+
if (sourceScale <= targetScale) return coefficient * powerOfTen(targetScale - sourceScale);
|
|
42
|
+
const divisor = powerOfTen(sourceScale - targetScale);
|
|
43
|
+
const quotient = coefficient / divisor;
|
|
44
|
+
return coefficient % divisor * 2n >= divisor ? quotient + 1n : quotient;
|
|
45
|
+
}
|
|
46
|
+
function groupInteger(value) {
|
|
47
|
+
return value.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Format an exact fixed-point value without ever coercing it to `Number`.
|
|
51
|
+
* Rounding is half-away-from-zero, matching Intl.NumberFormat's default.
|
|
52
|
+
*/
|
|
53
|
+
function formatExactDecimal(value, decimals, grouping, prefix, percent) {
|
|
54
|
+
const digits = quantizeDecimal(percent ? value.coefficient * 100n : value.coefficient, value.scale, decimals).toString().padStart(decimals + 1, "0");
|
|
55
|
+
const split = digits.length - decimals;
|
|
56
|
+
const integer = grouping ? groupInteger(digits.slice(0, split)) : digits.slice(0, split);
|
|
57
|
+
const fraction = decimals > 0 ? `.${digits.slice(split)}` : "";
|
|
58
|
+
return `${value.negative ? "-" : ""}${prefix}${integer}${fraction}${percent ? "%" : ""}`;
|
|
59
|
+
}
|
|
60
|
+
/** Format a number with fixed decimals + optional thousands grouping. */
|
|
61
|
+
function formatNumber(value, decimals, grouping) {
|
|
62
|
+
return value.toLocaleString("en-US", {
|
|
63
|
+
minimumFractionDigits: decimals,
|
|
64
|
+
maximumFractionDigits: decimals,
|
|
65
|
+
useGrouping: grouping
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The currency symbol a spec carries — everything before the first digit
|
|
70
|
+
* placeholder (`#`/`0`). The metric-view generator emits `$`, `€`, `£`, `¥`,
|
|
71
|
+
* `₹`, `R$`, or an unknown ISO code + space (e.g. `"XYZ "`); this recovers any
|
|
72
|
+
* of them verbatim. Returns `""` for a bare numeric spec (`"#,##0"`) or a
|
|
73
|
+
* percent spec (`"0.0%"`), neither of which has a leading symbol.
|
|
74
|
+
*/
|
|
75
|
+
function currencyPrefix(format) {
|
|
76
|
+
const match = format.match(/^[^#0]+/);
|
|
77
|
+
return match ? match[0] : "";
|
|
78
|
+
}
|
|
79
|
+
/** The spec's `{ isPercent, grouping, decimals, prefix }`, parsed at most once. */
|
|
80
|
+
function getOrParseSpec(format) {
|
|
81
|
+
const cached = specCache.get(format);
|
|
82
|
+
if (cached !== void 0) return cached;
|
|
83
|
+
const parsed = {
|
|
84
|
+
isPercent: format.includes("%"),
|
|
85
|
+
grouping: format.includes(","),
|
|
86
|
+
decimals: countDecimals(format),
|
|
87
|
+
prefix: currencyPrefix(format)
|
|
88
|
+
};
|
|
89
|
+
if (specCache.size < SPEC_CACHE_MAX) specCache.set(format, parsed);
|
|
90
|
+
return parsed;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Format a raw value using a UC/YAML printf-style format spec.
|
|
94
|
+
*
|
|
95
|
+
*
|
|
96
|
+
* No format spec -> sensible default: numbers via `toLocaleString`, everything
|
|
97
|
+
* else via `String()`. `null`/`undefined` -> `""`. Unrecognized specs fall back
|
|
98
|
+
* to a best-effort result (the number grouped, or `String(value)`).
|
|
99
|
+
*/
|
|
100
|
+
function formatValue(value, format) {
|
|
101
|
+
if (value === null || value === void 0) return "";
|
|
102
|
+
if (!format) {
|
|
103
|
+
if (typeof value === "number") return Number.isFinite(value) ? value.toLocaleString() : String(value);
|
|
104
|
+
if (typeof value === "bigint") return value.toLocaleString();
|
|
105
|
+
return String(value);
|
|
106
|
+
}
|
|
107
|
+
const { isPercent, grouping, decimals, prefix } = getOrParseSpec(format);
|
|
108
|
+
const exact = typeof value === "bigint" ? {
|
|
109
|
+
coefficient: value < 0n ? -value : value,
|
|
110
|
+
negative: value < 0n,
|
|
111
|
+
scale: 0
|
|
112
|
+
} : typeof value === "string" ? parseExactDecimal(value) : null;
|
|
113
|
+
if (exact !== null) return formatExactDecimal(exact, decimals, grouping, isPercent ? "" : prefix, isPercent);
|
|
114
|
+
const num = coerceNumber(value);
|
|
115
|
+
if (num === null) return String(value);
|
|
116
|
+
if (isPercent) return `${formatNumber(num * 100, decimals, grouping)}%`;
|
|
117
|
+
if (prefix) return `${num < 0 ? "-" : ""}${prefix}${formatNumber(Math.abs(num), decimals, grouping)}`;
|
|
118
|
+
return formatNumber(num, decimals, grouping);
|
|
119
|
+
}
|
|
120
|
+
function humanize(name) {
|
|
121
|
+
return name.replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/_/g, " ").replace(/\s+/g, " ").toLowerCase().replace(/\b\w/g, (l) => l.toUpperCase()).trim();
|
|
122
|
+
}
|
|
123
|
+
function formatLabel(name, columnMeta) {
|
|
124
|
+
if (columnMeta?.display_name) return columnMeta.display_name;
|
|
125
|
+
return humanize(name);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Maps a UC/spreadsheet-style format spec to the pieces consumed by
|
|
129
|
+
* [d3-format](https://d3js.org/d3-format)-based charts.
|
|
130
|
+
*
|
|
131
|
+
* Best-effort mapping for the common specs:
|
|
132
|
+
* - `"$#,##0.00"` -> `{ specifier: ",.2f", prefix: "$" }`
|
|
133
|
+
* - `"€#,##0.00"` -> `{ specifier: ",.2f", prefix: "€" }`
|
|
134
|
+
* - `"#,##0"` -> `{ specifier: ",.0f" }`, `"0.0%"` -> `{ specifier: ".1%" }`
|
|
135
|
+
*
|
|
136
|
+
* A d3 specifier cannot encode an arbitrary currency symbol: its `$` marker is
|
|
137
|
+
* resolved through global locale configuration. Returning the literal prefix
|
|
138
|
+
* separately lets consumers such as Plotly pass it as `tickprefix` instead of
|
|
139
|
+
* silently rendering every currency as `$`.
|
|
140
|
+
*/
|
|
141
|
+
function toD3Format(format) {
|
|
142
|
+
if (!format) return void 0;
|
|
143
|
+
const { prefix, grouping, decimals, isPercent } = getOrParseSpec(format);
|
|
144
|
+
const numeric = format.slice(prefix.length);
|
|
145
|
+
if (numeric.replace(/[#0,.%\s]/g, "") !== "") return void 0;
|
|
146
|
+
if (!/[0#]/.test(numeric)) return void 0;
|
|
147
|
+
const group = grouping ? "," : "";
|
|
148
|
+
if (isPercent) return {
|
|
149
|
+
specifier: `${group}.${decimals}%`,
|
|
150
|
+
...prefix ? { prefix } : {}
|
|
151
|
+
};
|
|
152
|
+
return {
|
|
153
|
+
specifier: `${group}.${decimals}f`,
|
|
154
|
+
...prefix ? { prefix } : {}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
//#endregion
|
|
159
|
+
export { formatLabel, formatValue, toD3Format };
|
|
160
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/js/format/index.ts"],"sourcesContent":["import type { MetricViewColumnDisplay } from \"shared\";\n\nexport type { MetricViewColumnDisplay };\n\nconst SPEC_CACHE_MAX = 256;\nconst specCache = new Map<\n string,\n {\n isPercent: boolean;\n grouping: boolean;\n decimals: number;\n prefix: string;\n }\n>();\n\nfunction countDecimals(format: string): number {\n const dotIndex = format.indexOf(\".\");\n if (dotIndex === -1) return 0;\n const frac = format.slice(dotIndex + 1);\n const match = frac.match(/^[0#]+/);\n return match ? match[0].length : 0;\n}\n\n/**\n * Best-effort coercion of an arbitrary value to a finite number.\n * Returns null when the value cannot be meaningfully treated as a number.\n */\nfunction coerceNumber(value: unknown): number | null {\n if (typeof value === \"number\") return Number.isFinite(value) ? value : null;\n if (typeof value === \"bigint\") return Number(value);\n if (typeof value === \"string\") {\n if (value.trim() === \"\") return null;\n const n = Number(value);\n return Number.isFinite(n) ? n : null;\n }\n return null;\n}\n\ninterface ExactDecimal {\n coefficient: bigint;\n negative: boolean;\n scale: number;\n}\n\n/** Parse a plain decimal string without passing through a JS number. */\nfunction parseExactDecimal(value: string): ExactDecimal | null {\n const match = /^([+-]?)(\\d+)(?:\\.(\\d+))?$/.exec(value.trim());\n if (!match) return null;\n\n const fraction = match[3] ?? \"\";\n const coefficient = BigInt(`${match[2]}${fraction}`);\n return {\n coefficient,\n negative: match[1] === \"-\" && coefficient !== 0n,\n scale: fraction.length,\n };\n}\n\nfunction powerOfTen(exponent: number): bigint {\n return 10n ** BigInt(exponent);\n}\n\n/** Round an unsigned fixed-point coefficient to the requested display scale. */\nfunction quantizeDecimal(\n coefficient: bigint,\n sourceScale: number,\n targetScale: number,\n): bigint {\n if (sourceScale <= targetScale) {\n return coefficient * powerOfTen(targetScale - sourceScale);\n }\n\n const divisor = powerOfTen(sourceScale - targetScale);\n const quotient = coefficient / divisor;\n const remainder = coefficient % divisor;\n return remainder * 2n >= divisor ? quotient + 1n : quotient;\n}\n\nfunction groupInteger(value: string): string {\n return value.replace(/\\B(?=(\\d{3})+(?!\\d))/g, \",\");\n}\n\n/**\n * Format an exact fixed-point value without ever coercing it to `Number`.\n * Rounding is half-away-from-zero, matching Intl.NumberFormat's default.\n */\nfunction formatExactDecimal(\n value: ExactDecimal,\n decimals: number,\n grouping: boolean,\n prefix: string,\n percent: boolean,\n): string {\n const coefficient = percent ? value.coefficient * 100n : value.coefficient;\n const quantized = quantizeDecimal(coefficient, value.scale, decimals);\n const digits = quantized.toString().padStart(decimals + 1, \"0\");\n const split = digits.length - decimals;\n const integer = grouping\n ? groupInteger(digits.slice(0, split))\n : digits.slice(0, split);\n const fraction = decimals > 0 ? `.${digits.slice(split)}` : \"\";\n const sign = value.negative ? \"-\" : \"\";\n return `${sign}${prefix}${integer}${fraction}${percent ? \"%\" : \"\"}`;\n}\n\n/** Format a number with fixed decimals + optional thousands grouping. */\nfunction formatNumber(\n value: number,\n decimals: number,\n grouping: boolean,\n): string {\n return value.toLocaleString(\"en-US\", {\n minimumFractionDigits: decimals,\n maximumFractionDigits: decimals,\n useGrouping: grouping,\n });\n}\n\n/**\n * The currency symbol a spec carries — everything before the first digit\n * placeholder (`#`/`0`). The metric-view generator emits `$`, `€`, `£`, `¥`,\n * `₹`, `R$`, or an unknown ISO code + space (e.g. `\"XYZ \"`); this recovers any\n * of them verbatim. Returns `\"\"` for a bare numeric spec (`\"#,##0\"`) or a\n * percent spec (`\"0.0%\"`), neither of which has a leading symbol.\n */\nfunction currencyPrefix(format: string): string {\n const match = format.match(/^[^#0]+/);\n return match ? match[0] : \"\";\n}\n\n/** The spec's `{ isPercent, grouping, decimals, prefix }`, parsed at most once. */\nfunction getOrParseSpec(format: string) {\n const cached = specCache.get(format);\n if (cached !== undefined) return cached;\n\n // Cache miss: parse now and store if under the limit.\n const parsed = {\n isPercent: format.includes(\"%\"),\n grouping: format.includes(\",\"),\n decimals: countDecimals(format),\n prefix: currencyPrefix(format),\n };\n\n if (specCache.size < SPEC_CACHE_MAX) {\n specCache.set(format, parsed);\n }\n\n return parsed;\n}\n\n/**\n * Format a raw value using a UC/YAML printf-style format spec.\n *\n *\n * No format spec -> sensible default: numbers via `toLocaleString`, everything\n * else via `String()`. `null`/`undefined` -> `\"\"`. Unrecognized specs fall back\n * to a best-effort result (the number grouped, or `String(value)`).\n */\nexport function formatValue(value: unknown, format?: string): string {\n if (value === null || value === undefined) return \"\";\n\n if (!format) {\n if (typeof value === \"number\") {\n return Number.isFinite(value) ? value.toLocaleString() : String(value);\n }\n if (typeof value === \"bigint\") return value.toLocaleString();\n return String(value);\n }\n\n const { isPercent, grouping, decimals, prefix } = getOrParseSpec(format);\n\n // JSON_ARRAY delivers SQL scalar cells as strings. Parse plain integer and\n // fractional forms as fixed-point values so DECIMAL precision is never lost\n // through Number(), even when the magnitude exceeds ±2^53.\n const exact =\n typeof value === \"bigint\"\n ? {\n coefficient: value < 0n ? -value : value,\n negative: value < 0n,\n scale: 0,\n }\n : typeof value === \"string\"\n ? parseExactDecimal(value)\n : null;\n if (exact !== null) {\n return formatExactDecimal(\n exact,\n decimals,\n grouping,\n isPercent ? \"\" : prefix,\n isPercent,\n );\n }\n\n const num = coerceNumber(value);\n // Non-numeric value with a numeric-ish spec: nothing sensible to format.\n if (num === null) return String(value);\n\n if (isPercent) {\n return `${formatNumber(num * 100, decimals, grouping)}%`;\n }\n\n if (prefix) {\n const sign = num < 0 ? \"-\" : \"\";\n return `${sign}${prefix}${formatNumber(Math.abs(num), decimals, grouping)}`;\n }\n\n return formatNumber(num, decimals, grouping);\n}\n\n// Turns a raw column name into a human-readable label.\nfunction humanize(name: string): string {\n return (\n name\n // Handle consecutive uppercase followed by lowercase (e.g., HTTPUrl -> HTTP Url)\n .replace(/([A-Z]+)([A-Z][a-z])/g, \"$1 $2\")\n // Handle lowercase followed by uppercase (e.g., totalSpend -> total Spend)\n .replace(/([a-z])([A-Z])/g, \"$1 $2\")\n // Replace underscores with spaces\n .replace(/_/g, \" \")\n // Collapse multiple spaces into one\n .replace(/\\s+/g, \" \")\n // Normalize to title case\n .toLowerCase()\n .replace(/\\b\\w/g, (l) => l.toUpperCase())\n .trim()\n );\n}\n\nexport function formatLabel(\n name: string,\n columnMeta?: MetricViewColumnDisplay,\n): string {\n if (columnMeta?.display_name) return columnMeta.display_name;\n return humanize(name);\n}\n\nexport interface D3FormatParts {\n /** Numeric d3-format specifier, without a currency symbol. */\n specifier: string;\n /** Literal currency prefix from the UC format, when present. */\n prefix?: string;\n}\n\n/**\n * Maps a UC/spreadsheet-style format spec to the pieces consumed by\n * [d3-format](https://d3js.org/d3-format)-based charts.\n *\n * Best-effort mapping for the common specs:\n * - `\"$#,##0.00\"` -> `{ specifier: \",.2f\", prefix: \"$\" }`\n * - `\"€#,##0.00\"` -> `{ specifier: \",.2f\", prefix: \"€\" }`\n * - `\"#,##0\"` -> `{ specifier: \",.0f\" }`, `\"0.0%\"` -> `{ specifier: \".1%\" }`\n *\n * A d3 specifier cannot encode an arbitrary currency symbol: its `$` marker is\n * resolved through global locale configuration. Returning the literal prefix\n * separately lets consumers such as Plotly pass it as `tickprefix` instead of\n * silently rendering every currency as `$`.\n */\nexport function toD3Format(format?: string): D3FormatParts | undefined {\n if (!format) return undefined;\n\n // Strip any leading currency prefix first, then require the remainder to be\n // built purely from numeric-format characters; anything else (date patterns,\n // free text, ...) is left unrecognized.\n const { prefix, grouping, decimals, isPercent } = getOrParseSpec(format);\n const numeric = format.slice(prefix.length);\n if (numeric.replace(/[#0,.%\\s]/g, \"\") !== \"\") return undefined;\n if (!/[0#]/.test(numeric)) return undefined;\n\n const group = grouping ? \",\" : \"\";\n\n if (isPercent) {\n return {\n specifier: `${group}.${decimals}%`,\n ...(prefix ? { prefix } : {}),\n };\n }\n\n return {\n specifier: `${group}.${decimals}f`,\n ...(prefix ? { prefix } : {}),\n };\n}\n"],"mappings":"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"}
|
package/dist/js/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { MetricFilter, MetricFilterOperatorName, MetricOrderBy, MetricOrderDirection, MetricPredicate } from "../shared/src/metric-filter.js";
|
|
2
|
+
import { MetricViewColumnDisplay } from "../shared/src/metric-metadata.js";
|
|
1
3
|
import { SQLBinaryMarker, SQLBooleanMarker, SQLDateMarker, SQLNumberMarker, SQLStringMarker, SQLTimestampMarker, SQLTypeMarker } from "../shared/src/sql/types.js";
|
|
2
4
|
import { isSQLTypeMarker, sql } from "../shared/src/sql/helpers.js";
|
|
3
5
|
import "../shared/src/index.js";
|
|
@@ -6,6 +8,8 @@ import { ArrowClient, DetectedFields, Field, Table } from "./arrow/arrow-client.
|
|
|
6
8
|
import { getArrowModule, initializeTypeIdSets } from "./arrow/lazy-arrow.js";
|
|
7
9
|
import "./arrow/index.js";
|
|
8
10
|
import { AppKitClientConfig, _resetConfigCache, getClientConfig, getPluginClientConfig } from "./config.js";
|
|
11
|
+
import { D3FormatParts, formatLabel, formatValue, toD3Format } from "./format/index.js";
|
|
12
|
+
import { MetricFilterShorthand, toMetricFilter } from "./metric-filter/index.js";
|
|
9
13
|
import { ConnectSSEOptions, SSEMessage } from "./sse/types.js";
|
|
10
14
|
import { connectSSE } from "./sse/connect-sse.js";
|
|
11
|
-
export { AppKitClientConfig, ArrowClient, ConnectSSEOptions, DATE_FIELD_PATTERNS, DetectedFields, Field, METADATA_DATE_PATTERNS, NAME_FIELD_PATTERNS, type SQLBinaryMarker, type SQLBooleanMarker, type SQLDateMarker, type SQLNumberMarker, type SQLStringMarker, type SQLTimestampMarker, type SQLTypeMarker, SSEMessage, Table, _resetConfigCache, connectSSE, getArrowModule, getClientConfig, getPluginClientConfig, initializeTypeIdSets, isSQLTypeMarker, sql };
|
|
15
|
+
export { AppKitClientConfig, ArrowClient, ConnectSSEOptions, D3FormatParts, DATE_FIELD_PATTERNS, DetectedFields, Field, METADATA_DATE_PATTERNS, MetricFilter, MetricFilterOperatorName, MetricFilterShorthand, MetricOrderBy, MetricOrderDirection, MetricPredicate, MetricViewColumnDisplay, NAME_FIELD_PATTERNS, type SQLBinaryMarker, type SQLBooleanMarker, type SQLDateMarker, type SQLNumberMarker, type SQLStringMarker, type SQLTimestampMarker, type SQLTypeMarker, SSEMessage, Table, _resetConfigCache, connectSSE, formatLabel, formatValue, getArrowModule, getClientConfig, getPluginClientConfig, initializeTypeIdSets, isSQLTypeMarker, sql, toD3Format, toMetricFilter };
|
package/dist/js/index.js
CHANGED
|
@@ -3,6 +3,8 @@ import { DATE_FIELD_PATTERNS, METADATA_DATE_PATTERNS, NAME_FIELD_PATTERNS } from
|
|
|
3
3
|
import { getArrowModule, initializeTypeIdSets } from "./arrow/lazy-arrow.js";
|
|
4
4
|
import { ArrowClient } from "./arrow/arrow-client.js";
|
|
5
5
|
import { _resetConfigCache, getClientConfig, getPluginClientConfig } from "./config.js";
|
|
6
|
+
import { formatLabel, formatValue, toD3Format } from "./format/index.js";
|
|
7
|
+
import { toMetricFilter } from "./metric-filter/index.js";
|
|
6
8
|
import { connectSSE } from "./sse/connect-sse.js";
|
|
7
9
|
|
|
8
|
-
export { ArrowClient, DATE_FIELD_PATTERNS, METADATA_DATE_PATTERNS, NAME_FIELD_PATTERNS, _resetConfigCache, connectSSE, getArrowModule, getClientConfig, getPluginClientConfig, initializeTypeIdSets, isSQLTypeMarker, sql };
|
|
10
|
+
export { ArrowClient, DATE_FIELD_PATTERNS, METADATA_DATE_PATTERNS, NAME_FIELD_PATTERNS, _resetConfigCache, connectSSE, formatLabel, formatValue, getArrowModule, getClientConfig, getPluginClientConfig, initializeTypeIdSets, isSQLTypeMarker, sql, toD3Format, toMetricFilter };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { MetricFilter, MetricFilterOperatorName, MetricOrderBy, MetricOrderDirection, MetricPredicate } from "../../shared/src/metric-filter.js";
|
|
2
|
+
import "../../shared/src/index.js";
|
|
3
|
+
|
|
4
|
+
//#region src/js/metric-filter/index.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Shorthand map of `dimension -> selected value(s)`.
|
|
7
|
+
* A member is dropped when its value is `undefined` or empty array,
|
|
8
|
+
* so a partially-filled filter-bar selection maps straight to "no predicate for that dimension".
|
|
9
|
+
*
|
|
10
|
+
*/
|
|
11
|
+
type MetricFilterShorthand = Record<string, string | number | null | ReadonlyArray<string | number> | undefined>;
|
|
12
|
+
/**
|
|
13
|
+
* Compile a `{ dimension -> value(s) }` shorthand into a {@link MetricFilter}
|
|
14
|
+
* Returns a bare {@link MetricPredicate} for a single member, an `and` group for
|
|
15
|
+
* several, and `undefined` when nothing is selected (so the caller can pass it
|
|
16
|
+
* straight to `useMetricView`'s optional `filter`, which omits the field when
|
|
17
|
+
* `undefined`). For operators beyond equality/membership (ranges, `contains`,
|
|
18
|
+
* `set`), build the {@link MetricFilter} tree directly.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* toMetricFilter({ region: "EMEA" });
|
|
23
|
+
* // → { member: "region", operator: "equals", values: ["EMEA"] }
|
|
24
|
+
*
|
|
25
|
+
* toMetricFilter({ region: ["EMEA", "APAC"], segment: "SMB" });
|
|
26
|
+
* // → { and: [
|
|
27
|
+
* // { member: "region", operator: "in", values: ["EMEA", "APAC"] },
|
|
28
|
+
* // { member: "segment", operator: "equals", values: ["SMB"] },
|
|
29
|
+
* // ] }
|
|
30
|
+
*
|
|
31
|
+
* toMetricFilter({ region: undefined }); // → undefined
|
|
32
|
+
*
|
|
33
|
+
* toMetricFilter({ region: null });
|
|
34
|
+
* // → { member: "region", operator: "notSet" }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
declare function toMetricFilter(selection: MetricFilterShorthand): MetricFilter | undefined;
|
|
38
|
+
//#endregion
|
|
39
|
+
export { MetricFilterShorthand, toMetricFilter };
|
|
40
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/js/metric-filter/index.ts"],"mappings":";;;;;;AAgBA;;;;KAAY,qBAAA,GAAwB,MAAA,kCAET,aAAA;AA4B3B;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,iBAAgB,cAAA,CACd,SAAA,EAAW,qBAAA,GACV,YAAA"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
//#region src/js/metric-filter/index.ts
|
|
2
|
+
/**
|
|
3
|
+
* Compile a `{ dimension -> value(s) }` shorthand into a {@link MetricFilter}
|
|
4
|
+
* Returns a bare {@link MetricPredicate} for a single member, an `and` group for
|
|
5
|
+
* several, and `undefined` when nothing is selected (so the caller can pass it
|
|
6
|
+
* straight to `useMetricView`'s optional `filter`, which omits the field when
|
|
7
|
+
* `undefined`). For operators beyond equality/membership (ranges, `contains`,
|
|
8
|
+
* `set`), build the {@link MetricFilter} tree directly.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* toMetricFilter({ region: "EMEA" });
|
|
13
|
+
* // → { member: "region", operator: "equals", values: ["EMEA"] }
|
|
14
|
+
*
|
|
15
|
+
* toMetricFilter({ region: ["EMEA", "APAC"], segment: "SMB" });
|
|
16
|
+
* // → { and: [
|
|
17
|
+
* // { member: "region", operator: "in", values: ["EMEA", "APAC"] },
|
|
18
|
+
* // { member: "segment", operator: "equals", values: ["SMB"] },
|
|
19
|
+
* // ] }
|
|
20
|
+
*
|
|
21
|
+
* toMetricFilter({ region: undefined }); // → undefined
|
|
22
|
+
*
|
|
23
|
+
* toMetricFilter({ region: null });
|
|
24
|
+
* // → { member: "region", operator: "notSet" }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
function toMetricFilter(selection) {
|
|
28
|
+
const predicates = [];
|
|
29
|
+
for (const member of Object.keys(selection)) {
|
|
30
|
+
const value = selection[member];
|
|
31
|
+
if (value === void 0) continue;
|
|
32
|
+
if (value === null) predicates.push({
|
|
33
|
+
member,
|
|
34
|
+
operator: "notSet"
|
|
35
|
+
});
|
|
36
|
+
else if (Array.isArray(value)) {
|
|
37
|
+
if (value.length === 0) continue;
|
|
38
|
+
predicates.push({
|
|
39
|
+
member,
|
|
40
|
+
operator: "in",
|
|
41
|
+
values: [...value]
|
|
42
|
+
});
|
|
43
|
+
} else predicates.push({
|
|
44
|
+
member,
|
|
45
|
+
operator: "equals",
|
|
46
|
+
values: [value]
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
if (predicates.length === 0) return void 0;
|
|
50
|
+
if (predicates.length === 1) return predicates[0];
|
|
51
|
+
return { and: predicates };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
//#endregion
|
|
55
|
+
export { toMetricFilter };
|
|
56
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/js/metric-filter/index.ts"],"sourcesContent":["import type { MetricFilter, MetricPredicate } from \"shared\";\n\nexport type {\n MetricFilter,\n MetricFilterOperatorName,\n MetricOrderBy,\n MetricOrderDirection,\n MetricPredicate,\n} from \"shared\";\n\n/**\n * Shorthand map of `dimension -> selected value(s)`.\n * A member is dropped when its value is `undefined` or empty array,\n * so a partially-filled filter-bar selection maps straight to \"no predicate for that dimension\".\n *\n */\nexport type MetricFilterShorthand = Record<\n string,\n string | number | null | ReadonlyArray<string | number> | undefined\n>;\n\n/**\n * Compile a `{ dimension -> value(s) }` shorthand into a {@link MetricFilter}\n * Returns a bare {@link MetricPredicate} for a single member, an `and` group for\n * several, and `undefined` when nothing is selected (so the caller can pass it\n * straight to `useMetricView`'s optional `filter`, which omits the field when\n * `undefined`). For operators beyond equality/membership (ranges, `contains`,\n * `set`), build the {@link MetricFilter} tree directly.\n *\n * @example\n * ```typescript\n * toMetricFilter({ region: \"EMEA\" });\n * // → { member: \"region\", operator: \"equals\", values: [\"EMEA\"] }\n *\n * toMetricFilter({ region: [\"EMEA\", \"APAC\"], segment: \"SMB\" });\n * // → { and: [\n * // { member: \"region\", operator: \"in\", values: [\"EMEA\", \"APAC\"] },\n * // { member: \"segment\", operator: \"equals\", values: [\"SMB\"] },\n * // ] }\n *\n * toMetricFilter({ region: undefined }); // → undefined\n *\n * toMetricFilter({ region: null });\n * // → { member: \"region\", operator: \"notSet\" }\n * ```\n */\nexport function toMetricFilter(\n selection: MetricFilterShorthand,\n): MetricFilter | undefined {\n const predicates: MetricPredicate[] = [];\n for (const member of Object.keys(selection)) {\n const value = selection[member];\n if (value === undefined) continue;\n if (value === null) {\n predicates.push({ member, operator: \"notSet\" });\n } else if (Array.isArray(value)) {\n if (value.length === 0) continue;\n predicates.push({ member, operator: \"in\", values: [...value] });\n } else {\n predicates.push({\n member,\n operator: \"equals\",\n values: [value as string | number],\n });\n }\n }\n if (predicates.length === 0) return undefined;\n if (predicates.length === 1) return predicates[0];\n return { and: predicates };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA8CA,SAAgB,eACd,WAC0B;CAC1B,MAAM,aAAgC,EAAE;AACxC,MAAK,MAAM,UAAU,OAAO,KAAK,UAAU,EAAE;EAC3C,MAAM,QAAQ,UAAU;AACxB,MAAI,UAAU,OAAW;AACzB,MAAI,UAAU,KACZ,YAAW,KAAK;GAAE;GAAQ,UAAU;GAAU,CAAC;WACtC,MAAM,QAAQ,MAAM,EAAE;AAC/B,OAAI,MAAM,WAAW,EAAG;AACxB,cAAW,KAAK;IAAE;IAAQ,UAAU;IAAM,QAAQ,CAAC,GAAG,MAAM;IAAE,CAAC;QAE/D,YAAW,KAAK;GACd;GACA,UAAU;GACV,QAAQ,CAAC,MAAyB;GACnC,CAAC;;AAGN,KAAI,WAAW,WAAW,EAAG,QAAO;AACpC,KAAI,WAAW,WAAW,EAAG,QAAO,WAAW;AAC/C,QAAO,EAAE,KAAK,YAAY"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ChartColorPalette, ChartData, ChartType, Orientation } from "./types.js";
|
|
1
|
+
import { ChartClickDatum, ChartColorPalette, ChartData, ChartType, ChartValueFormatter, Orientation } from "./types.js";
|
|
2
2
|
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
3
3
|
|
|
4
4
|
//#region src/react/charts/base.d.ts
|
|
@@ -62,8 +62,22 @@ interface BaseChartProps {
|
|
|
62
62
|
* duplicate echarts copies won't share registrations).
|
|
63
63
|
*/
|
|
64
64
|
options?: Record<string, unknown>;
|
|
65
|
+
/** Formats measure values. See {@link ChartBaseProps.valueFormatter}. */
|
|
66
|
+
valueFormatter?: ChartValueFormatter;
|
|
65
67
|
/** Additional CSS classes */
|
|
66
68
|
className?: string;
|
|
69
|
+
/**
|
|
70
|
+
* Fired when a data element (bar, slice, point) is clicked. Fire-and-forget:
|
|
71
|
+
* the return value is ignored (async handlers are fine — the chart never awaits).
|
|
72
|
+
* The handler receives a normalized {@link ChartClickDatum}.
|
|
73
|
+
*
|
|
74
|
+
* Pointer-only: charts render to <canvas>, so this does not fire for keyboard
|
|
75
|
+
* users. Provide a keyboard-accessible equivalent (e.g. a table row action) for
|
|
76
|
+
* the same action.
|
|
77
|
+
*/
|
|
78
|
+
onDataClick?: (datum: ChartClickDatum) => void;
|
|
79
|
+
/** Controlled selection by category name. See {@link ChartBaseProps.selected}. */
|
|
80
|
+
selected?: string | string[];
|
|
67
81
|
}
|
|
68
82
|
/**
|
|
69
83
|
* Base chart component that handles both Arrow and JSON data.
|
|
@@ -92,7 +106,10 @@ declare function BaseChart({
|
|
|
92
106
|
min,
|
|
93
107
|
max,
|
|
94
108
|
options: customOptions,
|
|
95
|
-
|
|
109
|
+
valueFormatter,
|
|
110
|
+
className,
|
|
111
|
+
onDataClick,
|
|
112
|
+
selected
|
|
96
113
|
}: BaseChartProps): react_jsx_runtime0.JSX.Element;
|
|
97
114
|
//#endregion
|
|
98
115
|
export { BaseChart, BaseChartProps };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.d.ts","names":[],"sources":["../../../src/react/charts/base.tsx"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"base.d.ts","names":[],"sources":["../../../src/react/charts/base.tsx"],"mappings":";;;;UAiHiB,cAAA;;EAEf,IAAA,EAAM,SAAA;EAFS;EAIf,SAAA,EAAW,SAAA;;EAEX,IAAA;EAFW;EAIX,IAAA;EAee;EAbf,WAAA,GAAc,WAAA;EAmDG;EAjDjB,MAAA;EA6DqC;EA3DrC,KAAA;EAZA;EAcA,UAAA;EAZA;;;;;;EAmBA,YAAA,GAAe,iBAAA;EATf;EAWA,MAAA;EAFA;EAIA,UAAA;EAFA;EAIA,MAAA;EAAA;EAEA,OAAA;EAEA;EAAA,UAAA;EAIA;EAFA,QAAA;EAMA;EAJA,WAAA;EAQA;EANA,UAAA;EAoBA;EAlBA,aAAA;EAoBA;EAlBA,QAAA;EAoBA;EAlBA,GAAA;EA4BsB;EA1BtB,GAAA;EA4BA;;;AAWF;;;;;;;;EA3BE,OAAA,GAAU,MAAA;EAkCV;EAhCA,cAAA,GAAiB,mBAAA;EAkCjB;EAhCA,SAAA;EAkCA;;;;;;;;;EAxBA,WAAA,IAAe,KAAA,EAAO,eAAA;EAkCtB;EAhCA,QAAA;AAAA;;;;;iBAWc,SAAA,CAAA;EACd,IAAA;EACA,SAAA;EACA,IAAA;EACA,IAAA;EACA,WAAA;EACA,MAAA;EACA,KAAA;EACA,UAAA;EACA,YAAA;EACA,MAAA,EAAQ,YAAA;EACR,UAAA;EACA,MAAA;EACA,OAAA;EACA,UAAA;EACA,QAAA;EACA,WAAA;EACA,UAAA;EACA,aAAA;EACA,QAAA;EACA,GAAA;EACA,GAAA;EACA,OAAA,EAAS,aAAA;EACT,cAAA;EACA,SAAA;EACA,WAAA;EACA;AAAA,GACC,cAAA,GAAc,kBAAA,CAAA,GAAA,CAAA,OAAA"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { mapToDatum } from "./utils.js";
|
|
1
2
|
import { normalizeChartData, normalizeHeatmapData } from "./normalize.js";
|
|
2
|
-
import { buildCartesianOption, buildHeatmapOption, buildHorizontalBarOption, buildPieOption, buildRadarOption } from "./options.js";
|
|
3
|
+
import { applySelectionEmphasis, buildCartesianOption, buildHeatmapOption, buildHorizontalBarOption, buildPieOption, buildRadarOption } from "./options.js";
|
|
3
4
|
import { useChartUITokens, useThemeColors } from "./theme.js";
|
|
4
5
|
import { BarChart, HeatmapChart, LineChart, PieChart, RadarChart, ScatterChart } from "echarts/charts";
|
|
5
6
|
import { GridComponent, LegendComponent, TitleComponent, TooltipComponent, VisualMapComponent } from "echarts/components";
|
|
@@ -41,10 +42,13 @@ function getDefaultPalette(chartType) {
|
|
|
41
42
|
* Base chart component that handles both Arrow and JSON data.
|
|
42
43
|
* Renders using ECharts for consistent output across both formats.
|
|
43
44
|
*/
|
|
44
|
-
function BaseChart({ data, chartType, xKey, yKey, orientation, height = 300, title, showLegend = true, colorPalette, colors: customColors, showSymbol = false, smooth = true, stacked = false, symbolSize = 8, showArea = true, innerRadius = 0, showLabels = true, labelPosition = "outside", yAxisKey, min, max, options: customOptions, className }) {
|
|
45
|
+
function BaseChart({ data, chartType, xKey, yKey, orientation, height = 300, title, showLegend = true, colorPalette, colors: customColors, showSymbol = false, smooth = true, stacked = false, symbolSize = 8, showArea = true, innerRadius = 0, showLabels = true, labelPosition = "outside", yAxisKey, min, max, options: customOptions, valueFormatter, className, onDataClick, selected }) {
|
|
45
46
|
const themeColors = useThemeColors(colorPalette ?? getDefaultPalette(chartType));
|
|
46
47
|
const colors = customColors ?? themeColors;
|
|
47
48
|
const ui = useChartUITokens();
|
|
49
|
+
const interactive = !!onDataClick;
|
|
50
|
+
const onDataClickRef = useRef(onDataClick);
|
|
51
|
+
onDataClickRef.current = onDataClick;
|
|
48
52
|
const echartsInstanceRef = useRef(null);
|
|
49
53
|
const chartRefCallback = useCallback((node) => {
|
|
50
54
|
if (echartsInstanceRef.current && !echartsInstanceRef.current.isDisposed()) echartsInstanceRef.current.dispose();
|
|
@@ -74,7 +78,8 @@ function BaseChart({ data, chartType, xKey, yKey, orientation, height = 300, tit
|
|
|
74
78
|
title,
|
|
75
79
|
showLegend,
|
|
76
80
|
xField,
|
|
77
|
-
ui
|
|
81
|
+
ui,
|
|
82
|
+
valueFormatter
|
|
78
83
|
};
|
|
79
84
|
const isPie = chartType === "pie" || chartType === "donut";
|
|
80
85
|
const isRadar = chartType === "radar";
|
|
@@ -101,12 +106,13 @@ function BaseChart({ data, chartType, xKey, yKey, orientation, height = 300, tit
|
|
|
101
106
|
stacked,
|
|
102
107
|
smooth,
|
|
103
108
|
showSymbol,
|
|
104
|
-
symbolSize
|
|
109
|
+
symbolSize,
|
|
110
|
+
interactive
|
|
105
111
|
});
|
|
106
|
-
return customOptions ? {
|
|
112
|
+
return applySelectionEmphasis(customOptions ? {
|
|
107
113
|
...opt,
|
|
108
114
|
...customOptions
|
|
109
|
-
} : opt;
|
|
115
|
+
} : opt, selected);
|
|
110
116
|
}, [
|
|
111
117
|
normalized,
|
|
112
118
|
colors,
|
|
@@ -125,8 +131,21 @@ function BaseChart({ data, chartType, xKey, yKey, orientation, height = 300, tit
|
|
|
125
131
|
showArea,
|
|
126
132
|
min,
|
|
127
133
|
max,
|
|
128
|
-
customOptions
|
|
134
|
+
customOptions,
|
|
135
|
+
valueFormatter,
|
|
136
|
+
selected,
|
|
137
|
+
interactive
|
|
129
138
|
]);
|
|
139
|
+
const axisLabels = useMemo(() => ({
|
|
140
|
+
xLabels: normalized.xData,
|
|
141
|
+
yLabels: "yAxisData" in normalized ? normalized.yAxisData : void 0
|
|
142
|
+
}), [normalized]);
|
|
143
|
+
const axisLabelsRef = useRef(axisLabels);
|
|
144
|
+
axisLabelsRef.current = axisLabels;
|
|
145
|
+
const onEvents = useMemo(() => interactive ? { click: (params, instance) => {
|
|
146
|
+
const result = onDataClickRef.current?.(mapToDatum(params, axisLabelsRef.current, instance));
|
|
147
|
+
if (result && typeof result.then === "function") result.catch(() => {});
|
|
148
|
+
} } : void 0, [interactive]);
|
|
130
149
|
if (!option) return /* @__PURE__ */ jsx("div", {
|
|
131
150
|
className: "flex items-center justify-center h-full text-muted-foreground",
|
|
132
151
|
children: "No data"
|
|
@@ -139,7 +158,8 @@ function BaseChart({ data, chartType, xKey, yKey, orientation, height = 300, tit
|
|
|
139
158
|
className,
|
|
140
159
|
opts: { renderer: "canvas" },
|
|
141
160
|
notMerge: false,
|
|
142
|
-
lazyUpdate: true
|
|
161
|
+
lazyUpdate: true,
|
|
162
|
+
onEvents
|
|
143
163
|
});
|
|
144
164
|
}
|
|
145
165
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.js","names":[],"sources":["../../../src/react/charts/base.tsx"],"sourcesContent":["// Type-only import: erased at compile time, does not pull the full echarts\n// bundle in. The `ECharts` type is used for the stored ECharts instance ref.\nimport type { ECharts } from \"echarts\";\nimport {\n BarChart,\n HeatmapChart,\n LineChart,\n PieChart,\n RadarChart,\n ScatterChart,\n} from \"echarts/charts\";\nimport {\n GridComponent,\n LegendComponent,\n TitleComponent,\n TooltipComponent,\n VisualMapComponent,\n} from \"echarts/components\";\nimport * as echarts from \"echarts/core\";\nimport { LegacyGridContainLabel } from \"echarts/features\";\nimport { CanvasRenderer } from \"echarts/renderers\";\n// Import the ESM core build, not `echarts-for-react/lib/core` (CJS). The package\n// has no `exports` map, so `/lib/core` resolves to the literal CommonJS file\n// whose default export is `module.exports`; under ESM default-import interop a\n// consumer's bundler hands back the whole `{ default, __esModule }` object rather\n// than the component, so `<ReactEChartsCore/>` renders an object and React throws\n// \"Element type is invalid\". `esm/core` is real ESM with a true `export default`.\nimport ReactEChartsCore from \"echarts-for-react/esm/core\";\nimport { useCallback, useMemo, useRef } from \"react\";\nimport { normalizeChartData, normalizeHeatmapData } from \"./normalize\";\nimport {\n buildCartesianOption,\n buildHeatmapOption,\n buildHorizontalBarOption,\n buildPieOption,\n buildRadarOption,\n type OptionBuilderContext,\n} from \"./options\";\nimport { useChartUITokens, useThemeColors } from \"./theme\";\nimport type {\n ChartColorPalette,\n ChartData,\n ChartType,\n Orientation,\n} from \"./types\";\n\n// ============================================================================\n// ECharts Registration (modular imports for tree-shaking)\n// ============================================================================\n// Only the chart types and components used by the option builders in\n// `options.ts` are registered. Importing from `echarts/core` instead of the\n// full `echarts` entry keeps unused chart types (graph, sankey, gauge, ...)\n// out of consumer bundles.\n//\n// If you add a new chart type or use a new ECharts feature (e.g. dataZoom,\n// toolbox, markLine), register it here. Consumers passing custom `options`\n// that need extra features can register them in their own app via\n// `import { use } from \"echarts/core\"`.\n//\n// Note: tooltip-driven axisPointer is bundled with `TooltipComponent`, so no\n// explicit AxisPointerComponent registration is needed.\n//\n// This `use()` call must stay co-located in the same module as `BaseChart`:\n// `package.json#sideEffects` declares JS modules side-effect free, so moving\n// registration to a separate import-for-side-effect module would let bundlers\n// drop it during tree-shaking.\necharts.use([\n // Series types used by the option builders\n LineChart, // line + area charts (area = line with areaStyle)\n BarChart, // bar + horizontal bar charts\n PieChart, // pie + donut charts\n ScatterChart,\n HeatmapChart,\n RadarChart,\n // Components referenced by built options\n TitleComponent, // `title`\n TooltipComponent, // `tooltip`\n LegendComponent, // `legend`\n GridComponent, // `grid` / `xAxis` / `yAxis`\n VisualMapComponent, // `visualMap` (heatmap color scale)\n // Features\n LegacyGridContainLabel, // `grid.containLabel` (cartesian option builder)\n // Renderer (BaseChart always renders with `renderer: \"canvas\"`)\n CanvasRenderer,\n]);\n\n// ============================================================================\n// Palette Selection\n// ============================================================================\n\n/**\n * Determines the appropriate color palette for a chart type.\n * - Heatmaps use sequential (low → high intensity)\n * - All other charts use categorical (distinct categories)\n */\nfunction getDefaultPalette(chartType: ChartType): ChartColorPalette {\n switch (chartType) {\n case \"heatmap\":\n return \"sequential\";\n default:\n return \"categorical\";\n }\n}\n\n// ============================================================================\n// Component Props\n// ============================================================================\n\nexport interface BaseChartProps {\n /** Chart data (Arrow Table or JSON array) - format is auto-detected */\n data: ChartData;\n /** Chart type */\n chartType: ChartType;\n /** X-axis field key (auto-detected from schema if not provided) */\n xKey?: string;\n /** Y-axis field key(s) (auto-detected from schema if not provided) */\n yKey?: string | string[];\n /** Chart orientation @default \"vertical\" */\n orientation?: Orientation;\n /** Chart height in pixels @default 300 */\n height?: number;\n /** Chart title */\n title?: string;\n /** Show legend @default true */\n showLegend?: boolean;\n /**\n * Color palette to use. Auto-selected based on chart type if not specified.\n * - \"categorical\": Distinct colors for different categories (bar, pie, line)\n * - \"sequential\": Gradient for magnitude (heatmap)\n * - \"diverging\": Two-tone for positive/negative (correlation)\n */\n colorPalette?: ChartColorPalette;\n /** Custom colors (overrides colorPalette) */\n colors?: string[];\n /** Show data point symbols (line/area charts) @default false */\n showSymbol?: boolean;\n /** Smooth line curves (line/area charts) @default true */\n smooth?: boolean;\n /** Stack series @default false */\n stacked?: boolean;\n /** Symbol size for scatter charts @default 8 */\n symbolSize?: number;\n /** Show area fill for radar charts @default true */\n showArea?: boolean;\n /** Inner radius for pie/donut (0-100) @default 0 */\n innerRadius?: number;\n /** Show labels on pie/donut slices @default true */\n showLabels?: boolean;\n /** Label position for pie/donut @default \"outside\" */\n labelPosition?: \"outside\" | \"inside\" | \"center\";\n /** Y-axis field key for heatmap (the row dimension) */\n yAxisKey?: string;\n /** Min value for heatmap color scale */\n min?: number;\n /** Max value for heatmap color scale */\n max?: number;\n /**\n * Additional ECharts options to merge.\n *\n * Only the built-in feature set is registered by this package, so options\n * referencing extra ECharts features (`dataZoom`, `toolbox`, `markLine`,\n * `markArea`, `graphic`, `dataset`, top-level `axisPointer`, ...) require\n * registering them in your app via `import { use } from \"echarts/core\"`.\n * This only works when your `echarts` resolves to the same module\n * instance/version as this package's (the registry is a singleton;\n * duplicate echarts copies won't share registrations).\n */\n options?: Record<string, unknown>;\n /** Additional CSS classes */\n className?: string;\n}\n\n// ============================================================================\n// Base Chart Component\n// ============================================================================\n\n/**\n * Base chart component that handles both Arrow and JSON data.\n * Renders using ECharts for consistent output across both formats.\n */\nexport function BaseChart({\n data,\n chartType,\n xKey,\n yKey,\n orientation,\n height = 300,\n title,\n showLegend = true,\n colorPalette,\n colors: customColors,\n showSymbol = false,\n smooth = true,\n stacked = false,\n symbolSize = 8,\n showArea = true,\n innerRadius = 0,\n showLabels = true,\n labelPosition = \"outside\",\n yAxisKey,\n min,\n max,\n options: customOptions,\n className,\n}: BaseChartProps) {\n // Determine the appropriate color palette based on chart type\n const resolvedPalette = colorPalette ?? getDefaultPalette(chartType);\n const themeColors = useThemeColors(resolvedPalette);\n const colors = customColors ?? themeColors;\n\n const ui = useChartUITokens();\n\n // Store ECharts instance directly to avoid stale ref issues on unmount\n const echartsInstanceRef = useRef<ECharts | null>(null);\n\n // Callback ref pattern: captures the ECharts instance when ReactECharts mounts\n // This ensures we always have a stable reference to the actual instance\n const chartRefCallback = useCallback((node: ReactEChartsCore | null) => {\n // Dispose previous instance if component is being replaced\n if (\n echartsInstanceRef.current &&\n !echartsInstanceRef.current.isDisposed()\n ) {\n echartsInstanceRef.current.dispose();\n }\n\n // Store the new instance\n if (node) {\n echartsInstanceRef.current = node.getEchartsInstance();\n } else {\n // Component unmounting - dispose the stored instance\n if (\n echartsInstanceRef.current &&\n !echartsInstanceRef.current.isDisposed()\n ) {\n echartsInstanceRef.current.dispose();\n }\n echartsInstanceRef.current = null;\n }\n }, []);\n\n // Memoize data normalization\n const normalized = useMemo(\n () =>\n chartType === \"heatmap\"\n ? normalizeHeatmapData(data, xKey, yAxisKey, yKey)\n : normalizeChartData(data, xKey, yKey, orientation),\n [data, xKey, yKey, yAxisKey, orientation, chartType],\n );\n\n // Memoize option building\n const option = useMemo(() => {\n const { xData, yFields, xField, chartType: detectedChartType } = normalized;\n\n if (xData.length === 0) return null;\n\n // Determine chart mode first (needed to handle yDataMap)\n const isHeatmap = chartType === \"heatmap\";\n\n // Heatmaps use heatmapData instead of yDataMap\n // For other charts, yDataMap is required\n const yDataMap = \"yDataMap\" in normalized ? normalized.yDataMap : {};\n\n const baseCtx: OptionBuilderContext = {\n xData,\n yDataMap,\n yFields,\n colors,\n title,\n showLegend,\n xField,\n ui,\n };\n const isPie = chartType === \"pie\" || chartType === \"donut\";\n const isRadar = chartType === \"radar\";\n const isHorizontal =\n !isPie &&\n !isRadar &&\n !isHeatmap &&\n (orientation === \"horizontal\" ||\n (detectedChartType === \"categorical\" &&\n !orientation &&\n chartType === \"bar\"));\n const isTimeSeries =\n detectedChartType === \"timeseries\" &&\n !isHorizontal &&\n !isRadar &&\n !isHeatmap;\n\n // Build option based on chart type\n let opt: Record<string, unknown>;\n\n if (isHeatmap && \"yAxisData\" in normalized && \"heatmapData\" in normalized) {\n const heatmapNorm = normalized as {\n yAxisData: (string | number)[];\n heatmapData: [number, number, number][];\n min: number;\n max: number;\n } & typeof normalized;\n opt = buildHeatmapOption({\n ...baseCtx,\n yAxisData: heatmapNorm.yAxisData,\n heatmapData: heatmapNorm.heatmapData,\n min: min ?? heatmapNorm.min,\n max: max ?? heatmapNorm.max,\n showLabels,\n });\n } else if (isRadar) {\n opt = buildRadarOption(baseCtx, showArea);\n } else if (isPie) {\n opt = buildPieOption(\n baseCtx,\n chartType as \"pie\" | \"donut\",\n innerRadius,\n showLabels,\n labelPosition,\n );\n } else if (isHorizontal) {\n opt = buildHorizontalBarOption(baseCtx, stacked);\n } else {\n opt = buildCartesianOption({\n ...baseCtx,\n chartType,\n isTimeSeries,\n stacked,\n smooth,\n showSymbol,\n symbolSize,\n });\n }\n\n // Merge custom options\n return customOptions ? { ...opt, ...customOptions } : opt;\n }, [\n normalized,\n colors,\n ui,\n title,\n showLegend,\n chartType,\n orientation,\n innerRadius,\n showLabels,\n labelPosition,\n stacked,\n smooth,\n showSymbol,\n symbolSize,\n showArea,\n min,\n max,\n customOptions,\n ]);\n\n if (!option) {\n return (\n <div className=\"flex items-center justify-center h-full text-muted-foreground\">\n No data\n </div>\n );\n }\n\n return (\n <ReactEChartsCore\n ref={chartRefCallback}\n echarts={echarts}\n option={option}\n style={{ height }}\n className={className}\n opts={{ renderer: \"canvas\" }}\n notMerge={false}\n lazyUpdate={true}\n />\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAkEA,QAAQ,IAAI;CAEV;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CAEA;CAEA;CACD,CAAC;;;;;;AAWF,SAAS,kBAAkB,WAAyC;AAClE,SAAQ,WAAR;EACE,KAAK,UACH,QAAO;EACT,QACE,QAAO;;;;;;;AAgFb,SAAgB,UAAU,EACxB,MACA,WACA,MACA,MACA,aACA,SAAS,KACT,OACA,aAAa,MACb,cACA,QAAQ,cACR,aAAa,OACb,SAAS,MACT,UAAU,OACV,aAAa,GACb,WAAW,MACX,cAAc,GACd,aAAa,MACb,gBAAgB,WAChB,UACA,KACA,KACA,SAAS,eACT,aACiB;CAGjB,MAAM,cAAc,eADI,gBAAgB,kBAAkB,UAAU,CACjB;CACnD,MAAM,SAAS,gBAAgB;CAE/B,MAAM,KAAK,kBAAkB;CAG7B,MAAM,qBAAqB,OAAuB,KAAK;CAIvD,MAAM,mBAAmB,aAAa,SAAkC;AAEtE,MACE,mBAAmB,WACnB,CAAC,mBAAmB,QAAQ,YAAY,CAExC,oBAAmB,QAAQ,SAAS;AAItC,MAAI,KACF,oBAAmB,UAAU,KAAK,oBAAoB;OACjD;AAEL,OACE,mBAAmB,WACnB,CAAC,mBAAmB,QAAQ,YAAY,CAExC,oBAAmB,QAAQ,SAAS;AAEtC,sBAAmB,UAAU;;IAE9B,EAAE,CAAC;CAGN,MAAM,aAAa,cAEf,cAAc,YACV,qBAAqB,MAAM,MAAM,UAAU,KAAK,GAChD,mBAAmB,MAAM,MAAM,MAAM,YAAY,EACvD;EAAC;EAAM;EAAM;EAAM;EAAU;EAAa;EAAU,CACrD;CAGD,MAAM,SAAS,cAAc;EAC3B,MAAM,EAAE,OAAO,SAAS,QAAQ,WAAW,sBAAsB;AAEjE,MAAI,MAAM,WAAW,EAAG,QAAO;EAG/B,MAAM,YAAY,cAAc;EAMhC,MAAM,UAAgC;GACpC;GACA,UAJe,cAAc,aAAa,WAAW,WAAW,EAAE;GAKlE;GACA;GACA;GACA;GACA;GACA;GACD;EACD,MAAM,QAAQ,cAAc,SAAS,cAAc;EACnD,MAAM,UAAU,cAAc;EAC9B,MAAM,eACJ,CAAC,SACD,CAAC,WACD,CAAC,cACA,gBAAgB,gBACd,sBAAsB,iBACrB,CAAC,eACD,cAAc;EACpB,MAAM,eACJ,sBAAsB,gBACtB,CAAC,gBACD,CAAC,WACD,CAAC;EAGH,IAAI;AAEJ,MAAI,aAAa,eAAe,cAAc,iBAAiB,YAAY;GACzE,MAAM,cAAc;AAMpB,SAAM,mBAAmB;IACvB,GAAG;IACH,WAAW,YAAY;IACvB,aAAa,YAAY;IACzB,KAAK,OAAO,YAAY;IACxB,KAAK,OAAO,YAAY;IACxB;IACD,CAAC;aACO,QACT,OAAM,iBAAiB,SAAS,SAAS;WAChC,MACT,OAAM,eACJ,SACA,WACA,aACA,YACA,cACD;WACQ,aACT,OAAM,yBAAyB,SAAS,QAAQ;MAEhD,OAAM,qBAAqB;GACzB,GAAG;GACH;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;AAIJ,SAAO,gBAAgB;GAAE,GAAG;GAAK,GAAG;GAAe,GAAG;IACrD;EACD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;AAEF,KAAI,CAAC,OACH,QACE,oBAAC;EAAI,WAAU;YAAgE;GAEzE;AAIV,QACE,oBAAC;EACC,KAAK;EACI;EACD;EACR,OAAO,EAAE,QAAQ;EACN;EACX,MAAM,EAAE,UAAU,UAAU;EAC5B,UAAU;EACV,YAAY;GACZ"}
|
|
1
|
+
{"version":3,"file":"base.js","names":[],"sources":["../../../src/react/charts/base.tsx"],"sourcesContent":["// Type-only import: erased at compile time, does not pull the full echarts\n// bundle in. The `ECharts` type is used for the stored ECharts instance ref.\nimport type { ECharts } from \"echarts\";\nimport {\n BarChart,\n HeatmapChart,\n LineChart,\n PieChart,\n RadarChart,\n ScatterChart,\n} from \"echarts/charts\";\nimport {\n GridComponent,\n LegendComponent,\n TitleComponent,\n TooltipComponent,\n VisualMapComponent,\n} from \"echarts/components\";\nimport * as echarts from \"echarts/core\";\nimport { LegacyGridContainLabel } from \"echarts/features\";\nimport { CanvasRenderer } from \"echarts/renderers\";\n// Import the ESM core build, not `echarts-for-react/lib/core` (CJS). The package\n// has no `exports` map, so `/lib/core` resolves to the literal CommonJS file\n// whose default export is `module.exports`; under ESM default-import interop a\n// consumer's bundler hands back the whole `{ default, __esModule }` object rather\n// than the component, so `<ReactEChartsCore/>` renders an object and React throws\n// \"Element type is invalid\". `esm/core` is real ESM with a true `export default`.\nimport ReactEChartsCore from \"echarts-for-react/esm/core\";\nimport { useCallback, useMemo, useRef } from \"react\";\nimport { normalizeChartData, normalizeHeatmapData } from \"./normalize\";\nimport {\n applySelectionEmphasis,\n buildCartesianOption,\n buildHeatmapOption,\n buildHorizontalBarOption,\n buildPieOption,\n buildRadarOption,\n type OptionBuilderContext,\n} from \"./options\";\nimport { useChartUITokens, useThemeColors } from \"./theme\";\nimport type {\n ChartBaseProps,\n ChartClickDatum,\n ChartColorPalette,\n ChartData,\n ChartType,\n ChartValueFormatter,\n Orientation,\n} from \"./types\";\nimport { mapToDatum } from \"./utils\";\n\n// ============================================================================\n// ECharts Registration (modular imports for tree-shaking)\n// ============================================================================\n// Only the chart types and components used by the option builders in\n// `options.ts` are registered. Importing from `echarts/core` instead of the\n// full `echarts` entry keeps unused chart types (graph, sankey, gauge, ...)\n// out of consumer bundles.\n//\n// If you add a new chart type or use a new ECharts feature (e.g. dataZoom,\n// toolbox, markLine), register it here. Consumers passing custom `options`\n// that need extra features can register them in their own app via\n// `import { use } from \"echarts/core\"`.\n//\n// Note: tooltip-driven axisPointer is bundled with `TooltipComponent`, so no\n// explicit AxisPointerComponent registration is needed.\n//\n// This `use()` call must stay co-located in the same module as `BaseChart`:\n// `package.json#sideEffects` declares JS modules side-effect free, so moving\n// registration to a separate import-for-side-effect module would let bundlers\n// drop it during tree-shaking.\necharts.use([\n // Series types used by the option builders\n LineChart, // line + area charts (area = line with areaStyle)\n BarChart, // bar + horizontal bar charts\n PieChart, // pie + donut charts\n ScatterChart,\n HeatmapChart,\n RadarChart,\n // Components referenced by built options\n TitleComponent, // `title`\n TooltipComponent, // `tooltip`\n LegendComponent, // `legend`\n GridComponent, // `grid` / `xAxis` / `yAxis`\n VisualMapComponent, // `visualMap` (heatmap color scale)\n // Features\n LegacyGridContainLabel, // `grid.containLabel` (cartesian option builder)\n // Renderer (BaseChart always renders with `renderer: \"canvas\"`)\n CanvasRenderer,\n]);\n\n// ============================================================================\n// Palette Selection\n// ============================================================================\n\n/**\n * Determines the appropriate color palette for a chart type.\n * - Heatmaps use sequential (low → high intensity)\n * - All other charts use categorical (distinct categories)\n */\nfunction getDefaultPalette(chartType: ChartType): ChartColorPalette {\n switch (chartType) {\n case \"heatmap\":\n return \"sequential\";\n default:\n return \"categorical\";\n }\n}\n\n// ============================================================================\n// Component Props\n// ============================================================================\n\nexport interface BaseChartProps {\n /** Chart data (Arrow Table or JSON array) - format is auto-detected */\n data: ChartData;\n /** Chart type */\n chartType: ChartType;\n /** X-axis field key (auto-detected from schema if not provided) */\n xKey?: string;\n /** Y-axis field key(s) (auto-detected from schema if not provided) */\n yKey?: string | string[];\n /** Chart orientation @default \"vertical\" */\n orientation?: Orientation;\n /** Chart height in pixels @default 300 */\n height?: number;\n /** Chart title */\n title?: string;\n /** Show legend @default true */\n showLegend?: boolean;\n /**\n * Color palette to use. Auto-selected based on chart type if not specified.\n * - \"categorical\": Distinct colors for different categories (bar, pie, line)\n * - \"sequential\": Gradient for magnitude (heatmap)\n * - \"diverging\": Two-tone for positive/negative (correlation)\n */\n colorPalette?: ChartColorPalette;\n /** Custom colors (overrides colorPalette) */\n colors?: string[];\n /** Show data point symbols (line/area charts) @default false */\n showSymbol?: boolean;\n /** Smooth line curves (line/area charts) @default true */\n smooth?: boolean;\n /** Stack series @default false */\n stacked?: boolean;\n /** Symbol size for scatter charts @default 8 */\n symbolSize?: number;\n /** Show area fill for radar charts @default true */\n showArea?: boolean;\n /** Inner radius for pie/donut (0-100) @default 0 */\n innerRadius?: number;\n /** Show labels on pie/donut slices @default true */\n showLabels?: boolean;\n /** Label position for pie/donut @default \"outside\" */\n labelPosition?: \"outside\" | \"inside\" | \"center\";\n /** Y-axis field key for heatmap (the row dimension) */\n yAxisKey?: string;\n /** Min value for heatmap color scale */\n min?: number;\n /** Max value for heatmap color scale */\n max?: number;\n /**\n * Additional ECharts options to merge.\n *\n * Only the built-in feature set is registered by this package, so options\n * referencing extra ECharts features (`dataZoom`, `toolbox`, `markLine`,\n * `markArea`, `graphic`, `dataset`, top-level `axisPointer`, ...) require\n * registering them in your app via `import { use } from \"echarts/core\"`.\n * This only works when your `echarts` resolves to the same module\n * instance/version as this package's (the registry is a singleton;\n * duplicate echarts copies won't share registrations).\n */\n options?: Record<string, unknown>;\n /** Formats measure values. See {@link ChartBaseProps.valueFormatter}. */\n valueFormatter?: ChartValueFormatter;\n /** Additional CSS classes */\n className?: string;\n /**\n * Fired when a data element (bar, slice, point) is clicked. Fire-and-forget:\n * the return value is ignored (async handlers are fine — the chart never awaits).\n * The handler receives a normalized {@link ChartClickDatum}.\n *\n * Pointer-only: charts render to <canvas>, so this does not fire for keyboard\n * users. Provide a keyboard-accessible equivalent (e.g. a table row action) for\n * the same action.\n */\n onDataClick?: (datum: ChartClickDatum) => void;\n /** Controlled selection by category name. See {@link ChartBaseProps.selected}. */\n selected?: string | string[];\n}\n\n// ============================================================================\n// Base Chart Component\n// ============================================================================\n\n/**\n * Base chart component that handles both Arrow and JSON data.\n * Renders using ECharts for consistent output across both formats.\n */\nexport function BaseChart({\n data,\n chartType,\n xKey,\n yKey,\n orientation,\n height = 300,\n title,\n showLegend = true,\n colorPalette,\n colors: customColors,\n showSymbol = false,\n smooth = true,\n stacked = false,\n symbolSize = 8,\n showArea = true,\n innerRadius = 0,\n showLabels = true,\n labelPosition = \"outside\",\n yAxisKey,\n min,\n max,\n options: customOptions,\n valueFormatter,\n className,\n onDataClick,\n selected,\n}: BaseChartProps) {\n // Determine the appropriate color palette based on chart type\n const resolvedPalette = colorPalette ?? getDefaultPalette(chartType);\n const themeColors = useThemeColors(resolvedPalette);\n const colors = customColors ?? themeColors;\n\n const ui = useChartUITokens();\n\n // Handler presence enables line interaction and gates events. Tracking the\n // boolean keeps inline handler identities from rebuilding chart options.\n const interactive = !!onDataClick;\n\n // Keep the latest handler in a ref so `onEvents` can call the current\n // `onDataClick` without listing it as a dependency (see `onEvents` below).\n const onDataClickRef = useRef(onDataClick);\n onDataClickRef.current = onDataClick;\n\n // Store ECharts instance directly to avoid stale ref issues on unmount\n const echartsInstanceRef = useRef<ECharts | null>(null);\n\n // Callback ref pattern: captures the ECharts instance when ReactECharts mounts\n // This ensures we always have a stable reference to the actual instance\n const chartRefCallback = useCallback((node: ReactEChartsCore | null) => {\n // Dispose previous instance if component is being replaced\n if (\n echartsInstanceRef.current &&\n !echartsInstanceRef.current.isDisposed()\n ) {\n echartsInstanceRef.current.dispose();\n }\n\n // Store the new instance\n if (node) {\n echartsInstanceRef.current = node.getEchartsInstance();\n } else {\n // Component unmounting - dispose the stored instance\n if (\n echartsInstanceRef.current &&\n !echartsInstanceRef.current.isDisposed()\n ) {\n echartsInstanceRef.current.dispose();\n }\n echartsInstanceRef.current = null;\n }\n }, []);\n\n // Memoize data normalization\n const normalized = useMemo(\n () =>\n chartType === \"heatmap\"\n ? normalizeHeatmapData(data, xKey, yAxisKey, yKey)\n : normalizeChartData(data, xKey, yKey, orientation),\n [data, xKey, yKey, yAxisKey, orientation, chartType],\n );\n\n // Memoize option building\n const option = useMemo(() => {\n const { xData, yFields, xField, chartType: detectedChartType } = normalized;\n\n if (xData.length === 0) return null;\n\n // Determine chart mode first (needed to handle yDataMap)\n const isHeatmap = chartType === \"heatmap\";\n\n // Heatmaps use heatmapData instead of yDataMap\n // For other charts, yDataMap is required\n const yDataMap = \"yDataMap\" in normalized ? normalized.yDataMap : {};\n\n const baseCtx: OptionBuilderContext = {\n xData,\n yDataMap,\n yFields,\n colors,\n title,\n showLegend,\n xField,\n ui,\n valueFormatter,\n };\n const isPie = chartType === \"pie\" || chartType === \"donut\";\n const isRadar = chartType === \"radar\";\n const isHorizontal =\n !isPie &&\n !isRadar &&\n !isHeatmap &&\n (orientation === \"horizontal\" ||\n (detectedChartType === \"categorical\" &&\n !orientation &&\n chartType === \"bar\"));\n const isTimeSeries =\n detectedChartType === \"timeseries\" &&\n !isHorizontal &&\n !isRadar &&\n !isHeatmap;\n\n // Build option based on chart type\n let opt: Record<string, unknown>;\n\n if (isHeatmap && \"yAxisData\" in normalized && \"heatmapData\" in normalized) {\n const heatmapNorm = normalized as {\n yAxisData: (string | number)[];\n heatmapData: [number, number, number][];\n min: number;\n max: number;\n } & typeof normalized;\n opt = buildHeatmapOption({\n ...baseCtx,\n yAxisData: heatmapNorm.yAxisData,\n heatmapData: heatmapNorm.heatmapData,\n min: min ?? heatmapNorm.min,\n max: max ?? heatmapNorm.max,\n showLabels,\n });\n } else if (isRadar) {\n opt = buildRadarOption(baseCtx, showArea);\n } else if (isPie) {\n opt = buildPieOption(\n baseCtx,\n chartType as \"pie\" | \"donut\",\n innerRadius,\n showLabels,\n labelPosition,\n );\n } else if (isHorizontal) {\n opt = buildHorizontalBarOption(baseCtx, stacked);\n } else {\n opt = buildCartesianOption({\n ...baseCtx,\n chartType,\n isTimeSeries,\n stacked,\n smooth,\n showSymbol,\n symbolSize,\n interactive,\n });\n }\n\n // Apply selection after custom options; empty selection is a no-op.\n const merged = customOptions ? { ...opt, ...customOptions } : opt;\n return applySelectionEmphasis(merged, selected);\n }, [\n normalized,\n colors,\n ui,\n title,\n showLegend,\n chartType,\n orientation,\n innerRadius,\n showLabels,\n labelPosition,\n stacked,\n smooth,\n showSymbol,\n symbolSize,\n showArea,\n min,\n max,\n customOptions,\n valueFormatter,\n selected,\n interactive,\n ]);\n\n // Heatmap data uses axis indexes; preserve labels for click results instead\n // of exposing those raw positions.\n const axisLabels = useMemo(\n () => ({\n xLabels: normalized.xData,\n yLabels:\n \"yAxisData\" in normalized\n ? (normalized.yAxisData as (string | number)[])\n : undefined,\n }),\n [normalized],\n );\n\n // `onEvents` must not re-subscribe when the data changes (e.g. each SSE tick).\n const axisLabelsRef = useRef(axisLabels);\n axisLabelsRef.current = axisLabels;\n\n // Memoize by handler presence because echarts-for-react re-subscribes whenever\n // `onEvents` changes, while callers commonly pass a new inline callback on\n // every render. This also avoids churn during frequently changing data such\n // as SSE ticks.\n const onEvents = useMemo(\n () =>\n interactive\n ? {\n click: (params: unknown, instance: ECharts) => {\n const result = onDataClickRef.current?.(\n mapToDatum(params, axisLabelsRef.current, instance),\n ) as void | Promise<void>;\n if (\n result &&\n typeof (result as Promise<void>).then === \"function\"\n ) {\n (result as Promise<void>).catch(() => {});\n }\n },\n }\n : undefined,\n [interactive],\n );\n\n if (!option) {\n return (\n <div className=\"flex items-center justify-center h-full text-muted-foreground\">\n No data\n </div>\n );\n }\n\n return (\n <ReactEChartsCore\n ref={chartRefCallback}\n echarts={echarts}\n option={option}\n style={{ height }}\n className={className}\n opts={{ renderer: \"canvas\" }}\n notMerge={false}\n lazyUpdate={true}\n onEvents={onEvents}\n />\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DATE_FIELD_PATTERNS, METADATA_DATE_PATTERNS, NAME_FIELD_PATTERNS } from "../../js/constants.js";
|
|
2
|
-
import {
|
|
2
|
+
import { formatLabel } from "../../js/format/index.js";
|
|
3
|
+
import { AreaChartProps, AreaChartSpecificProps, BarChartProps, BarChartSpecificProps, ChartBaseProps, ChartClickDatum, ChartColorPalette, ChartData, ChartType, ChartUITokens, ChartValueFormatter, DataFormat, DataProps, DonutChartProps, HeatmapChartProps, HeatmapChartSpecificProps, LineChartProps, LineChartSpecificProps, NormalizedChartData, NormalizedChartDataBase, Orientation, PieChartProps, PieChartSpecificProps, QueryProps, RadarChartProps, RadarChartSpecificProps, ScatterChartProps, ScatterChartSpecificProps, UnifiedChartProps, isArrowTable, isDataProps, isQueryProps } from "./types.js";
|
|
3
4
|
import { AreaChart } from "./area/index.js";
|
|
4
5
|
import { BarChart } from "./bar/index.js";
|
|
5
6
|
import { HeatmapChart } from "./heatmap/index.js";
|
|
@@ -15,5 +16,5 @@ import { ChartWrapper, ChartWrapperProps } from "./wrapper.js";
|
|
|
15
16
|
import { NormalizedHeatmapData, normalizeChartData, normalizeHeatmapData } from "./normalize.js";
|
|
16
17
|
import { CHART_COLOR_VARS, CHART_COLOR_VARS_CATEGORICAL, CHART_COLOR_VARS_DIVERGING, CHART_COLOR_VARS_SEQUENTIAL, FALLBACK_COLORS_CATEGORICAL, FALLBACK_COLORS_DIVERGING, FALLBACK_COLORS_SEQUENTIAL } from "./constants.js";
|
|
17
18
|
import { useAllThemeColors, useChartUITokens, useThemeColors } from "./theme.js";
|
|
18
|
-
import { createTimeSeriesData,
|
|
19
|
-
import { CartesianContext, HeatmapContext, OptionBuilderContext, buildCartesianOption, buildHeatmapOption, buildHorizontalBarOption, buildPieOption, buildRadarOption } from "./options.js";
|
|
19
|
+
import { createTimeSeriesData, sortTimeSeriesAscending, toChartArray, toChartValue, truncateLabel } from "./utils.js";
|
|
20
|
+
import { CartesianContext, HeatmapContext, OptionBuilderContext, applySelectionEmphasis, buildCartesianOption, buildHeatmapOption, buildHorizontalBarOption, buildPieOption, buildRadarOption } from "./options.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"normalize.d.ts","names":[],"sources":["../../../src/react/charts/normalize.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"normalize.d.ts","names":[],"sources":["../../../src/react/charts/normalize.ts"],"mappings":";;;;;AA4NA;;iBAAgB,kBAAA,CACd,IAAA,EAAM,SAAA,EACN,IAAA,WACA,IAAA,sBACA,WAAA,GAAc,WAAA,GACb,mBAAA;;;;;;UAiGc,qBAAA,SAA8B,uBAAA;EArG7C;EAuGA,SAAA;EArGA;EAuGA,WAAA;EAtGA;EAwGA,GAAA;EAvGoB;EAyGpB,GAAA;AAAA;;;;;;;;;;iBAYc,oBAAA,CACd,IAAA,EAAM,SAAA,EACN,IAAA,WACA,QAAA,WACA,QAAA,uBACC,qBAAA"}
|