@agent-native/toolkit 0.213.1 → 0.216.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/agent-native.eject.json +9 -0
- package/dist/app/agentkit/react/chat.d.ts.map +1 -1
- package/dist/app/agentkit/react/chat.js +2 -2
- package/dist/app/agentkit/react/chat.js.map +1 -1
- package/dist/app/agentkit/react/components.d.ts +4 -1
- package/dist/app/agentkit/react/components.d.ts.map +1 -1
- package/dist/app/agentkit/react/components.js +73 -13
- package/dist/app/agentkit/react/components.js.map +1 -1
- package/dist/app/agentkit/react/context.d.ts +19 -1
- package/dist/app/agentkit/react/context.d.ts.map +1 -1
- package/dist/app/agentkit/react/context.js +7 -1
- package/dist/app/agentkit/react/context.js.map +1 -1
- package/dist/app/agentkit/react/index.d.ts +2 -0
- package/dist/app/agentkit/react/index.d.ts.map +1 -1
- package/dist/app/agentkit/react/index.js +2 -0
- package/dist/app/agentkit/react/index.js.map +1 -1
- package/dist/app/agentkit/react/message-content-order.d.ts +21 -0
- package/dist/app/agentkit/react/message-content-order.d.ts.map +1 -0
- package/dist/app/agentkit/react/message-content-order.js +173 -0
- package/dist/app/agentkit/react/message-content-order.js.map +1 -0
- package/dist/app/agentkit/react/root.d.ts +6 -1
- package/dist/app/agentkit/react/root.d.ts.map +1 -1
- package/dist/app/agentkit/react/root.js +2 -2
- package/dist/app/agentkit/react/root.js.map +1 -1
- package/dist/app/agentkit/react/styles.css +45 -8
- package/dist/app/agentkit/react/transcript-layout.d.ts +51 -0
- package/dist/app/agentkit/react/transcript-layout.d.ts.map +1 -0
- package/dist/app/agentkit/react/transcript-layout.js +34 -0
- package/dist/app/agentkit/react/transcript-layout.js.map +1 -0
- package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.js +9 -4
- package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
- package/dist/app/chat/AgentPanel.d.ts +16 -2
- package/dist/app/chat/AgentPanel.d.ts.map +1 -1
- package/dist/app/chat/AgentPanel.js +26 -5
- package/dist/app/chat/AgentPanel.js.map +1 -1
- package/dist/app/chat/chat/index.d.ts +2 -0
- package/dist/app/chat/chat/index.d.ts.map +1 -1
- package/dist/app/chat/chat/index.js.map +1 -1
- package/dist/app/chat/chat/surface-types.d.ts +15 -0
- package/dist/app/chat/chat/surface-types.d.ts.map +1 -1
- package/dist/app/chat/chat/surface-types.js.map +1 -1
- package/dist/app/chat/chat/tool-call-display.d.ts +7 -2
- package/dist/app/chat/chat/tool-call-display.d.ts.map +1 -1
- package/dist/app/chat/chat/tool-call-display.js +10 -9
- package/dist/app/chat/chat/tool-call-display.js.map +1 -1
- package/dist/app/chat/chat/widgets/DataChartRenderer.d.ts.map +1 -1
- package/dist/app/chat/chat/widgets/DataChartRenderer.js +300 -33
- package/dist/app/chat/chat/widgets/DataChartRenderer.js.map +1 -1
- package/dist/app/chat/chat/widgets/DataChartWidget.d.ts +3 -2
- package/dist/app/chat/chat/widgets/DataChartWidget.d.ts.map +1 -1
- package/dist/app/chat/chat/widgets/DataChartWidget.js +18 -9
- package/dist/app/chat/chat/widgets/DataChartWidget.js.map +1 -1
- package/dist/app/chat/chat/widgets/DataInsightsWidget.js +1 -1
- package/dist/app/chat/chat/widgets/DataInsightsWidget.js.map +1 -1
- package/dist/app/chat/chat/widgets/DataTableWidget.d.ts +3 -2
- package/dist/app/chat/chat/widgets/DataTableWidget.d.ts.map +1 -1
- package/dist/app/chat/chat/widgets/DataTableWidget.js +10 -66
- package/dist/app/chat/chat/widgets/DataTableWidget.js.map +1 -1
- package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.d.ts +11 -0
- package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.d.ts.map +1 -0
- package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.js +133 -0
- package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.js.map +1 -0
- package/dist/app/chat/chat/widgets/builtin-tool-renderers.js +2 -2
- package/dist/app/chat/chat/widgets/builtin-tool-renderers.js.map +1 -1
- package/dist/app/chat/mcp-apps/McpAppRenderer.d.ts.map +1 -1
- package/dist/app/chat/mcp-apps/McpAppRenderer.js +6 -5
- package/dist/app/chat/mcp-apps/McpAppRenderer.js.map +1 -1
- package/dist/app/chat/mcp-apps/mcp-app-theme.d.ts +2 -0
- package/dist/app/chat/mcp-apps/mcp-app-theme.d.ts.map +1 -0
- package/dist/app/chat/mcp-apps/mcp-app-theme.js +11 -0
- package/dist/app/chat/mcp-apps/mcp-app-theme.js.map +1 -0
- package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ar-SA.js +9 -0
- package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
- package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/de-DE.js +9 -0
- package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
- package/dist/app/i18n/catalogs/en-US.d.ts +9 -0
- package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/en-US.js +9 -0
- package/dist/app/i18n/catalogs/en-US.js.map +1 -1
- package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/es-ES.js +9 -0
- package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
- package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/fr-FR.js +9 -0
- package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
- package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/hi-IN.js +9 -0
- package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
- package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ja-JP.js +9 -0
- package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
- package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ko-KR.js +9 -0
- package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
- package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/pt-BR.js +9 -0
- package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
- package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/zh-CN.js +9 -0
- package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
- package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/zh-TW.js +9 -0
- package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
- package/dist/app/providers/AppProviders.d.ts +2 -1
- package/dist/app/providers/AppProviders.d.ts.map +1 -1
- package/dist/app/providers/AppProviders.js +4 -3
- package/dist/app/providers/AppProviders.js.map +1 -1
- package/dist/app/providers/ThemeFamilySync.d.ts +2 -0
- package/dist/app/providers/ThemeFamilySync.d.ts.map +1 -0
- package/dist/app/providers/ThemeFamilySync.js +6 -0
- package/dist/app/providers/ThemeFamilySync.js.map +1 -0
- package/dist/app/settings/account/PreferencesSettings.d.ts.map +1 -1
- package/dist/app/settings/account/PreferencesSettings.js +2 -1
- package/dist/app/settings/account/PreferencesSettings.js.map +1 -1
- package/dist/app/settings/account/ThemeFamilySettings.d.ts +2 -0
- package/dist/app/settings/account/ThemeFamilySettings.d.ts.map +1 -0
- package/dist/app/settings/account/ThemeFamilySettings.js +31 -0
- package/dist/app/settings/account/ThemeFamilySettings.js.map +1 -0
- package/dist/app/settings/account/search-entries.d.ts.map +1 -1
- package/dist/app/settings/account/search-entries.js +6 -0
- package/dist/app/settings/account/search-entries.js.map +1 -1
- package/dist/app/shared/DefaultSpinner.d.ts.map +1 -1
- package/dist/app/shared/DefaultSpinner.js +4 -5
- package/dist/app/shared/DefaultSpinner.js.map +1 -1
- package/dist/app/shared/ErrorBoundary.d.ts +1 -0
- package/dist/app/shared/ErrorBoundary.d.ts.map +1 -1
- package/dist/app/shared/ErrorBoundary.js +27 -12
- package/dist/app/shared/ErrorBoundary.js.map +1 -1
- package/dist/app/shared/PoweredByBadge.d.ts.map +1 -1
- package/dist/app/shared/PoweredByBadge.js +8 -49
- package/dist/app/shared/PoweredByBadge.js.map +1 -1
- package/dist/app/styles/agent-conversation.css +0 -23
- package/dist/app/styles/agent-native.css +1 -1
- package/dist/app/styles/theme-families.css +66 -0
- package/dist/composer/ComposerContextBanner.d.ts +24 -0
- package/dist/composer/ComposerContextBanner.d.ts.map +1 -0
- package/dist/composer/ComposerContextBanner.js +9 -0
- package/dist/composer/ComposerContextBanner.js.map +1 -0
- package/dist/composer/ComposerModePicker.d.ts +26 -0
- package/dist/composer/ComposerModePicker.d.ts.map +1 -0
- package/dist/composer/ComposerModePicker.js +14 -0
- package/dist/composer/ComposerModePicker.js.map +1 -0
- package/dist/composer/index.d.ts +2 -0
- package/dist/composer/index.d.ts.map +1 -1
- package/dist/composer/index.js +2 -0
- package/dist/composer/index.js.map +1 -1
- package/dist/styles.css +1 -0
- package/package.json +3 -3
- package/src/app/agentkit/react/chat.tsx +2 -0
- package/src/app/agentkit/react/components.interaction.spec.tsx +107 -0
- package/src/app/agentkit/react/components.spec.tsx +40 -1
- package/src/app/agentkit/react/components.tsx +186 -31
- package/src/app/agentkit/react/context.tsx +29 -0
- package/src/app/agentkit/react/index.ts +2 -0
- package/src/app/agentkit/react/lifecycle.spec.tsx +289 -4
- package/src/app/agentkit/react/message-content-order.spec.ts +190 -0
- package/src/app/agentkit/react/message-content-order.ts +246 -0
- package/src/app/agentkit/react/root.tsx +11 -0
- package/src/app/agentkit/react/styles.css +45 -8
- package/src/app/agentkit/react/transcript-layout.spec.ts +72 -0
- package/src/app/agentkit/react/transcript-layout.ts +97 -0
- package/src/app/auth/AuthPage.attribution.spec.tsx +6 -0
- package/src/app/chat/AgentKitAssistantChat.spec.tsx +58 -0
- package/src/app/chat/AgentKitAssistantChat.tsx +15 -1
- package/src/app/chat/AgentPanel.header.spec.ts +72 -14
- package/src/app/chat/AgentPanel.page-header-share.spec.tsx +101 -0
- package/src/app/chat/AgentPanel.tsx +111 -55
- package/src/app/chat/chat/index.ts +2 -0
- package/src/app/chat/chat/surface-types.ts +18 -0
- package/src/app/chat/chat/tool-call-display.spec.tsx +83 -0
- package/src/app/chat/chat/tool-call-display.tsx +40 -8
- package/src/app/chat/chat/widgets/DataChartRenderer.spec.tsx +849 -0
- package/src/app/chat/chat/widgets/DataChartRenderer.tsx +567 -72
- package/src/app/chat/chat/widgets/DataChartWidget.spec.tsx +432 -0
- package/src/app/chat/chat/widgets/DataChartWidget.ssr.spec.tsx +25 -0
- package/src/app/chat/chat/widgets/DataChartWidget.tsx +58 -33
- package/src/app/chat/chat/widgets/DataInsightsWidget.tsx +2 -2
- package/src/app/chat/chat/widgets/DataTableWidget.spec.tsx +37 -3
- package/src/app/chat/chat/widgets/DataTableWidget.tsx +22 -86
- package/src/app/chat/chat/widgets/DataWidgetDisplayControls.tsx +321 -0
- package/src/app/chat/chat/widgets/builtin-tool-renderers.tsx +5 -5
- package/src/app/chat/mcp-apps/McpAppRenderer.tsx +10 -5
- package/src/app/chat/mcp-apps/mcp-app-theme.spec.ts +30 -0
- package/src/app/chat/mcp-apps/mcp-app-theme.ts +11 -0
- package/src/app/i18n/catalogs/ar-SA.ts +10 -0
- package/src/app/i18n/catalogs/de-DE.ts +11 -0
- package/src/app/i18n/catalogs/en-US.ts +11 -0
- package/src/app/i18n/catalogs/es-ES.ts +12 -0
- package/src/app/i18n/catalogs/fr-FR.ts +12 -0
- package/src/app/i18n/catalogs/hi-IN.ts +10 -0
- package/src/app/i18n/catalogs/ja-JP.ts +11 -0
- package/src/app/i18n/catalogs/ko-KR.ts +10 -0
- package/src/app/i18n/catalogs/pt-BR.ts +11 -0
- package/src/app/i18n/catalogs/zh-CN.ts +9 -0
- package/src/app/i18n/catalogs/zh-TW.ts +10 -0
- package/src/app/providers/AppProviders.spec.tsx +71 -0
- package/src/app/providers/AppProviders.tsx +10 -0
- package/src/app/providers/ThemeFamilySync.tsx +6 -0
- package/src/app/settings/account/PreferencesSettings.spec.tsx +13 -2
- package/src/app/settings/account/PreferencesSettings.tsx +8 -0
- package/src/app/settings/account/ThemeFamilySettings.tsx +61 -0
- package/src/app/settings/account/search-entries.ts +6 -0
- package/src/app/shared/DefaultSpinner.spec.tsx +14 -0
- package/src/app/shared/DefaultSpinner.tsx +4 -5
- package/src/app/shared/ErrorBoundary.spec.tsx +45 -2
- package/src/app/shared/ErrorBoundary.tsx +37 -12
- package/src/app/shared/PoweredByBadge.spec.tsx +16 -0
- package/src/app/shared/PoweredByBadge.tsx +8 -50
- package/src/app/styles/agent-conversation.css +0 -23
- package/src/app/styles/agent-native.css +1 -1
- package/src/app/styles/agent-native.spec.ts +18 -0
- package/src/app/styles/theme-families.css +66 -0
- package/src/app/styles/theme-families.spec.ts +29 -0
- package/src/composer/ComposerContextBanner.spec.tsx +100 -0
- package/src/composer/ComposerContextBanner.tsx +77 -0
- package/src/composer/ComposerModePicker.spec.tsx +101 -0
- package/src/composer/ComposerModePicker.tsx +98 -0
- package/src/composer/index.ts +9 -0
- package/src/styles.css +1 -0
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
import { useT } from "@agent-native/core/client/i18n";
|
|
2
|
+
import { DATA_WIDGET_MAX_CHART_POINTS } from "@agent-native/core/data-widgets";
|
|
2
3
|
|
|
3
4
|
import type { DataChartWidget as DataChartWidgetData } from "./data-widget-types.js";
|
|
4
5
|
|
|
5
6
|
const DEFAULT_COLORS = [
|
|
6
|
-
"hsl(var(--primary))",
|
|
7
|
-
"hsl(199 89% 48%)",
|
|
8
|
-
"hsl(151 55% 42%)",
|
|
9
|
-
"hsl(35 92% 50%)",
|
|
7
|
+
"hsl(var(--chart-1, var(--primary)))",
|
|
8
|
+
"hsl(var(--chart-2, 199 89% 48%))",
|
|
9
|
+
"hsl(var(--chart-3, 151 55% 42%))",
|
|
10
|
+
"hsl(var(--chart-4, 35 92% 50%))",
|
|
10
11
|
];
|
|
11
12
|
|
|
12
13
|
const WIDTH = 640;
|
|
13
|
-
const HEIGHT =
|
|
14
|
+
const HEIGHT = 360;
|
|
14
15
|
const MARGIN = { top: 14, right: 18, bottom: 34, left: 46 };
|
|
15
|
-
const
|
|
16
|
-
const
|
|
16
|
+
const Y_AXIS_TITLE_X = 14;
|
|
17
|
+
const VALUE_AXIS_TICK_LABEL_INSET = 8;
|
|
18
|
+
const VALUE_AXIS_TITLE_TICK_GAP = 8;
|
|
19
|
+
const MAX_LABEL_LENGTH = 16;
|
|
20
|
+
const MIN_PLOT_WIDTH = 32;
|
|
21
|
+
const LABEL_FONT_FAMILIES = {
|
|
22
|
+
sans: "var(--font-sans, ui-sans-serif, system-ui, sans-serif)",
|
|
23
|
+
serif: "ui-serif, Georgia, Cambria, serif",
|
|
24
|
+
mono: "ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
25
|
+
} as const;
|
|
17
26
|
|
|
18
27
|
function numericValue(
|
|
19
28
|
row: Record<string, unknown>,
|
|
@@ -34,9 +43,83 @@ function formatTick(value: number): string {
|
|
|
34
43
|
return value.toFixed(1);
|
|
35
44
|
}
|
|
36
45
|
|
|
46
|
+
function formatAxisTick(value: number, interval: number): string {
|
|
47
|
+
const [mantissa, exponent = "0"] = interval
|
|
48
|
+
.toString()
|
|
49
|
+
.toLowerCase()
|
|
50
|
+
.split("e");
|
|
51
|
+
const decimalPlaces = Math.max(
|
|
52
|
+
0,
|
|
53
|
+
(mantissa.split(".")[1]?.length ?? 0) - Number(exponent),
|
|
54
|
+
);
|
|
55
|
+
if (Math.abs(value) >= 1000) {
|
|
56
|
+
return value.toLocaleString(undefined, {
|
|
57
|
+
minimumFractionDigits: decimalPlaces,
|
|
58
|
+
maximumFractionDigits: decimalPlaces,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
return value.toFixed(decimalPlaces);
|
|
62
|
+
}
|
|
63
|
+
|
|
37
64
|
function labelFor(value: unknown): string {
|
|
38
|
-
const
|
|
39
|
-
return
|
|
65
|
+
const characters = Array.from(value == null ? "" : String(value));
|
|
66
|
+
return characters.length > MAX_LABEL_LENGTH
|
|
67
|
+
? `${characters.slice(0, MAX_LABEL_LENGTH - 3).join("")}...`
|
|
68
|
+
: characters.join("");
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function estimateTextWidth(text: string, fontSize: number): number {
|
|
72
|
+
return Array.from(text).reduce((width, character) => {
|
|
73
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
74
|
+
return width + (codePoint >= 0x2e80 ? fontSize : fontSize * 0.65);
|
|
75
|
+
}, 0);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function formatCompactTick(
|
|
79
|
+
value: number,
|
|
80
|
+
fontSize: number,
|
|
81
|
+
maxWidth: number,
|
|
82
|
+
): string {
|
|
83
|
+
for (let fractionDigits = 15; fractionDigits >= 0; fractionDigits -= 1) {
|
|
84
|
+
const [mantissa = "0", exponent = "0"] = value
|
|
85
|
+
.toExponential(fractionDigits)
|
|
86
|
+
.split("e");
|
|
87
|
+
const label = `${mantissa.replace(/\.?0+$/, "")}e${exponent}`;
|
|
88
|
+
if (estimateTextWidth(label, fontSize) <= maxWidth) return label;
|
|
89
|
+
}
|
|
90
|
+
return value.toExponential(0);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function maximumHorizontalLabelWidth(
|
|
94
|
+
categoryMargin: number,
|
|
95
|
+
includeValueLabels: boolean,
|
|
96
|
+
): number {
|
|
97
|
+
let lower = 0;
|
|
98
|
+
let upper = WIDTH;
|
|
99
|
+
while (lower < upper) {
|
|
100
|
+
const candidate = Math.ceil((lower + upper) / 2);
|
|
101
|
+
const axisInset = Math.ceil(candidate / 2) + 2;
|
|
102
|
+
const valueLabelInset = includeValueLabels ? candidate + 13 : 0;
|
|
103
|
+
const leftMargin = Math.max(categoryMargin, axisInset) + valueLabelInset;
|
|
104
|
+
const rightMargin = Math.max(MARGIN.right, axisInset, valueLabelInset);
|
|
105
|
+
if (WIDTH - leftMargin - rightMargin >= MIN_PLOT_WIDTH) {
|
|
106
|
+
lower = candidate;
|
|
107
|
+
} else {
|
|
108
|
+
upper = candidate - 1;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return lower;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function formatHorizontalValueLabel(
|
|
115
|
+
value: number,
|
|
116
|
+
fontSize: number,
|
|
117
|
+
maxWidth: number,
|
|
118
|
+
): string {
|
|
119
|
+
const label = formatTick(value);
|
|
120
|
+
return estimateTextWidth(label, fontSize) > maxWidth
|
|
121
|
+
? formatCompactTick(value, fontSize, maxWidth)
|
|
122
|
+
: label;
|
|
40
123
|
}
|
|
41
124
|
|
|
42
125
|
function rangeFor(chart: DataChartWidgetData) {
|
|
@@ -61,76 +144,388 @@ function rangeFor(chart: DataChartWidgetData) {
|
|
|
61
144
|
return { min, max };
|
|
62
145
|
}
|
|
63
146
|
|
|
64
|
-
function ChartBody({
|
|
147
|
+
function ChartBody({
|
|
148
|
+
chart,
|
|
149
|
+
labelFontSize,
|
|
150
|
+
}: {
|
|
151
|
+
chart: DataChartWidgetData;
|
|
152
|
+
labelFontSize: number;
|
|
153
|
+
}) {
|
|
154
|
+
const categoryGapRatio = chart.horizontalCategoryGapRatio;
|
|
155
|
+
if (
|
|
156
|
+
categoryGapRatio !== undefined &&
|
|
157
|
+
(!Number.isFinite(categoryGapRatio) ||
|
|
158
|
+
categoryGapRatio < 0 ||
|
|
159
|
+
categoryGapRatio > 0.9)
|
|
160
|
+
) {
|
|
161
|
+
throw new Error("Horizontal category gap ratio must be between 0 and 0.9");
|
|
162
|
+
}
|
|
65
163
|
const { min, max } = rangeFor(chart);
|
|
66
|
-
const
|
|
67
|
-
|
|
164
|
+
const isHorizontalBar = chart.type === "bar" && chart.layout === "horizontal";
|
|
165
|
+
const tickInterval = chart.valueAxisTickInterval;
|
|
166
|
+
const gridTickValues =
|
|
167
|
+
tickInterval === undefined
|
|
168
|
+
? Array.from({ length: 4 }, (_, index) => min + ((max - min) * index) / 3)
|
|
169
|
+
: (() => {
|
|
170
|
+
if (!Number.isFinite(tickInterval) || tickInterval < 1e-12) {
|
|
171
|
+
throw new Error(
|
|
172
|
+
"Chart value-axis interval must be finite and positive",
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
if (!Number.isFinite(max - min)) {
|
|
176
|
+
throw new Error("Chart value-axis range must be finite");
|
|
177
|
+
}
|
|
178
|
+
const firstTick = Math.ceil(min / tickInterval - 1e-9);
|
|
179
|
+
const lastTick = Math.floor(max / tickInterval + 1e-9);
|
|
180
|
+
const tickCount = lastTick - firstTick + 1;
|
|
181
|
+
if (
|
|
182
|
+
!Number.isSafeInteger(firstTick) ||
|
|
183
|
+
!Number.isSafeInteger(lastTick) ||
|
|
184
|
+
!Number.isSafeInteger(tickCount) ||
|
|
185
|
+
tickCount <= 0 ||
|
|
186
|
+
tickCount > 100
|
|
187
|
+
) {
|
|
188
|
+
throw new Error("Chart value-axis interval exceeds the tick limit");
|
|
189
|
+
}
|
|
190
|
+
return Array.from(
|
|
191
|
+
{ length: tickCount },
|
|
192
|
+
(_, index) => (firstTick + index) * tickInterval,
|
|
193
|
+
);
|
|
194
|
+
})();
|
|
195
|
+
const axisTitleFontSize = chart.axisTitleFontSize ?? 11;
|
|
196
|
+
const horizontalCategoryWidth = isHorizontalBar
|
|
197
|
+
? Math.max(
|
|
198
|
+
0,
|
|
199
|
+
...chart.data.map((row) =>
|
|
200
|
+
estimateTextWidth(labelFor(row[chart.xKey]), labelFontSize),
|
|
201
|
+
),
|
|
202
|
+
)
|
|
203
|
+
: 0;
|
|
204
|
+
const categoryMargin =
|
|
205
|
+
Math.max(MARGIN.left, horizontalCategoryWidth + 14) +
|
|
206
|
+
(chart.yAxisTitle ? 20 : 0);
|
|
207
|
+
const categoryLabelX = categoryMargin - 8;
|
|
208
|
+
const horizontalTextWidthLimit = isHorizontalBar
|
|
209
|
+
? maximumHorizontalLabelWidth(
|
|
210
|
+
categoryMargin,
|
|
211
|
+
Boolean(chart.showValueLabels),
|
|
212
|
+
)
|
|
213
|
+
: 0;
|
|
214
|
+
const verticalValueTickMargin = isHorizontalBar
|
|
215
|
+
? 0
|
|
216
|
+
: chart.yAxisTitle
|
|
217
|
+
? Y_AXIS_TITLE_X +
|
|
218
|
+
axisTitleFontSize / 2 +
|
|
219
|
+
VALUE_AXIS_TITLE_TICK_GAP +
|
|
220
|
+
VALUE_AXIS_TICK_LABEL_INSET
|
|
221
|
+
: VALUE_AXIS_TICK_LABEL_INSET * 2;
|
|
222
|
+
const valueTickLabels = gridTickValues.map((value) => {
|
|
223
|
+
const label =
|
|
224
|
+
tickInterval === undefined
|
|
225
|
+
? formatTick(value)
|
|
226
|
+
: formatAxisTick(value, tickInterval);
|
|
227
|
+
const maximumWidth = isHorizontalBar
|
|
228
|
+
? horizontalTextWidthLimit
|
|
229
|
+
: WIDTH - MARGIN.right - MIN_PLOT_WIDTH - verticalValueTickMargin;
|
|
230
|
+
return estimateTextWidth(label, labelFontSize) > maximumWidth
|
|
231
|
+
? formatCompactTick(value, labelFontSize, maximumWidth)
|
|
232
|
+
: label;
|
|
233
|
+
});
|
|
234
|
+
const valueTickLabelWidth = Math.max(
|
|
235
|
+
0,
|
|
236
|
+
...valueTickLabels.map((label) => estimateTextWidth(label, labelFontSize)),
|
|
237
|
+
);
|
|
238
|
+
const customValueTickLabelWidth = isHorizontalBar ? 0 : valueTickLabelWidth;
|
|
239
|
+
const horizontalValueTickLabelInset = isHorizontalBar
|
|
240
|
+
? Math.ceil(valueTickLabelWidth / 2) + 2
|
|
241
|
+
: 0;
|
|
242
|
+
const customValueTickWidth = isHorizontalBar ? 0 : customValueTickLabelWidth;
|
|
243
|
+
const horizontalPositiveValueWidth =
|
|
244
|
+
isHorizontalBar && chart.showValueLabels
|
|
245
|
+
? Math.max(
|
|
246
|
+
0,
|
|
247
|
+
...chart.data.flatMap((row) =>
|
|
248
|
+
chart.series
|
|
249
|
+
.map((series) => numericValue(row, series.key))
|
|
250
|
+
.filter((value): value is number => value !== null && value >= 0)
|
|
251
|
+
.map((value) =>
|
|
252
|
+
estimateTextWidth(
|
|
253
|
+
formatHorizontalValueLabel(
|
|
254
|
+
value,
|
|
255
|
+
chart.valueLabelFontSize ?? 11,
|
|
256
|
+
horizontalTextWidthLimit,
|
|
257
|
+
),
|
|
258
|
+
chart.valueLabelFontSize ?? 11,
|
|
259
|
+
),
|
|
260
|
+
),
|
|
261
|
+
),
|
|
262
|
+
)
|
|
263
|
+
: 0;
|
|
264
|
+
const horizontalNegativeValueWidth =
|
|
265
|
+
isHorizontalBar && chart.showValueLabels
|
|
266
|
+
? Math.max(
|
|
267
|
+
0,
|
|
268
|
+
...chart.data.flatMap((row) =>
|
|
269
|
+
chart.series
|
|
270
|
+
.map((series) => numericValue(row, series.key))
|
|
271
|
+
.filter((value): value is number => value !== null && value < 0)
|
|
272
|
+
.map((value) =>
|
|
273
|
+
estimateTextWidth(
|
|
274
|
+
formatHorizontalValueLabel(
|
|
275
|
+
value,
|
|
276
|
+
chart.valueLabelFontSize ?? 11,
|
|
277
|
+
horizontalTextWidthLimit,
|
|
278
|
+
),
|
|
279
|
+
chart.valueLabelFontSize ?? 11,
|
|
280
|
+
),
|
|
281
|
+
),
|
|
282
|
+
),
|
|
283
|
+
)
|
|
284
|
+
: 0;
|
|
285
|
+
const margin = {
|
|
286
|
+
...MARGIN,
|
|
287
|
+
left:
|
|
288
|
+
Math.max(
|
|
289
|
+
categoryMargin,
|
|
290
|
+
customValueTickWidth + verticalValueTickMargin,
|
|
291
|
+
horizontalValueTickLabelInset,
|
|
292
|
+
) +
|
|
293
|
+
(horizontalNegativeValueWidth > 0
|
|
294
|
+
? horizontalNegativeValueWidth + 13
|
|
295
|
+
: 0),
|
|
296
|
+
right: Math.max(
|
|
297
|
+
MARGIN.right,
|
|
298
|
+
horizontalPositiveValueWidth + 13,
|
|
299
|
+
horizontalValueTickLabelInset,
|
|
300
|
+
),
|
|
301
|
+
bottom: MARGIN.bottom + (chart.xAxisTitle ? 16 : 0),
|
|
302
|
+
};
|
|
303
|
+
const plotWidth = WIDTH - margin.left - margin.right;
|
|
304
|
+
const plotHeight = HEIGHT - margin.top - margin.bottom;
|
|
305
|
+
const xForValue = (value: number) =>
|
|
306
|
+
margin.left + ((value - min) / (max - min)) * plotWidth;
|
|
307
|
+
const yForValue = (value: number) =>
|
|
308
|
+
margin.top + ((max - value) / (max - min)) * plotHeight;
|
|
68
309
|
const xForPoint = (index: number) =>
|
|
69
310
|
chart.data.length <= 1
|
|
70
|
-
?
|
|
71
|
-
:
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
311
|
+
? margin.left + plotWidth / 2
|
|
312
|
+
: margin.left + (index / (chart.data.length - 1)) * plotWidth;
|
|
313
|
+
const yForRow = (index: number) =>
|
|
314
|
+
margin.top + ((index + 0.5) * plotHeight) / Math.max(1, chart.data.length);
|
|
315
|
+
const axisLabelMinSpacing = isHorizontalBar
|
|
316
|
+
? valueTickLabelWidth + 8
|
|
317
|
+
: labelFontSize + 4;
|
|
318
|
+
const visibleAxisLabelCount = Math.max(
|
|
319
|
+
1,
|
|
320
|
+
Math.floor(
|
|
321
|
+
(isHorizontalBar ? plotWidth : plotHeight) / axisLabelMinSpacing,
|
|
322
|
+
) + 1,
|
|
323
|
+
);
|
|
324
|
+
const axisLabelStride = Math.max(
|
|
325
|
+
1,
|
|
326
|
+
Math.ceil(
|
|
327
|
+
(gridTickValues.length - 1) / Math.max(1, visibleAxisLabelCount - 1),
|
|
328
|
+
),
|
|
329
|
+
);
|
|
330
|
+
const axisLabelIndexes = new Set(
|
|
331
|
+
gridTickValues.flatMap((_, index) =>
|
|
332
|
+
index % axisLabelStride === 0 &&
|
|
333
|
+
(visibleAxisLabelCount > 1 || index === 0)
|
|
334
|
+
? [index]
|
|
335
|
+
: [],
|
|
336
|
+
),
|
|
337
|
+
);
|
|
338
|
+
const finalAxisLabelIndex = gridTickValues.length - 1;
|
|
339
|
+
if (
|
|
340
|
+
visibleAxisLabelCount > 1 &&
|
|
341
|
+
finalAxisLabelIndex >= 0 &&
|
|
342
|
+
!axisLabelIndexes.has(finalAxisLabelIndex)
|
|
343
|
+
) {
|
|
344
|
+
if (isHorizontalBar) {
|
|
345
|
+
axisLabelIndexes.add(finalAxisLabelIndex);
|
|
346
|
+
} else {
|
|
347
|
+
const precedingAxisLabelIndex =
|
|
348
|
+
Math.floor(finalAxisLabelIndex / axisLabelStride) * axisLabelStride;
|
|
349
|
+
const tickSpacing = plotHeight / Math.max(1, finalAxisLabelIndex);
|
|
350
|
+
const finalLabelSpacing =
|
|
351
|
+
(finalAxisLabelIndex - precedingAxisLabelIndex) * tickSpacing;
|
|
352
|
+
if (finalLabelSpacing < axisLabelMinSpacing) {
|
|
353
|
+
axisLabelIndexes.delete(precedingAxisLabelIndex);
|
|
354
|
+
}
|
|
355
|
+
axisLabelIndexes.add(finalAxisLabelIndex);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
const axisLabelPosition = (index: number) =>
|
|
359
|
+
isHorizontalBar
|
|
360
|
+
? xForValue(gridTickValues[index]!)
|
|
361
|
+
: yForValue(gridTickValues[index]!);
|
|
362
|
+
while (true) {
|
|
363
|
+
const selectedAxisLabelIndexes = [...axisLabelIndexes].sort(
|
|
364
|
+
(first, second) => first - second,
|
|
365
|
+
);
|
|
366
|
+
const crowdedPair = selectedAxisLabelIndexes.findIndex(
|
|
367
|
+
(currentIndex, position) => {
|
|
368
|
+
if (position === 0) return false;
|
|
369
|
+
const precedingIndex = selectedAxisLabelIndexes[position - 1]!;
|
|
370
|
+
const centerSpacing = Math.abs(
|
|
371
|
+
axisLabelPosition(currentIndex) - axisLabelPosition(precedingIndex),
|
|
372
|
+
);
|
|
373
|
+
const requiredSpacing = isHorizontalBar
|
|
374
|
+
? (estimateTextWidth(valueTickLabels[currentIndex]!, labelFontSize) +
|
|
375
|
+
estimateTextWidth(
|
|
376
|
+
valueTickLabels[precedingIndex]!,
|
|
377
|
+
labelFontSize,
|
|
378
|
+
)) /
|
|
379
|
+
2 +
|
|
380
|
+
8
|
|
381
|
+
: labelFontSize + 4;
|
|
382
|
+
return centerSpacing < requiredSpacing;
|
|
383
|
+
},
|
|
384
|
+
);
|
|
385
|
+
if (crowdedPair < 0) break;
|
|
386
|
+
const precedingIndex = selectedAxisLabelIndexes[crowdedPair - 1]!;
|
|
387
|
+
const currentIndex = selectedAxisLabelIndexes[crowdedPair]!;
|
|
388
|
+
axisLabelIndexes.delete(
|
|
389
|
+
currentIndex === finalAxisLabelIndex ? precedingIndex : currentIndex,
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
const gridTicks = gridTickValues.map((value, index) => ({
|
|
393
|
+
value,
|
|
394
|
+
index,
|
|
395
|
+
x: xForValue(value),
|
|
396
|
+
y: yForValue(value),
|
|
397
|
+
}));
|
|
76
398
|
const xTickEvery = Math.max(1, Math.ceil(chart.data.length / 6));
|
|
77
|
-
const
|
|
399
|
+
const zero = Math.max(min, Math.min(max, 0));
|
|
400
|
+
const zeroX = xForValue(zero);
|
|
401
|
+
const zeroY = yForValue(zero);
|
|
402
|
+
const axisTitleFontWeight = chart.axisTitleFontWeight ?? 500;
|
|
403
|
+
const valueLabelFontSize = chart.valueLabelFontSize ?? 11;
|
|
404
|
+
const valueLabelFontWeight = chart.valueLabelFontWeight ?? 400;
|
|
78
405
|
|
|
79
406
|
const axes = (
|
|
80
407
|
<g aria-hidden="true">
|
|
81
|
-
{gridTicks.map((tick) =>
|
|
82
|
-
|
|
83
|
-
<
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
408
|
+
{gridTicks.map((tick) =>
|
|
409
|
+
isHorizontalBar ? (
|
|
410
|
+
<g key={tick.x}>
|
|
411
|
+
<line
|
|
412
|
+
x1={tick.x}
|
|
413
|
+
x2={tick.x}
|
|
414
|
+
y1={margin.top}
|
|
415
|
+
y2={HEIGHT - margin.bottom}
|
|
416
|
+
stroke="hsl(var(--chart-grid, var(--border)))"
|
|
417
|
+
strokeDasharray={tickInterval === undefined ? "3 3" : undefined}
|
|
418
|
+
/>
|
|
419
|
+
{axisLabelIndexes.has(tick.index) ? (
|
|
420
|
+
<text
|
|
421
|
+
x={tick.x}
|
|
422
|
+
y={HEIGHT - margin.bottom + 14}
|
|
423
|
+
fill="hsl(var(--muted-foreground))"
|
|
424
|
+
fontSize={labelFontSize}
|
|
425
|
+
textAnchor="middle"
|
|
426
|
+
>
|
|
427
|
+
{valueTickLabels[tick.index]}
|
|
428
|
+
</text>
|
|
429
|
+
) : null}
|
|
430
|
+
</g>
|
|
431
|
+
) : (
|
|
432
|
+
<g key={tick.y}>
|
|
433
|
+
<line
|
|
434
|
+
x1={margin.left}
|
|
435
|
+
x2={WIDTH - margin.right}
|
|
436
|
+
y1={tick.y}
|
|
437
|
+
y2={tick.y}
|
|
438
|
+
stroke="hsl(var(--chart-grid, var(--border)))"
|
|
439
|
+
strokeDasharray={tickInterval === undefined ? "3 3" : undefined}
|
|
440
|
+
/>
|
|
441
|
+
{axisLabelIndexes.has(tick.index) ? (
|
|
442
|
+
<text
|
|
443
|
+
x={margin.left - VALUE_AXIS_TICK_LABEL_INSET}
|
|
444
|
+
y={tick.y + 4}
|
|
445
|
+
fill="hsl(var(--muted-foreground))"
|
|
446
|
+
fontSize={labelFontSize}
|
|
447
|
+
textAnchor="end"
|
|
448
|
+
>
|
|
449
|
+
{valueTickLabels[tick.index]}
|
|
450
|
+
</text>
|
|
451
|
+
) : null}
|
|
452
|
+
</g>
|
|
453
|
+
),
|
|
454
|
+
)}
|
|
455
|
+
<line
|
|
456
|
+
x1={isHorizontalBar ? zeroX : margin.left}
|
|
457
|
+
x2={isHorizontalBar ? zeroX : WIDTH - margin.right}
|
|
458
|
+
y1={isHorizontalBar ? margin.top : zeroY}
|
|
459
|
+
y2={isHorizontalBar ? HEIGHT - margin.bottom : zeroY}
|
|
460
|
+
stroke="hsl(var(--chart-grid, var(--border)))"
|
|
461
|
+
/>
|
|
462
|
+
{chart.data.map((row, index) =>
|
|
463
|
+
isHorizontalBar ? (
|
|
91
464
|
<text
|
|
92
|
-
|
|
93
|
-
|
|
465
|
+
key={`${index}-${String(row[chart.xKey] ?? "")}`}
|
|
466
|
+
x={categoryLabelX}
|
|
467
|
+
y={yForRow(index) + labelFontSize * 0.35}
|
|
94
468
|
fill="hsl(var(--muted-foreground))"
|
|
95
|
-
fontSize=
|
|
469
|
+
fontSize={labelFontSize}
|
|
96
470
|
textAnchor="end"
|
|
97
471
|
>
|
|
98
|
-
{
|
|
472
|
+
{labelFor(row[chart.xKey])}
|
|
99
473
|
</text>
|
|
100
|
-
|
|
101
|
-
))}
|
|
102
|
-
<line
|
|
103
|
-
x1={MARGIN.left}
|
|
104
|
-
x2={WIDTH - MARGIN.right}
|
|
105
|
-
y1={zeroY}
|
|
106
|
-
y2={zeroY}
|
|
107
|
-
stroke="hsl(var(--border))"
|
|
108
|
-
/>
|
|
109
|
-
{chart.data.map((row, index) =>
|
|
110
|
-
index % xTickEvery === 0 || index === chart.data.length - 1 ? (
|
|
474
|
+
) : index % xTickEvery === 0 || index === chart.data.length - 1 ? (
|
|
111
475
|
<text
|
|
112
476
|
key={`${index}-${String(row[chart.xKey] ?? "")}`}
|
|
113
477
|
x={
|
|
114
478
|
chart.type === "bar"
|
|
115
|
-
?
|
|
479
|
+
? margin.left + (index + 0.5) * (plotWidth / chart.data.length)
|
|
116
480
|
: xForPoint(index)
|
|
117
481
|
}
|
|
118
482
|
y={HEIGHT - 12}
|
|
119
483
|
fill="hsl(var(--muted-foreground))"
|
|
120
|
-
fontSize=
|
|
484
|
+
fontSize={labelFontSize}
|
|
121
485
|
textAnchor="middle"
|
|
122
486
|
>
|
|
123
487
|
{labelFor(row[chart.xKey])}
|
|
124
488
|
</text>
|
|
125
489
|
) : null,
|
|
126
490
|
)}
|
|
491
|
+
{chart.xAxisTitle ? (
|
|
492
|
+
<text
|
|
493
|
+
x={margin.left + plotWidth / 2}
|
|
494
|
+
y={HEIGHT - 4}
|
|
495
|
+
fill="hsl(var(--foreground))"
|
|
496
|
+
fontSize={axisTitleFontSize}
|
|
497
|
+
fontWeight={axisTitleFontWeight}
|
|
498
|
+
textAnchor="middle"
|
|
499
|
+
>
|
|
500
|
+
{chart.xAxisTitle}
|
|
501
|
+
</text>
|
|
502
|
+
) : null}
|
|
503
|
+
{chart.yAxisTitle ? (
|
|
504
|
+
<text
|
|
505
|
+
x={Y_AXIS_TITLE_X}
|
|
506
|
+
y={margin.top + plotHeight / 2}
|
|
507
|
+
transform={`rotate(-90 ${Y_AXIS_TITLE_X} ${margin.top + plotHeight / 2})`}
|
|
508
|
+
fill="hsl(var(--foreground))"
|
|
509
|
+
fontSize={axisTitleFontSize}
|
|
510
|
+
fontWeight={axisTitleFontWeight}
|
|
511
|
+
textAnchor="middle"
|
|
512
|
+
>
|
|
513
|
+
{chart.yAxisTitle}
|
|
514
|
+
</text>
|
|
515
|
+
) : null}
|
|
127
516
|
</g>
|
|
128
517
|
);
|
|
129
518
|
|
|
130
519
|
if (chart.type === "bar") {
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
520
|
+
const slotSize =
|
|
521
|
+
(isHorizontalBar ? plotHeight : plotWidth) /
|
|
522
|
+
Math.max(1, chart.data.length);
|
|
523
|
+
const groupSize =
|
|
524
|
+
slotSize *
|
|
525
|
+
(isHorizontalBar && categoryGapRatio !== undefined
|
|
526
|
+
? 1 - categoryGapRatio
|
|
527
|
+
: 0.72);
|
|
528
|
+
const barSize = groupSize / Math.max(1, chart.series.length);
|
|
134
529
|
return (
|
|
135
530
|
<>
|
|
136
531
|
{axes}
|
|
@@ -141,23 +536,65 @@ function ChartBody({ chart }: { chart: DataChartWidgetData }) {
|
|
|
141
536
|
const color =
|
|
142
537
|
series.color ??
|
|
143
538
|
DEFAULT_COLORS[seriesIndex % DEFAULT_COLORS.length];
|
|
144
|
-
const
|
|
145
|
-
const
|
|
539
|
+
const valueX = xForValue(value);
|
|
540
|
+
const valueY = yForValue(value);
|
|
541
|
+
const x = isHorizontalBar
|
|
542
|
+
? Math.min(zeroX, valueX)
|
|
543
|
+
: margin.left +
|
|
544
|
+
rowIndex * slotSize +
|
|
545
|
+
(slotSize - groupSize) / 2 +
|
|
546
|
+
seriesIndex * barSize;
|
|
547
|
+
const y = isHorizontalBar
|
|
548
|
+
? margin.top +
|
|
549
|
+
rowIndex * slotSize +
|
|
550
|
+
(slotSize - groupSize) / 2 +
|
|
551
|
+
seriesIndex * barSize
|
|
552
|
+
: yForValue(Math.max(value, 0));
|
|
553
|
+
const width = isHorizontalBar
|
|
554
|
+
? Math.max(2, Math.abs(valueX - zeroX))
|
|
555
|
+
: Math.max(2, barSize - 2);
|
|
556
|
+
const height = isHorizontalBar
|
|
557
|
+
? Math.max(0, barSize - Math.min(2, barSize * 0.1))
|
|
558
|
+
: Math.max(1, Math.abs(zeroY - valueY));
|
|
146
559
|
return (
|
|
147
|
-
<
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
560
|
+
<g key={`${rowIndex}-${series.key}`}>
|
|
561
|
+
<rect
|
|
562
|
+
x={x}
|
|
563
|
+
y={y}
|
|
564
|
+
width={width}
|
|
565
|
+
height={height}
|
|
566
|
+
rx="3"
|
|
567
|
+
fill={color}
|
|
568
|
+
/>
|
|
569
|
+
{chart.showValueLabels ? (
|
|
570
|
+
<text
|
|
571
|
+
x={
|
|
572
|
+
isHorizontalBar
|
|
573
|
+
? valueX + (value < 0 ? -5 : 5)
|
|
574
|
+
: x + width / 2
|
|
575
|
+
}
|
|
576
|
+
y={
|
|
577
|
+
isHorizontalBar
|
|
578
|
+
? y + height / 2 + valueLabelFontSize * 0.35
|
|
579
|
+
: y - 5
|
|
580
|
+
}
|
|
581
|
+
fill="hsl(var(--muted-foreground))"
|
|
582
|
+
fontSize={valueLabelFontSize}
|
|
583
|
+
fontWeight={valueLabelFontWeight}
|
|
584
|
+
textAnchor={
|
|
585
|
+
isHorizontalBar ? (value < 0 ? "end" : "start") : "middle"
|
|
586
|
+
}
|
|
587
|
+
>
|
|
588
|
+
{isHorizontalBar
|
|
589
|
+
? formatHorizontalValueLabel(
|
|
590
|
+
value,
|
|
591
|
+
valueLabelFontSize,
|
|
592
|
+
horizontalTextWidthLimit,
|
|
593
|
+
)
|
|
594
|
+
: formatTick(value)}
|
|
595
|
+
</text>
|
|
596
|
+
) : null}
|
|
597
|
+
</g>
|
|
161
598
|
);
|
|
162
599
|
}),
|
|
163
600
|
)}
|
|
@@ -171,9 +608,12 @@ function ChartBody({ chart }: { chart: DataChartWidgetData }) {
|
|
|
171
608
|
const value = numericValue(row, series.key);
|
|
172
609
|
return value === null
|
|
173
610
|
? null
|
|
174
|
-
: { x: xForPoint(rowIndex), y:
|
|
611
|
+
: { x: xForPoint(rowIndex), y: yForValue(value), value };
|
|
175
612
|
})
|
|
176
|
-
.filter(
|
|
613
|
+
.filter(
|
|
614
|
+
(point): point is { x: number; y: number; value: number } =>
|
|
615
|
+
point !== null,
|
|
616
|
+
);
|
|
177
617
|
const color =
|
|
178
618
|
series.color ?? DEFAULT_COLORS[seriesIndex % DEFAULT_COLORS.length];
|
|
179
619
|
const linePath = points
|
|
@@ -221,6 +661,35 @@ function ChartBody({ chart }: { chart: DataChartWidgetData }) {
|
|
|
221
661
|
strokeWidth="2"
|
|
222
662
|
/>
|
|
223
663
|
))}
|
|
664
|
+
{chart.showValueLabels
|
|
665
|
+
? points.map((point, index) => {
|
|
666
|
+
const abovePoint =
|
|
667
|
+
point.y - valueLabelFontSize - 5 >= margin.top;
|
|
668
|
+
return (
|
|
669
|
+
<text
|
|
670
|
+
key={`value-${index}`}
|
|
671
|
+
x={point.x}
|
|
672
|
+
y={
|
|
673
|
+
abovePoint
|
|
674
|
+
? point.y - 5
|
|
675
|
+
: point.y + valueLabelFontSize + 5
|
|
676
|
+
}
|
|
677
|
+
fill="hsl(var(--muted-foreground))"
|
|
678
|
+
fontSize={valueLabelFontSize}
|
|
679
|
+
fontWeight={valueLabelFontWeight}
|
|
680
|
+
textAnchor={
|
|
681
|
+
point.x <= margin.left
|
|
682
|
+
? "start"
|
|
683
|
+
: point.x >= WIDTH - margin.right
|
|
684
|
+
? "end"
|
|
685
|
+
: "middle"
|
|
686
|
+
}
|
|
687
|
+
>
|
|
688
|
+
{formatTick(point.value)}
|
|
689
|
+
</text>
|
|
690
|
+
);
|
|
691
|
+
})
|
|
692
|
+
: null}
|
|
224
693
|
</g>
|
|
225
694
|
))}
|
|
226
695
|
</>
|
|
@@ -229,16 +698,42 @@ function ChartBody({ chart }: { chart: DataChartWidgetData }) {
|
|
|
229
698
|
|
|
230
699
|
export function DataChartRenderer({ chart }: { chart: DataChartWidgetData }) {
|
|
231
700
|
const t = useT();
|
|
701
|
+
const renderedChart =
|
|
702
|
+
chart.data.length > DATA_WIDGET_MAX_CHART_POINTS
|
|
703
|
+
? {
|
|
704
|
+
...chart,
|
|
705
|
+
data: chart.data.slice(0, DATA_WIDGET_MAX_CHART_POINTS),
|
|
706
|
+
}
|
|
707
|
+
: chart;
|
|
708
|
+
const contentHeight = renderedChart.contentHeight;
|
|
709
|
+
const contentPadding = renderedChart.contentPadding ?? 0;
|
|
710
|
+
const hasContentBox = contentHeight !== undefined || contentPadding > 0;
|
|
711
|
+
const labelFontFamily =
|
|
712
|
+
LABEL_FONT_FAMILIES[renderedChart.labelFontFamily ?? "sans"];
|
|
713
|
+
const labelFontSize = renderedChart.labelFontSize ?? 11;
|
|
232
714
|
return (
|
|
233
|
-
<div
|
|
715
|
+
<div
|
|
716
|
+
className="box-border w-full min-w-0 overflow-hidden"
|
|
717
|
+
style={
|
|
718
|
+
hasContentBox
|
|
719
|
+
? {
|
|
720
|
+
...(contentHeight !== undefined
|
|
721
|
+
? { height: `${contentHeight}px` }
|
|
722
|
+
: {}),
|
|
723
|
+
padding: `${contentPadding}px`,
|
|
724
|
+
}
|
|
725
|
+
: undefined
|
|
726
|
+
}
|
|
727
|
+
>
|
|
234
728
|
<svg
|
|
235
|
-
className=
|
|
729
|
+
className={`agent-data-chart-renderer block w-full ${contentHeight !== undefined ? "h-full" : "h-auto"}`}
|
|
236
730
|
viewBox={`0 0 ${WIDTH} ${HEIGHT}`}
|
|
731
|
+
preserveAspectRatio={contentHeight !== undefined ? "none" : undefined}
|
|
732
|
+
fontFamily={labelFontFamily}
|
|
237
733
|
role="img"
|
|
238
|
-
aria-label={
|
|
239
|
-
preserveAspectRatio="none"
|
|
734
|
+
aria-label={renderedChart.title ?? t("agentChat.widget.dataChart")}
|
|
240
735
|
>
|
|
241
|
-
<ChartBody chart={
|
|
736
|
+
<ChartBody chart={renderedChart} labelFontSize={labelFontSize} />
|
|
242
737
|
</svg>
|
|
243
738
|
</div>
|
|
244
739
|
);
|