@opencx/widget-react 5.4.0 → 5.4.1
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/{Chart.impl-C6jESAyh.js → Chart.impl-DHL9WtSk.js} +2 -2
- package/dist/{Chart.impl-C6jESAyh.js.map → Chart.impl-DHL9WtSk.js.map} +1 -1
- package/dist/{Chart.impl-BLTobbyF.cjs → Chart.impl-Ds2iAr8P.cjs} +2 -2
- package/dist/{Chart.impl-BLTobbyF.cjs.map → Chart.impl-Ds2iAr8P.cjs.map} +1 -1
- package/dist/{index-DFDJp9mO.cjs → index-D-LXlHO4.cjs} +3 -3
- package/dist/{index-DFDJp9mO.cjs.map → index-D-LXlHO4.cjs.map} +1 -1
- package/dist/{index-DBNYdRSl.js → index-uTBDmBjX.js} +3 -3
- package/dist/{index-DBNYdRSl.js.map → index-uTBDmBjX.js.map} +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/package.json.d.ts +1 -1
- package/package.json +3 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as n, jsxs as d } from "react/jsx-runtime";
|
|
2
2
|
import f from "react";
|
|
3
3
|
import { PieChart as T, Tooltip as h, Legend as C, Pie as S, Cell as b, Label as E, LineChart as I, CartesianGrid as p, XAxis as y, Line as W, BarChart as k, Bar as R, YAxis as v } from "recharts";
|
|
4
|
-
import { u as O } from "./index-
|
|
4
|
+
import { u as O } from "./index-uTBDmBjX.js";
|
|
5
5
|
const K = 2, N = 0.62, Y = ".recharts-yAxis .recharts-cartesian-axis-tick-value";
|
|
6
6
|
function z(t, e) {
|
|
7
7
|
return t.length * e * N;
|
|
@@ -204,4 +204,4 @@ function F({ height: t }) {
|
|
|
204
204
|
export {
|
|
205
205
|
B as default
|
|
206
206
|
};
|
|
207
|
-
//# sourceMappingURL=Chart.impl-
|
|
207
|
+
//# sourceMappingURL=Chart.impl-DHL9WtSk.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Chart.impl-C6jESAyh.js","sources":["../src/json-render/chart-axis.ts","../src/json-render/Chart.impl.tsx"],"sourcesContent":["/**\n * Y-axis sizing for the cartesian charts.\n *\n * recharts 2.x has no auto-width axis: `YAxis.width` is a fixed number, and a\n * tick label wider than the room left of its tick pokes out past the svg's\n * left edge, where the viewport clips it. That is how a 120/80/150/90/200 bar\n * chart rendered \"0\" for every tick in the 400px chat panel — only the last\n * digit of each label survived. The axis is sized from the labels recharts\n * actually painted instead: measured in a browser, estimated where the text\n * cannot be measured (jsdom, a hidden embed).\n */\n\n/** Slack past the widest label so hinting never shaves its first glyph. */\nconst Y_TICK_LABEL_PAD = 2;\n\n/**\n * Fallback glyph advance as a fraction of the font size, used when the text\n * cannot be measured. Sized for digits in UI sans fonts, which run 0.55–0.65em;\n * erring wide costs a couple of px of plot, erring narrow clips.\n */\nconst ESTIMATED_GLYPH_ADVANCE = 0.62;\n\n/** recharts' painted Y tick labels (`<text>`, right-anchored at their `x`). */\nexport const Y_TICK_LABEL_SELECTOR =\n '.recharts-yAxis .recharts-cartesian-axis-tick-value';\n\nexport function estimateTextWidth(text: string, fontSize: number): number {\n return text.length * fontSize * ESTIMATED_GLYPH_ADVANCE;\n}\n\ntype MeasurableText = Element & { getComputedTextLength: () => number };\n\n/** SVG text content elements in a browser; jsdom's `<text>` has no such method. */\nfunction isMeasurableText(node: Element): node is MeasurableText {\n return (\n 'getComputedTextLength' in node &&\n typeof node.getComputedTextLength === 'function'\n );\n}\n\n/**\n * The painted width of an svg text node: the browser's glyph run when it can\n * report one (a non-positive length means not laid out — a hidden embed), the\n * estimate otherwise.\n */\nexport function measureSvgTextWidth(node: Element, fontSize: number): number {\n if (isMeasurableText(node)) {\n const measured = node.getComputedTextLength();\n if (measured > 0) return measured;\n }\n return estimateTextWidth(node.textContent ?? '', fontSize);\n}\n\n/**\n * The narrowest `YAxis.width` that keeps every painted tick label inside the\n * svg, or `null` when no Y ticks are in the DOM (a pie, or nothing painted\n * yet). Derived from geometry, not from recharts constants: each label's\n * right edge sits at its tick's `x`, and `currentWidth - x` is whatever gap\n * the axis reserves between label and plot — so the requirement is the widest\n * label plus that gap, whichever tick size/margin recharts applied.\n */\nexport function requiredYAxisWidth(\n container: ParentNode,\n currentWidth: number,\n fontSize: number,\n): number | null {\n let widest = 0;\n let anchorX: number | null = null;\n container.querySelectorAll(Y_TICK_LABEL_SELECTOR).forEach((tick) => {\n const rawX = tick.getAttribute('x');\n if (rawX === null) return;\n const x = Number(rawX);\n if (!Number.isFinite(x)) return;\n anchorX = x;\n widest = Math.max(widest, measureSvgTextWidth(tick, fontSize));\n });\n if (anchorX === null) return null;\n return Math.ceil(widest + (currentWidth - anchorX) + Y_TICK_LABEL_PAD);\n}\n","import React from 'react';\nimport {\n Bar,\n BarChart,\n CartesianGrid,\n Cell,\n Label,\n Legend,\n Line,\n LineChart,\n Pie,\n PieChart,\n Tooltip,\n XAxis,\n YAxis,\n} from 'recharts';\nimport type { ChartProps } from './props';\nimport { requiredYAxisWidth } from './chart-axis';\nimport { useJsonRenderHost } from './host';\n\n/**\n * Measure the wrapper width ourselves instead of relying on recharts'\n * `ResponsiveContainer`. On recharts 2.12 that container renders an empty,\n * svg-less box whenever it mounts measuring 0 — which is exactly this case: the\n * chart is a `React.lazy` chunk that mounts after the widget popover has laid\n * out, and it never re-measures on its own. Feeding recharts an explicit numeric\n * width sidesteps the trap; the `ResizeObserver` keeps it in sync when the embed\n * is resized.\n */\nfunction useContainerWidth(): {\n ref: React.RefObject<HTMLDivElement | null>;\n width: number;\n} {\n const ref = React.useRef<HTMLDivElement>(null);\n const [width, setWidth] = React.useState(0);\n React.useLayoutEffect(() => {\n const el = ref.current;\n if (!el) return;\n const update = () => setWidth(el.clientWidth);\n update();\n const observer = new ResizeObserver(update);\n observer.observe(el);\n return () => observer.disconnect();\n }, []);\n return { ref, width };\n}\n\nconst Y_TICK_FONT_SIZE = 11;\n/** First-paint axis width; the measured width replaces it before paint. */\nconst Y_AXIS_INITIAL_WIDTH = 32;\n\n/**\n * Size the Y axis from the tick labels recharts painted (see `chart-axis.ts`):\n * a fixed `YAxis.width` clips any label wider than it, and there is no\n * auto-width axis in recharts 2.x. Re-measured whenever the painted labels can\n * change — the data, the chart type, the height (tick count) or the container\n * width — in a layout effect so the corrected axis paints in the same frame.\n * Converges in one pass: the Y ticks do not depend on the axis width, so the\n * re-render after `setWidth` measures the same labels and settles.\n */\nfunction useYAxisWidth(\n ref: React.RefObject<HTMLDivElement | null>,\n {\n type,\n data,\n height,\n containerWidth,\n }: Pick<ChartProps, 'type' | 'data'> & {\n height: number;\n containerWidth: number;\n },\n): number {\n const [width, setWidth] = React.useState(Y_AXIS_INITIAL_WIDTH);\n React.useLayoutEffect(() => {\n const el = ref.current;\n if (!el) return;\n const needed = requiredYAxisWidth(el, width, Y_TICK_FONT_SIZE);\n if (needed !== null && needed !== width) setWidth(needed);\n }, [ref, width, type, data, height, containerWidth]);\n return width;\n}\n\n/**\n * The actual chart renderer. Loaded lazily (see `Chart.tsx`) so recharts — the\n * one heavy dependency in the registry — is a separate chunk that only downloads\n * when the agent actually emits a Chart.\n *\n * Styled to match the companion/Impact-Dashboard charts: a monochrome ramp of\n * the theme foreground (90/70/50/30% alpha), donut pie with an optional center\n * label + legend, and clean axes (no tick/axis lines). The ramp reads the\n * widget's `--opencx-foreground` token so charts follow the embed theme, with\n * a neutral fallback for any context where the token is missing.\n */\n\nconst FOREGROUND = 'hsl(var(--opencx-foreground, 240 10% 20%))';\n// Same ramp as the dashboard's REPORTS_COLOR (surface-foreground at 90/70/50/30%).\nconst RAMP = [90, 70, 50, 30].map(\n (pct) => `color-mix(in oklch, ${FOREGROUND} ${pct}%, transparent)`,\n);\nconst rampColor = (i: number): string => RAMP[i % RAMP.length] ?? FOREGROUND;\n\nconst TOOLTIP_STYLE: React.CSSProperties = {\n borderRadius: 8,\n border: '1px solid color-mix(in oklch, currentColor 15%, transparent)',\n fontSize: 12,\n};\n\nexport default function ChartImpl({\n type,\n data,\n height,\n centerLabel,\n}: ChartProps) {\n const h = height ?? 220;\n const { ref, width } = useContainerWidth();\n const yAxisWidth = useYAxisWidth(ref, {\n type,\n data,\n height: h,\n containerWidth: width,\n });\n\n if (data.length === 0) {\n return <ChartEmpty height={h} />;\n }\n\n // Shared by the cartesian charts: the axis is exactly as wide as its labels,\n // so the plot keeps every px it can on small numbers and 7-digit or\n // currency-formatted ticks never lose their leading digits.\n const yAxis = (\n <YAxis\n tickLine={false}\n axisLine={false}\n fontSize={Y_TICK_FONT_SIZE}\n width={yAxisWidth}\n />\n );\n\n return (\n <div ref={ref} className=\"w-full text-foreground\" style={{ height: h }}>\n {width > 0 &&\n (type === 'pie' ? (\n <PieChart width={width} height={h}>\n <Tooltip contentStyle={TOOLTIP_STYLE} />\n <Legend\n verticalAlign=\"bottom\"\n iconType=\"circle\"\n iconSize={8}\n wrapperStyle={{ fontSize: 12 }}\n />\n <Pie\n data={data}\n dataKey=\"value\"\n nameKey=\"label\"\n outerRadius=\"85%\"\n innerRadius=\"60%\"\n >\n {data.map((_, i) => (\n <Cell key={i} fill={rampColor(i)} />\n ))}\n {centerLabel && (\n <Label\n position=\"center\"\n content={({ viewBox }) => {\n if (viewBox && 'cx' in viewBox && 'cy' in viewBox) {\n return (\n <text\n x={viewBox.cx}\n y={viewBox.cy}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n className=\"fill-foreground text-2xl font-medium\"\n >\n {centerLabel}\n </text>\n );\n }\n }}\n />\n )}\n </Pie>\n </PieChart>\n ) : type === 'line' ? (\n <LineChart\n width={width}\n height={h}\n data={data}\n margin={{ top: 8, right: 8, bottom: 0, left: 0 }}\n >\n <CartesianGrid strokeOpacity={0.15} vertical={false} />\n <XAxis\n dataKey=\"label\"\n tickLine={false}\n axisLine={false}\n tickMargin={10}\n fontSize={11}\n />\n {yAxis}\n <Tooltip contentStyle={TOOLTIP_STYLE} />\n <Line\n type=\"monotone\"\n dataKey=\"value\"\n stroke={rampColor(0)}\n strokeWidth={2}\n dot={false}\n />\n </LineChart>\n ) : (\n <BarChart\n width={width}\n height={h}\n data={data}\n margin={{ top: 8, right: 8, bottom: 0, left: 0 }}\n >\n <CartesianGrid strokeOpacity={0.15} vertical={false} />\n <XAxis\n dataKey=\"label\"\n tickLine={false}\n axisLine={false}\n tickMargin={10}\n fontSize={11}\n />\n {yAxis}\n <Tooltip\n contentStyle={TOOLTIP_STYLE}\n cursor={{ fillOpacity: 0.06 }}\n />\n <Bar dataKey=\"value\" fill={rampColor(0)} radius={[4, 4, 0, 0]} />\n </BarChart>\n ))}\n </div>\n );\n}\n\nfunction ChartEmpty({ height }: { height: number }) {\n const { t } = useJsonRenderHost();\n return (\n <div\n className=\"flex w-full items-center justify-center rounded-lg border border-muted-foreground/15 text-xs text-muted-foreground\"\n style={{ height }}\n >\n {t('json_no_chart_data')}\n </div>\n );\n}\n"],"names":["Y_TICK_LABEL_PAD","ESTIMATED_GLYPH_ADVANCE","Y_TICK_LABEL_SELECTOR","estimateTextWidth","text","fontSize","isMeasurableText","node","measureSvgTextWidth","measured","requiredYAxisWidth","container","currentWidth","widest","anchorX","tick","rawX","x","useContainerWidth","ref","React","width","setWidth","el","update","observer","Y_TICK_FONT_SIZE","Y_AXIS_INITIAL_WIDTH","useYAxisWidth","type","data","height","containerWidth","needed","FOREGROUND","RAMP","pct","rampColor","i","TOOLTIP_STYLE","ChartImpl","centerLabel","h","yAxisWidth","jsx","ChartEmpty","yAxis","YAxis","PieChart","Tooltip","Legend","jsxs","Pie","_","Cell","Label","viewBox","LineChart","CartesianGrid","XAxis","Line","BarChart","Bar","t","useJsonRenderHost"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Chart.impl-DHL9WtSk.js","sources":["../src/json-render/chart-axis.ts","../src/json-render/Chart.impl.tsx"],"sourcesContent":["/**\n * Y-axis sizing for the cartesian charts.\n *\n * recharts 2.x has no auto-width axis: `YAxis.width` is a fixed number, and a\n * tick label wider than the room left of its tick pokes out past the svg's\n * left edge, where the viewport clips it. That is how a 120/80/150/90/200 bar\n * chart rendered \"0\" for every tick in the 400px chat panel — only the last\n * digit of each label survived. The axis is sized from the labels recharts\n * actually painted instead: measured in a browser, estimated where the text\n * cannot be measured (jsdom, a hidden embed).\n */\n\n/** Slack past the widest label so hinting never shaves its first glyph. */\nconst Y_TICK_LABEL_PAD = 2;\n\n/**\n * Fallback glyph advance as a fraction of the font size, used when the text\n * cannot be measured. Sized for digits in UI sans fonts, which run 0.55–0.65em;\n * erring wide costs a couple of px of plot, erring narrow clips.\n */\nconst ESTIMATED_GLYPH_ADVANCE = 0.62;\n\n/** recharts' painted Y tick labels (`<text>`, right-anchored at their `x`). */\nexport const Y_TICK_LABEL_SELECTOR =\n '.recharts-yAxis .recharts-cartesian-axis-tick-value';\n\nexport function estimateTextWidth(text: string, fontSize: number): number {\n return text.length * fontSize * ESTIMATED_GLYPH_ADVANCE;\n}\n\ntype MeasurableText = Element & { getComputedTextLength: () => number };\n\n/** SVG text content elements in a browser; jsdom's `<text>` has no such method. */\nfunction isMeasurableText(node: Element): node is MeasurableText {\n return (\n 'getComputedTextLength' in node &&\n typeof node.getComputedTextLength === 'function'\n );\n}\n\n/**\n * The painted width of an svg text node: the browser's glyph run when it can\n * report one (a non-positive length means not laid out — a hidden embed), the\n * estimate otherwise.\n */\nexport function measureSvgTextWidth(node: Element, fontSize: number): number {\n if (isMeasurableText(node)) {\n const measured = node.getComputedTextLength();\n if (measured > 0) return measured;\n }\n return estimateTextWidth(node.textContent ?? '', fontSize);\n}\n\n/**\n * The narrowest `YAxis.width` that keeps every painted tick label inside the\n * svg, or `null` when no Y ticks are in the DOM (a pie, or nothing painted\n * yet). Derived from geometry, not from recharts constants: each label's\n * right edge sits at its tick's `x`, and `currentWidth - x` is whatever gap\n * the axis reserves between label and plot — so the requirement is the widest\n * label plus that gap, whichever tick size/margin recharts applied.\n */\nexport function requiredYAxisWidth(\n container: ParentNode,\n currentWidth: number,\n fontSize: number,\n): number | null {\n let widest = 0;\n let anchorX: number | null = null;\n container.querySelectorAll(Y_TICK_LABEL_SELECTOR).forEach((tick) => {\n const rawX = tick.getAttribute('x');\n if (rawX === null) return;\n const x = Number(rawX);\n if (!Number.isFinite(x)) return;\n anchorX = x;\n widest = Math.max(widest, measureSvgTextWidth(tick, fontSize));\n });\n if (anchorX === null) return null;\n return Math.ceil(widest + (currentWidth - anchorX) + Y_TICK_LABEL_PAD);\n}\n","import React from 'react';\nimport {\n Bar,\n BarChart,\n CartesianGrid,\n Cell,\n Label,\n Legend,\n Line,\n LineChart,\n Pie,\n PieChart,\n Tooltip,\n XAxis,\n YAxis,\n} from 'recharts';\nimport type { ChartProps } from './props';\nimport { requiredYAxisWidth } from './chart-axis';\nimport { useJsonRenderHost } from './host';\n\n/**\n * Measure the wrapper width ourselves instead of relying on recharts'\n * `ResponsiveContainer`. On recharts 2.12 that container renders an empty,\n * svg-less box whenever it mounts measuring 0 — which is exactly this case: the\n * chart is a `React.lazy` chunk that mounts after the widget popover has laid\n * out, and it never re-measures on its own. Feeding recharts an explicit numeric\n * width sidesteps the trap; the `ResizeObserver` keeps it in sync when the embed\n * is resized.\n */\nfunction useContainerWidth(): {\n ref: React.RefObject<HTMLDivElement | null>;\n width: number;\n} {\n const ref = React.useRef<HTMLDivElement>(null);\n const [width, setWidth] = React.useState(0);\n React.useLayoutEffect(() => {\n const el = ref.current;\n if (!el) return;\n const update = () => setWidth(el.clientWidth);\n update();\n const observer = new ResizeObserver(update);\n observer.observe(el);\n return () => observer.disconnect();\n }, []);\n return { ref, width };\n}\n\nconst Y_TICK_FONT_SIZE = 11;\n/** First-paint axis width; the measured width replaces it before paint. */\nconst Y_AXIS_INITIAL_WIDTH = 32;\n\n/**\n * Size the Y axis from the tick labels recharts painted (see `chart-axis.ts`):\n * a fixed `YAxis.width` clips any label wider than it, and there is no\n * auto-width axis in recharts 2.x. Re-measured whenever the painted labels can\n * change — the data, the chart type, the height (tick count) or the container\n * width — in a layout effect so the corrected axis paints in the same frame.\n * Converges in one pass: the Y ticks do not depend on the axis width, so the\n * re-render after `setWidth` measures the same labels and settles.\n */\nfunction useYAxisWidth(\n ref: React.RefObject<HTMLDivElement | null>,\n {\n type,\n data,\n height,\n containerWidth,\n }: Pick<ChartProps, 'type' | 'data'> & {\n height: number;\n containerWidth: number;\n },\n): number {\n const [width, setWidth] = React.useState(Y_AXIS_INITIAL_WIDTH);\n React.useLayoutEffect(() => {\n const el = ref.current;\n if (!el) return;\n const needed = requiredYAxisWidth(el, width, Y_TICK_FONT_SIZE);\n if (needed !== null && needed !== width) setWidth(needed);\n }, [ref, width, type, data, height, containerWidth]);\n return width;\n}\n\n/**\n * The actual chart renderer. Loaded lazily (see `Chart.tsx`) so recharts — the\n * one heavy dependency in the registry — is a separate chunk that only downloads\n * when the agent actually emits a Chart.\n *\n * Styled to match the companion/Impact-Dashboard charts: a monochrome ramp of\n * the theme foreground (90/70/50/30% alpha), donut pie with an optional center\n * label + legend, and clean axes (no tick/axis lines). The ramp reads the\n * widget's `--opencx-foreground` token so charts follow the embed theme, with\n * a neutral fallback for any context where the token is missing.\n */\n\nconst FOREGROUND = 'hsl(var(--opencx-foreground, 240 10% 20%))';\n// Same ramp as the dashboard's REPORTS_COLOR (surface-foreground at 90/70/50/30%).\nconst RAMP = [90, 70, 50, 30].map(\n (pct) => `color-mix(in oklch, ${FOREGROUND} ${pct}%, transparent)`,\n);\nconst rampColor = (i: number): string => RAMP[i % RAMP.length] ?? FOREGROUND;\n\nconst TOOLTIP_STYLE: React.CSSProperties = {\n borderRadius: 8,\n border: '1px solid color-mix(in oklch, currentColor 15%, transparent)',\n fontSize: 12,\n};\n\nexport default function ChartImpl({\n type,\n data,\n height,\n centerLabel,\n}: ChartProps) {\n const h = height ?? 220;\n const { ref, width } = useContainerWidth();\n const yAxisWidth = useYAxisWidth(ref, {\n type,\n data,\n height: h,\n containerWidth: width,\n });\n\n if (data.length === 0) {\n return <ChartEmpty height={h} />;\n }\n\n // Shared by the cartesian charts: the axis is exactly as wide as its labels,\n // so the plot keeps every px it can on small numbers and 7-digit or\n // currency-formatted ticks never lose their leading digits.\n const yAxis = (\n <YAxis\n tickLine={false}\n axisLine={false}\n fontSize={Y_TICK_FONT_SIZE}\n width={yAxisWidth}\n />\n );\n\n return (\n <div ref={ref} className=\"w-full text-foreground\" style={{ height: h }}>\n {width > 0 &&\n (type === 'pie' ? (\n <PieChart width={width} height={h}>\n <Tooltip contentStyle={TOOLTIP_STYLE} />\n <Legend\n verticalAlign=\"bottom\"\n iconType=\"circle\"\n iconSize={8}\n wrapperStyle={{ fontSize: 12 }}\n />\n <Pie\n data={data}\n dataKey=\"value\"\n nameKey=\"label\"\n outerRadius=\"85%\"\n innerRadius=\"60%\"\n >\n {data.map((_, i) => (\n <Cell key={i} fill={rampColor(i)} />\n ))}\n {centerLabel && (\n <Label\n position=\"center\"\n content={({ viewBox }) => {\n if (viewBox && 'cx' in viewBox && 'cy' in viewBox) {\n return (\n <text\n x={viewBox.cx}\n y={viewBox.cy}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n className=\"fill-foreground text-2xl font-medium\"\n >\n {centerLabel}\n </text>\n );\n }\n }}\n />\n )}\n </Pie>\n </PieChart>\n ) : type === 'line' ? (\n <LineChart\n width={width}\n height={h}\n data={data}\n margin={{ top: 8, right: 8, bottom: 0, left: 0 }}\n >\n <CartesianGrid strokeOpacity={0.15} vertical={false} />\n <XAxis\n dataKey=\"label\"\n tickLine={false}\n axisLine={false}\n tickMargin={10}\n fontSize={11}\n />\n {yAxis}\n <Tooltip contentStyle={TOOLTIP_STYLE} />\n <Line\n type=\"monotone\"\n dataKey=\"value\"\n stroke={rampColor(0)}\n strokeWidth={2}\n dot={false}\n />\n </LineChart>\n ) : (\n <BarChart\n width={width}\n height={h}\n data={data}\n margin={{ top: 8, right: 8, bottom: 0, left: 0 }}\n >\n <CartesianGrid strokeOpacity={0.15} vertical={false} />\n <XAxis\n dataKey=\"label\"\n tickLine={false}\n axisLine={false}\n tickMargin={10}\n fontSize={11}\n />\n {yAxis}\n <Tooltip\n contentStyle={TOOLTIP_STYLE}\n cursor={{ fillOpacity: 0.06 }}\n />\n <Bar dataKey=\"value\" fill={rampColor(0)} radius={[4, 4, 0, 0]} />\n </BarChart>\n ))}\n </div>\n );\n}\n\nfunction ChartEmpty({ height }: { height: number }) {\n const { t } = useJsonRenderHost();\n return (\n <div\n className=\"flex w-full items-center justify-center rounded-lg border border-muted-foreground/15 text-xs text-muted-foreground\"\n style={{ height }}\n >\n {t('json_no_chart_data')}\n </div>\n );\n}\n"],"names":["Y_TICK_LABEL_PAD","ESTIMATED_GLYPH_ADVANCE","Y_TICK_LABEL_SELECTOR","estimateTextWidth","text","fontSize","isMeasurableText","node","measureSvgTextWidth","measured","requiredYAxisWidth","container","currentWidth","widest","anchorX","tick","rawX","x","useContainerWidth","ref","React","width","setWidth","el","update","observer","Y_TICK_FONT_SIZE","Y_AXIS_INITIAL_WIDTH","useYAxisWidth","type","data","height","containerWidth","needed","FOREGROUND","RAMP","pct","rampColor","i","TOOLTIP_STYLE","ChartImpl","centerLabel","h","yAxisWidth","jsx","ChartEmpty","yAxis","YAxis","PieChart","Tooltip","Legend","jsxs","Pie","_","Cell","Label","viewBox","LineChart","CartesianGrid","XAxis","Line","BarChart","Bar","t","useJsonRenderHost"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("react/jsx-runtime"),h=require("react"),r=require("recharts"),j=require("./index-
|
|
2
|
-
//# sourceMappingURL=Chart.impl-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("react/jsx-runtime"),h=require("react"),r=require("recharts"),j=require("./index-D-LXlHO4.cjs"),L=2,T=.62,A=".recharts-yAxis .recharts-cartesian-axis-tick-value";function S(e,n){return e.length*n*T}function C(e){return"getComputedTextLength"in e&&typeof e.getComputedTextLength=="function"}function b(e,n){if(C(e)){const l=e.getComputedTextLength();if(l>0)return l}return S(e.textContent??"",n)}function _(e,n,l){let o=0,i=null;return e.querySelectorAll(A).forEach(s=>{const c=s.getAttribute("x");if(c===null)return;const a=Number(c);Number.isFinite(a)&&(i=a,o=Math.max(o,b(s,l)))}),i===null?null:Math.ceil(o+(n-i)+L)}function E(){const e=h.useRef(null),[n,l]=h.useState(0);return h.useLayoutEffect(()=>{const o=e.current;if(!o)return;const i=()=>l(o.clientWidth);i();const s=new ResizeObserver(i);return s.observe(o),()=>s.disconnect()},[]),{ref:e,width:n}}const y=11,I=32;function R(e,{type:n,data:l,height:o,containerWidth:i}){const[s,c]=h.useState(I);return h.useLayoutEffect(()=>{const a=e.current;if(!a)return;const u=_(a,s,y);u!==null&&u!==s&&c(u)},[e,s,n,l,o,i]),s}const p="hsl(var(--opencx-foreground, 240 10% 20%))",g=[90,70,50,30].map(e=>`color-mix(in oklch, ${p} ${e}%, transparent)`),f=e=>g[e%g.length]??p,x={borderRadius:8,border:"1px solid color-mix(in oklch, currentColor 15%, transparent)",fontSize:12};function W({type:e,data:n,height:l,centerLabel:o}){const i=l??220,{ref:s,width:c}=E(),a=R(s,{type:e,data:n,height:i,containerWidth:c});if(n.length===0)return t.jsx(k,{height:i});const u=t.jsx(r.YAxis,{tickLine:!1,axisLine:!1,fontSize:y,width:a});return t.jsx("div",{ref:s,className:"w-full text-foreground",style:{height:i},children:c>0&&(e==="pie"?t.jsxs(r.PieChart,{width:c,height:i,children:[t.jsx(r.Tooltip,{contentStyle:x}),t.jsx(r.Legend,{verticalAlign:"bottom",iconType:"circle",iconSize:8,wrapperStyle:{fontSize:12}}),t.jsxs(r.Pie,{data:n,dataKey:"value",nameKey:"label",outerRadius:"85%",innerRadius:"60%",children:[n.map((d,m)=>t.jsx(r.Cell,{fill:f(m)},m)),o&&t.jsx(r.Label,{position:"center",content:({viewBox:d})=>{if(d&&"cx"in d&&"cy"in d)return t.jsx("text",{x:d.cx,y:d.cy,textAnchor:"middle",dominantBaseline:"middle",className:"fill-foreground text-2xl font-medium",children:o})}})]})]}):e==="line"?t.jsxs(r.LineChart,{width:c,height:i,data:n,margin:{top:8,right:8,bottom:0,left:0},children:[t.jsx(r.CartesianGrid,{strokeOpacity:.15,vertical:!1}),t.jsx(r.XAxis,{dataKey:"label",tickLine:!1,axisLine:!1,tickMargin:10,fontSize:11}),u,t.jsx(r.Tooltip,{contentStyle:x}),t.jsx(r.Line,{type:"monotone",dataKey:"value",stroke:f(0),strokeWidth:2,dot:!1})]}):t.jsxs(r.BarChart,{width:c,height:i,data:n,margin:{top:8,right:8,bottom:0,left:0},children:[t.jsx(r.CartesianGrid,{strokeOpacity:.15,vertical:!1}),t.jsx(r.XAxis,{dataKey:"label",tickLine:!1,axisLine:!1,tickMargin:10,fontSize:11}),u,t.jsx(r.Tooltip,{contentStyle:x,cursor:{fillOpacity:.06}}),t.jsx(r.Bar,{dataKey:"value",fill:f(0),radius:[4,4,0,0]})]}))})}function k({height:e}){const{t:n}=j.useJsonRenderHost();return t.jsx("div",{className:"flex w-full items-center justify-center rounded-lg border border-muted-foreground/15 text-xs text-muted-foreground",style:{height:e},children:n("json_no_chart_data")})}exports.default=W;
|
|
2
|
+
//# sourceMappingURL=Chart.impl-Ds2iAr8P.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Chart.impl-BLTobbyF.cjs","sources":["../src/json-render/chart-axis.ts","../src/json-render/Chart.impl.tsx"],"sourcesContent":["/**\n * Y-axis sizing for the cartesian charts.\n *\n * recharts 2.x has no auto-width axis: `YAxis.width` is a fixed number, and a\n * tick label wider than the room left of its tick pokes out past the svg's\n * left edge, where the viewport clips it. That is how a 120/80/150/90/200 bar\n * chart rendered \"0\" for every tick in the 400px chat panel — only the last\n * digit of each label survived. The axis is sized from the labels recharts\n * actually painted instead: measured in a browser, estimated where the text\n * cannot be measured (jsdom, a hidden embed).\n */\n\n/** Slack past the widest label so hinting never shaves its first glyph. */\nconst Y_TICK_LABEL_PAD = 2;\n\n/**\n * Fallback glyph advance as a fraction of the font size, used when the text\n * cannot be measured. Sized for digits in UI sans fonts, which run 0.55–0.65em;\n * erring wide costs a couple of px of plot, erring narrow clips.\n */\nconst ESTIMATED_GLYPH_ADVANCE = 0.62;\n\n/** recharts' painted Y tick labels (`<text>`, right-anchored at their `x`). */\nexport const Y_TICK_LABEL_SELECTOR =\n '.recharts-yAxis .recharts-cartesian-axis-tick-value';\n\nexport function estimateTextWidth(text: string, fontSize: number): number {\n return text.length * fontSize * ESTIMATED_GLYPH_ADVANCE;\n}\n\ntype MeasurableText = Element & { getComputedTextLength: () => number };\n\n/** SVG text content elements in a browser; jsdom's `<text>` has no such method. */\nfunction isMeasurableText(node: Element): node is MeasurableText {\n return (\n 'getComputedTextLength' in node &&\n typeof node.getComputedTextLength === 'function'\n );\n}\n\n/**\n * The painted width of an svg text node: the browser's glyph run when it can\n * report one (a non-positive length means not laid out — a hidden embed), the\n * estimate otherwise.\n */\nexport function measureSvgTextWidth(node: Element, fontSize: number): number {\n if (isMeasurableText(node)) {\n const measured = node.getComputedTextLength();\n if (measured > 0) return measured;\n }\n return estimateTextWidth(node.textContent ?? '', fontSize);\n}\n\n/**\n * The narrowest `YAxis.width` that keeps every painted tick label inside the\n * svg, or `null` when no Y ticks are in the DOM (a pie, or nothing painted\n * yet). Derived from geometry, not from recharts constants: each label's\n * right edge sits at its tick's `x`, and `currentWidth - x` is whatever gap\n * the axis reserves between label and plot — so the requirement is the widest\n * label plus that gap, whichever tick size/margin recharts applied.\n */\nexport function requiredYAxisWidth(\n container: ParentNode,\n currentWidth: number,\n fontSize: number,\n): number | null {\n let widest = 0;\n let anchorX: number | null = null;\n container.querySelectorAll(Y_TICK_LABEL_SELECTOR).forEach((tick) => {\n const rawX = tick.getAttribute('x');\n if (rawX === null) return;\n const x = Number(rawX);\n if (!Number.isFinite(x)) return;\n anchorX = x;\n widest = Math.max(widest, measureSvgTextWidth(tick, fontSize));\n });\n if (anchorX === null) return null;\n return Math.ceil(widest + (currentWidth - anchorX) + Y_TICK_LABEL_PAD);\n}\n","import React from 'react';\nimport {\n Bar,\n BarChart,\n CartesianGrid,\n Cell,\n Label,\n Legend,\n Line,\n LineChart,\n Pie,\n PieChart,\n Tooltip,\n XAxis,\n YAxis,\n} from 'recharts';\nimport type { ChartProps } from './props';\nimport { requiredYAxisWidth } from './chart-axis';\nimport { useJsonRenderHost } from './host';\n\n/**\n * Measure the wrapper width ourselves instead of relying on recharts'\n * `ResponsiveContainer`. On recharts 2.12 that container renders an empty,\n * svg-less box whenever it mounts measuring 0 — which is exactly this case: the\n * chart is a `React.lazy` chunk that mounts after the widget popover has laid\n * out, and it never re-measures on its own. Feeding recharts an explicit numeric\n * width sidesteps the trap; the `ResizeObserver` keeps it in sync when the embed\n * is resized.\n */\nfunction useContainerWidth(): {\n ref: React.RefObject<HTMLDivElement | null>;\n width: number;\n} {\n const ref = React.useRef<HTMLDivElement>(null);\n const [width, setWidth] = React.useState(0);\n React.useLayoutEffect(() => {\n const el = ref.current;\n if (!el) return;\n const update = () => setWidth(el.clientWidth);\n update();\n const observer = new ResizeObserver(update);\n observer.observe(el);\n return () => observer.disconnect();\n }, []);\n return { ref, width };\n}\n\nconst Y_TICK_FONT_SIZE = 11;\n/** First-paint axis width; the measured width replaces it before paint. */\nconst Y_AXIS_INITIAL_WIDTH = 32;\n\n/**\n * Size the Y axis from the tick labels recharts painted (see `chart-axis.ts`):\n * a fixed `YAxis.width` clips any label wider than it, and there is no\n * auto-width axis in recharts 2.x. Re-measured whenever the painted labels can\n * change — the data, the chart type, the height (tick count) or the container\n * width — in a layout effect so the corrected axis paints in the same frame.\n * Converges in one pass: the Y ticks do not depend on the axis width, so the\n * re-render after `setWidth` measures the same labels and settles.\n */\nfunction useYAxisWidth(\n ref: React.RefObject<HTMLDivElement | null>,\n {\n type,\n data,\n height,\n containerWidth,\n }: Pick<ChartProps, 'type' | 'data'> & {\n height: number;\n containerWidth: number;\n },\n): number {\n const [width, setWidth] = React.useState(Y_AXIS_INITIAL_WIDTH);\n React.useLayoutEffect(() => {\n const el = ref.current;\n if (!el) return;\n const needed = requiredYAxisWidth(el, width, Y_TICK_FONT_SIZE);\n if (needed !== null && needed !== width) setWidth(needed);\n }, [ref, width, type, data, height, containerWidth]);\n return width;\n}\n\n/**\n * The actual chart renderer. Loaded lazily (see `Chart.tsx`) so recharts — the\n * one heavy dependency in the registry — is a separate chunk that only downloads\n * when the agent actually emits a Chart.\n *\n * Styled to match the companion/Impact-Dashboard charts: a monochrome ramp of\n * the theme foreground (90/70/50/30% alpha), donut pie with an optional center\n * label + legend, and clean axes (no tick/axis lines). The ramp reads the\n * widget's `--opencx-foreground` token so charts follow the embed theme, with\n * a neutral fallback for any context where the token is missing.\n */\n\nconst FOREGROUND = 'hsl(var(--opencx-foreground, 240 10% 20%))';\n// Same ramp as the dashboard's REPORTS_COLOR (surface-foreground at 90/70/50/30%).\nconst RAMP = [90, 70, 50, 30].map(\n (pct) => `color-mix(in oklch, ${FOREGROUND} ${pct}%, transparent)`,\n);\nconst rampColor = (i: number): string => RAMP[i % RAMP.length] ?? FOREGROUND;\n\nconst TOOLTIP_STYLE: React.CSSProperties = {\n borderRadius: 8,\n border: '1px solid color-mix(in oklch, currentColor 15%, transparent)',\n fontSize: 12,\n};\n\nexport default function ChartImpl({\n type,\n data,\n height,\n centerLabel,\n}: ChartProps) {\n const h = height ?? 220;\n const { ref, width } = useContainerWidth();\n const yAxisWidth = useYAxisWidth(ref, {\n type,\n data,\n height: h,\n containerWidth: width,\n });\n\n if (data.length === 0) {\n return <ChartEmpty height={h} />;\n }\n\n // Shared by the cartesian charts: the axis is exactly as wide as its labels,\n // so the plot keeps every px it can on small numbers and 7-digit or\n // currency-formatted ticks never lose their leading digits.\n const yAxis = (\n <YAxis\n tickLine={false}\n axisLine={false}\n fontSize={Y_TICK_FONT_SIZE}\n width={yAxisWidth}\n />\n );\n\n return (\n <div ref={ref} className=\"w-full text-foreground\" style={{ height: h }}>\n {width > 0 &&\n (type === 'pie' ? (\n <PieChart width={width} height={h}>\n <Tooltip contentStyle={TOOLTIP_STYLE} />\n <Legend\n verticalAlign=\"bottom\"\n iconType=\"circle\"\n iconSize={8}\n wrapperStyle={{ fontSize: 12 }}\n />\n <Pie\n data={data}\n dataKey=\"value\"\n nameKey=\"label\"\n outerRadius=\"85%\"\n innerRadius=\"60%\"\n >\n {data.map((_, i) => (\n <Cell key={i} fill={rampColor(i)} />\n ))}\n {centerLabel && (\n <Label\n position=\"center\"\n content={({ viewBox }) => {\n if (viewBox && 'cx' in viewBox && 'cy' in viewBox) {\n return (\n <text\n x={viewBox.cx}\n y={viewBox.cy}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n className=\"fill-foreground text-2xl font-medium\"\n >\n {centerLabel}\n </text>\n );\n }\n }}\n />\n )}\n </Pie>\n </PieChart>\n ) : type === 'line' ? (\n <LineChart\n width={width}\n height={h}\n data={data}\n margin={{ top: 8, right: 8, bottom: 0, left: 0 }}\n >\n <CartesianGrid strokeOpacity={0.15} vertical={false} />\n <XAxis\n dataKey=\"label\"\n tickLine={false}\n axisLine={false}\n tickMargin={10}\n fontSize={11}\n />\n {yAxis}\n <Tooltip contentStyle={TOOLTIP_STYLE} />\n <Line\n type=\"monotone\"\n dataKey=\"value\"\n stroke={rampColor(0)}\n strokeWidth={2}\n dot={false}\n />\n </LineChart>\n ) : (\n <BarChart\n width={width}\n height={h}\n data={data}\n margin={{ top: 8, right: 8, bottom: 0, left: 0 }}\n >\n <CartesianGrid strokeOpacity={0.15} vertical={false} />\n <XAxis\n dataKey=\"label\"\n tickLine={false}\n axisLine={false}\n tickMargin={10}\n fontSize={11}\n />\n {yAxis}\n <Tooltip\n contentStyle={TOOLTIP_STYLE}\n cursor={{ fillOpacity: 0.06 }}\n />\n <Bar dataKey=\"value\" fill={rampColor(0)} radius={[4, 4, 0, 0]} />\n </BarChart>\n ))}\n </div>\n );\n}\n\nfunction ChartEmpty({ height }: { height: number }) {\n const { t } = useJsonRenderHost();\n return (\n <div\n className=\"flex w-full items-center justify-center rounded-lg border border-muted-foreground/15 text-xs text-muted-foreground\"\n style={{ height }}\n >\n {t('json_no_chart_data')}\n </div>\n );\n}\n"],"names":["Y_TICK_LABEL_PAD","ESTIMATED_GLYPH_ADVANCE","Y_TICK_LABEL_SELECTOR","estimateTextWidth","text","fontSize","isMeasurableText","node","measureSvgTextWidth","measured","requiredYAxisWidth","container","currentWidth","widest","anchorX","tick","rawX","x","useContainerWidth","ref","React","width","setWidth","el","update","observer","Y_TICK_FONT_SIZE","Y_AXIS_INITIAL_WIDTH","useYAxisWidth","type","data","height","containerWidth","needed","FOREGROUND","RAMP","pct","rampColor","i","TOOLTIP_STYLE","ChartImpl","centerLabel","h","yAxisWidth","jsx","ChartEmpty","yAxis","YAxis","PieChart","Tooltip","Legend","jsxs","Pie","_","Cell","Label","viewBox","LineChart","CartesianGrid","XAxis","Line","BarChart","Bar","t","useJsonRenderHost"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Chart.impl-Ds2iAr8P.cjs","sources":["../src/json-render/chart-axis.ts","../src/json-render/Chart.impl.tsx"],"sourcesContent":["/**\n * Y-axis sizing for the cartesian charts.\n *\n * recharts 2.x has no auto-width axis: `YAxis.width` is a fixed number, and a\n * tick label wider than the room left of its tick pokes out past the svg's\n * left edge, where the viewport clips it. That is how a 120/80/150/90/200 bar\n * chart rendered \"0\" for every tick in the 400px chat panel — only the last\n * digit of each label survived. The axis is sized from the labels recharts\n * actually painted instead: measured in a browser, estimated where the text\n * cannot be measured (jsdom, a hidden embed).\n */\n\n/** Slack past the widest label so hinting never shaves its first glyph. */\nconst Y_TICK_LABEL_PAD = 2;\n\n/**\n * Fallback glyph advance as a fraction of the font size, used when the text\n * cannot be measured. Sized for digits in UI sans fonts, which run 0.55–0.65em;\n * erring wide costs a couple of px of plot, erring narrow clips.\n */\nconst ESTIMATED_GLYPH_ADVANCE = 0.62;\n\n/** recharts' painted Y tick labels (`<text>`, right-anchored at their `x`). */\nexport const Y_TICK_LABEL_SELECTOR =\n '.recharts-yAxis .recharts-cartesian-axis-tick-value';\n\nexport function estimateTextWidth(text: string, fontSize: number): number {\n return text.length * fontSize * ESTIMATED_GLYPH_ADVANCE;\n}\n\ntype MeasurableText = Element & { getComputedTextLength: () => number };\n\n/** SVG text content elements in a browser; jsdom's `<text>` has no such method. */\nfunction isMeasurableText(node: Element): node is MeasurableText {\n return (\n 'getComputedTextLength' in node &&\n typeof node.getComputedTextLength === 'function'\n );\n}\n\n/**\n * The painted width of an svg text node: the browser's glyph run when it can\n * report one (a non-positive length means not laid out — a hidden embed), the\n * estimate otherwise.\n */\nexport function measureSvgTextWidth(node: Element, fontSize: number): number {\n if (isMeasurableText(node)) {\n const measured = node.getComputedTextLength();\n if (measured > 0) return measured;\n }\n return estimateTextWidth(node.textContent ?? '', fontSize);\n}\n\n/**\n * The narrowest `YAxis.width` that keeps every painted tick label inside the\n * svg, or `null` when no Y ticks are in the DOM (a pie, or nothing painted\n * yet). Derived from geometry, not from recharts constants: each label's\n * right edge sits at its tick's `x`, and `currentWidth - x` is whatever gap\n * the axis reserves between label and plot — so the requirement is the widest\n * label plus that gap, whichever tick size/margin recharts applied.\n */\nexport function requiredYAxisWidth(\n container: ParentNode,\n currentWidth: number,\n fontSize: number,\n): number | null {\n let widest = 0;\n let anchorX: number | null = null;\n container.querySelectorAll(Y_TICK_LABEL_SELECTOR).forEach((tick) => {\n const rawX = tick.getAttribute('x');\n if (rawX === null) return;\n const x = Number(rawX);\n if (!Number.isFinite(x)) return;\n anchorX = x;\n widest = Math.max(widest, measureSvgTextWidth(tick, fontSize));\n });\n if (anchorX === null) return null;\n return Math.ceil(widest + (currentWidth - anchorX) + Y_TICK_LABEL_PAD);\n}\n","import React from 'react';\nimport {\n Bar,\n BarChart,\n CartesianGrid,\n Cell,\n Label,\n Legend,\n Line,\n LineChart,\n Pie,\n PieChart,\n Tooltip,\n XAxis,\n YAxis,\n} from 'recharts';\nimport type { ChartProps } from './props';\nimport { requiredYAxisWidth } from './chart-axis';\nimport { useJsonRenderHost } from './host';\n\n/**\n * Measure the wrapper width ourselves instead of relying on recharts'\n * `ResponsiveContainer`. On recharts 2.12 that container renders an empty,\n * svg-less box whenever it mounts measuring 0 — which is exactly this case: the\n * chart is a `React.lazy` chunk that mounts after the widget popover has laid\n * out, and it never re-measures on its own. Feeding recharts an explicit numeric\n * width sidesteps the trap; the `ResizeObserver` keeps it in sync when the embed\n * is resized.\n */\nfunction useContainerWidth(): {\n ref: React.RefObject<HTMLDivElement | null>;\n width: number;\n} {\n const ref = React.useRef<HTMLDivElement>(null);\n const [width, setWidth] = React.useState(0);\n React.useLayoutEffect(() => {\n const el = ref.current;\n if (!el) return;\n const update = () => setWidth(el.clientWidth);\n update();\n const observer = new ResizeObserver(update);\n observer.observe(el);\n return () => observer.disconnect();\n }, []);\n return { ref, width };\n}\n\nconst Y_TICK_FONT_SIZE = 11;\n/** First-paint axis width; the measured width replaces it before paint. */\nconst Y_AXIS_INITIAL_WIDTH = 32;\n\n/**\n * Size the Y axis from the tick labels recharts painted (see `chart-axis.ts`):\n * a fixed `YAxis.width` clips any label wider than it, and there is no\n * auto-width axis in recharts 2.x. Re-measured whenever the painted labels can\n * change — the data, the chart type, the height (tick count) or the container\n * width — in a layout effect so the corrected axis paints in the same frame.\n * Converges in one pass: the Y ticks do not depend on the axis width, so the\n * re-render after `setWidth` measures the same labels and settles.\n */\nfunction useYAxisWidth(\n ref: React.RefObject<HTMLDivElement | null>,\n {\n type,\n data,\n height,\n containerWidth,\n }: Pick<ChartProps, 'type' | 'data'> & {\n height: number;\n containerWidth: number;\n },\n): number {\n const [width, setWidth] = React.useState(Y_AXIS_INITIAL_WIDTH);\n React.useLayoutEffect(() => {\n const el = ref.current;\n if (!el) return;\n const needed = requiredYAxisWidth(el, width, Y_TICK_FONT_SIZE);\n if (needed !== null && needed !== width) setWidth(needed);\n }, [ref, width, type, data, height, containerWidth]);\n return width;\n}\n\n/**\n * The actual chart renderer. Loaded lazily (see `Chart.tsx`) so recharts — the\n * one heavy dependency in the registry — is a separate chunk that only downloads\n * when the agent actually emits a Chart.\n *\n * Styled to match the companion/Impact-Dashboard charts: a monochrome ramp of\n * the theme foreground (90/70/50/30% alpha), donut pie with an optional center\n * label + legend, and clean axes (no tick/axis lines). The ramp reads the\n * widget's `--opencx-foreground` token so charts follow the embed theme, with\n * a neutral fallback for any context where the token is missing.\n */\n\nconst FOREGROUND = 'hsl(var(--opencx-foreground, 240 10% 20%))';\n// Same ramp as the dashboard's REPORTS_COLOR (surface-foreground at 90/70/50/30%).\nconst RAMP = [90, 70, 50, 30].map(\n (pct) => `color-mix(in oklch, ${FOREGROUND} ${pct}%, transparent)`,\n);\nconst rampColor = (i: number): string => RAMP[i % RAMP.length] ?? FOREGROUND;\n\nconst TOOLTIP_STYLE: React.CSSProperties = {\n borderRadius: 8,\n border: '1px solid color-mix(in oklch, currentColor 15%, transparent)',\n fontSize: 12,\n};\n\nexport default function ChartImpl({\n type,\n data,\n height,\n centerLabel,\n}: ChartProps) {\n const h = height ?? 220;\n const { ref, width } = useContainerWidth();\n const yAxisWidth = useYAxisWidth(ref, {\n type,\n data,\n height: h,\n containerWidth: width,\n });\n\n if (data.length === 0) {\n return <ChartEmpty height={h} />;\n }\n\n // Shared by the cartesian charts: the axis is exactly as wide as its labels,\n // so the plot keeps every px it can on small numbers and 7-digit or\n // currency-formatted ticks never lose their leading digits.\n const yAxis = (\n <YAxis\n tickLine={false}\n axisLine={false}\n fontSize={Y_TICK_FONT_SIZE}\n width={yAxisWidth}\n />\n );\n\n return (\n <div ref={ref} className=\"w-full text-foreground\" style={{ height: h }}>\n {width > 0 &&\n (type === 'pie' ? (\n <PieChart width={width} height={h}>\n <Tooltip contentStyle={TOOLTIP_STYLE} />\n <Legend\n verticalAlign=\"bottom\"\n iconType=\"circle\"\n iconSize={8}\n wrapperStyle={{ fontSize: 12 }}\n />\n <Pie\n data={data}\n dataKey=\"value\"\n nameKey=\"label\"\n outerRadius=\"85%\"\n innerRadius=\"60%\"\n >\n {data.map((_, i) => (\n <Cell key={i} fill={rampColor(i)} />\n ))}\n {centerLabel && (\n <Label\n position=\"center\"\n content={({ viewBox }) => {\n if (viewBox && 'cx' in viewBox && 'cy' in viewBox) {\n return (\n <text\n x={viewBox.cx}\n y={viewBox.cy}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n className=\"fill-foreground text-2xl font-medium\"\n >\n {centerLabel}\n </text>\n );\n }\n }}\n />\n )}\n </Pie>\n </PieChart>\n ) : type === 'line' ? (\n <LineChart\n width={width}\n height={h}\n data={data}\n margin={{ top: 8, right: 8, bottom: 0, left: 0 }}\n >\n <CartesianGrid strokeOpacity={0.15} vertical={false} />\n <XAxis\n dataKey=\"label\"\n tickLine={false}\n axisLine={false}\n tickMargin={10}\n fontSize={11}\n />\n {yAxis}\n <Tooltip contentStyle={TOOLTIP_STYLE} />\n <Line\n type=\"monotone\"\n dataKey=\"value\"\n stroke={rampColor(0)}\n strokeWidth={2}\n dot={false}\n />\n </LineChart>\n ) : (\n <BarChart\n width={width}\n height={h}\n data={data}\n margin={{ top: 8, right: 8, bottom: 0, left: 0 }}\n >\n <CartesianGrid strokeOpacity={0.15} vertical={false} />\n <XAxis\n dataKey=\"label\"\n tickLine={false}\n axisLine={false}\n tickMargin={10}\n fontSize={11}\n />\n {yAxis}\n <Tooltip\n contentStyle={TOOLTIP_STYLE}\n cursor={{ fillOpacity: 0.06 }}\n />\n <Bar dataKey=\"value\" fill={rampColor(0)} radius={[4, 4, 0, 0]} />\n </BarChart>\n ))}\n </div>\n );\n}\n\nfunction ChartEmpty({ height }: { height: number }) {\n const { t } = useJsonRenderHost();\n return (\n <div\n className=\"flex w-full items-center justify-center rounded-lg border border-muted-foreground/15 text-xs text-muted-foreground\"\n style={{ height }}\n >\n {t('json_no_chart_data')}\n </div>\n );\n}\n"],"names":["Y_TICK_LABEL_PAD","ESTIMATED_GLYPH_ADVANCE","Y_TICK_LABEL_SELECTOR","estimateTextWidth","text","fontSize","isMeasurableText","node","measureSvgTextWidth","measured","requiredYAxisWidth","container","currentWidth","widest","anchorX","tick","rawX","x","useContainerWidth","ref","React","width","setWidth","el","update","observer","Y_TICK_FONT_SIZE","Y_AXIS_INITIAL_WIDTH","useYAxisWidth","type","data","height","containerWidth","needed","FOREGROUND","RAMP","pct","rampColor","i","TOOLTIP_STYLE","ChartImpl","centerLabel","h","yAxisWidth","jsx","ChartEmpty","yAxis","YAxis","PieChart","Tooltip","Legend","jsxs","Pie","_","Cell","Label","viewBox","LineChart","CartesianGrid","XAxis","Line","BarChart","Bar","t","useJsonRenderHost"],"mappings":"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"}
|