@zendir/ui 0.8.0 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/dist/react/charts/unified/AstroChart.js +7 -6
- package/dist/react/charts/unified/AstroChart.js.map +1 -1
- package/dist/react/core/data/DataTable.js +1 -0
- package/dist/react/core/data/DataTable.js.map +1 -1
- package/dist/react/core/overlays/Tooltip.d.ts +14 -0
- package/dist/react/core/overlays/Tooltip.js +19 -3
- package/dist/react/core/overlays/Tooltip.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @zendir/ui
|
|
2
2
|
|
|
3
|
+
## 0.8.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 0a14e84: Fix `DataTable`'s `rowTooltipContent` taking the table apart. Each row was wrapped in the tooltip's own `div`, which between `tbody` and `tr` is not a table row — the rows stopped taking part in the table's layout and their cells reflowed as inline text, several rows to a line, with the columns gone.
|
|
8
|
+
|
|
9
|
+
`Tooltip` gains `asChild`, which attaches the handlers and the ref to the child instead of wrapping it, composing with any handlers the child already has. `DataTable` uses it for rows, so a row keeps `tbody` as its parent whatever is attached to it.
|
|
10
|
+
|
|
11
|
+
## 0.8.1
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- 927fecb: A mark line no longer animates itself in. It is a fact about the data, true before the chart first paints, but the chart replaces its whole option on every redraw rather than merging — so that entrance replayed each time anything else about the chart changed, and reference lines drew themselves back in while a reader was looking at something else.
|
|
16
|
+
|
|
17
|
+
Its label also carried more weight than a threshold warrants: 11px semibold inside a bordered pill read as an alert laid over the plot rather than a value written beside it. Smaller, lighter, tighter padding.
|
|
18
|
+
|
|
3
19
|
## 0.8.0
|
|
4
20
|
|
|
5
21
|
### Minor Changes
|
|
@@ -499,6 +499,7 @@ function buildEChartsOptions(props, theme, hideTitle = false) {
|
|
|
499
499
|
if (seriesArray.length > 0) seriesArray[0].markLine = {
|
|
500
500
|
silent: true,
|
|
501
501
|
symbol: "none",
|
|
502
|
+
animation: false,
|
|
502
503
|
data: annotations.markLines.data.map((line) => ({
|
|
503
504
|
...line,
|
|
504
505
|
lineStyle: {
|
|
@@ -510,17 +511,17 @@ function buildEChartsOptions(props, theme, hideTitle = false) {
|
|
|
510
511
|
show: !!line.label,
|
|
511
512
|
formatter: line.label,
|
|
512
513
|
position: line.labelPosition || "middle",
|
|
513
|
-
distance:
|
|
514
|
+
distance: 6,
|
|
514
515
|
color: theme.textStyle.color,
|
|
515
|
-
fontWeight:
|
|
516
|
-
fontSize:
|
|
516
|
+
fontWeight: 500,
|
|
517
|
+
fontSize: 9.5,
|
|
517
518
|
textBorderWidth: 0,
|
|
518
519
|
textBorderColor: "transparent",
|
|
519
520
|
backgroundColor: theme.tooltip.backgroundColor,
|
|
520
521
|
borderColor: line.color || theme.color[2],
|
|
521
|
-
borderWidth:
|
|
522
|
-
borderRadius:
|
|
523
|
-
padding: [
|
|
522
|
+
borderWidth: .75,
|
|
523
|
+
borderRadius: 3,
|
|
524
|
+
padding: [1, 5]
|
|
524
525
|
}
|
|
525
526
|
}))
|
|
526
527
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AstroChart.js","names":[],"sources":["../../../../src/react/charts/unified/AstroChart.tsx"],"sourcesContent":["/**\r\n * @zendir/ui - AstroChart Component\r\n *\r\n * Enterprise-grade unified charting component built on ECharts.\r\n * Follows AstroUXD design patterns with full theme integration.\r\n *\r\n * Features:\r\n * - 15+ chart types\r\n * - Real-time streaming support\r\n * - Zoom, pan, brush selection\r\n * - Annotations (thresholds, regions)\r\n * - Export (PNG, SVG, CSV)\r\n * - Crosshair sync across charts\r\n * - Full accessibility support\r\n * - Dark/light theme support\r\n */\r\n\r\nimport React, {\r\n useRef,\r\n useEffect,\r\n useMemo,\r\n useCallback,\r\n useState,\r\n forwardRef,\r\n useImperativeHandle,\r\n memo,\r\n} from \"react\";\r\nimport ReactEChartsCore from \"echarts-for-react/lib/core\";\r\nimport * as echarts from \"echarts/core\";\r\nimport {\r\n LineChart,\r\n BarChart,\r\n PieChart,\r\n ScatterChart,\r\n RadarChart,\r\n GaugeChart,\r\n HeatmapChart,\r\n CandlestickChart,\r\n BoxplotChart,\r\n TreemapChart,\r\n SunburstChart,\r\n FunnelChart,\r\n SankeyChart,\r\n CustomChart,\r\n GraphChart,\r\n ParallelChart as EChartsParallelChart,\r\n} from \"echarts/charts\";\r\nimport {\r\n GridComponent,\r\n TooltipComponent,\r\n TitleComponent,\r\n LegendComponent,\r\n DataZoomComponent,\r\n ToolboxComponent,\r\n MarkLineComponent,\r\n MarkAreaComponent,\r\n MarkPointComponent,\r\n VisualMapComponent,\r\n BrushComponent,\r\n PolarComponent,\r\n GraphicComponent,\r\n RadarComponent,\r\n GeoComponent,\r\n ParallelComponent,\r\n} from \"echarts/components\";\r\nimport { CanvasRenderer, SVGRenderer } from \"echarts/renderers\";\r\nimport type { EChartsOption, ECharts } from \"echarts\";\r\n\r\nimport { useTheme } from \"../../theme\";\r\nimport { AstroIcon, type AstroIconName } from \"../../core/display/AstroIcon\";\r\nimport { HeaderIconWithStatus } from \"../../core/display/HeaderIconWithStatus\";\r\nimport { useCardBorderStyle } from \"../../context/DisplaySettingsContext\";\r\nimport {\r\n createAstroEChartsTheme,\r\n createAreaGradient,\r\n getSeriesColor,\r\n} from \"./theme\";\r\nimport type {\r\n AstroChartProps,\r\n SeriesData,\r\n ChartType,\r\n ChartEventParams,\r\n DataPoint,\r\n TimeSeriesPoint,\r\n ExportFileNameContext,\r\n ChartStatus,\r\n} from \"./types\";\r\n\r\n// =============================================================================\r\n// Status Shape SVGs (matching DataValue component for consistency)\r\n// =============================================================================\r\n\r\nconst STATUS_SHAPES: Record<\r\n ChartStatus,\r\n (size: number, color: string) => React.ReactNode\r\n> = {\r\n off: (size, color) => (\r\n <svg viewBox=\"0 0 12 12\" width={size} height={size}>\r\n <circle cx=\"6\" cy=\"6\" r=\"5\" fill={color} />\r\n </svg>\r\n ),\r\n standby: (size, color) => (\r\n <svg viewBox=\"0 0 12 12\" width={size} height={size}>\r\n <circle cx=\"6\" cy=\"6\" r=\"5\" fill={color} />\r\n </svg>\r\n ),\r\n normal: (size, color) => (\r\n <svg viewBox=\"0 0 12 12\" width={size} height={size}>\r\n <circle cx=\"6\" cy=\"6\" r=\"5\" fill={color} />\r\n </svg>\r\n ),\r\n caution: (size, color) => (\r\n <svg viewBox=\"0 0 12 12\" width={size} height={size}>\r\n <rect x=\"1\" y=\"1\" width=\"10\" height=\"10\" fill={color} />\r\n </svg>\r\n ),\r\n serious: (size, color) => (\r\n <svg viewBox=\"0 0 12 12\" width={size} height={size}>\r\n <polygon points=\"6,1 11,6 6,11 1,6\" fill={color} />\r\n </svg>\r\n ),\r\n critical: (size, color) => (\r\n <svg viewBox=\"0 0 12 12\" width={size} height={size}>\r\n <polygon points=\"6,11 1,2 11,2\" fill={color} />\r\n </svg>\r\n ),\r\n};\r\n\r\n/**\r\n * Astro UX Status Labels\r\n * Follows Astro UXDS naming conventions\r\n * https://www.astrouxds.com/patterns/status-system/\r\n */\r\nconst STATUS_LABELS: Record<ChartStatus, string> = {\r\n off: \"Off\",\r\n standby: \"Standby\",\r\n normal: \"Normal\",\r\n caution: \"Caution\",\r\n serious: \"Serious\",\r\n critical: \"Critical\",\r\n};\r\n\r\n/** Slug for safe filenames: lowercase, alphanumeric and hyphens only */\r\nfunction slug(str: string): string {\r\n return str\r\n .toLowerCase()\r\n .replace(/[^a-z0-9]+/g, \"-\")\r\n .replace(/^-|-$/g, \"\");\r\n}\r\n\r\n/** Build base export name from title, series names, and chart type */\r\nfunction derivedExportBaseName(\r\n title: string | undefined,\r\n seriesNames: string[],\r\n chartType: string,\r\n): string {\r\n const parts: string[] = [\"zendir\"];\r\n if (title) parts.push(slug(title));\r\n else parts.push(\"chart\");\r\n const seriesSlug = seriesNames\r\n .slice(0, 4)\r\n .map(slug)\r\n .filter(Boolean)\r\n .join(\"-\");\r\n if (seriesSlug) parts.push(seriesSlug);\r\n parts.push(slug(chartType));\r\n return parts.join(\"-\") || \"zendir-chart\";\r\n}\r\n\r\n// Register ECharts components\r\necharts.use([\r\n LineChart,\r\n BarChart,\r\n PieChart,\r\n ScatterChart,\r\n RadarChart,\r\n GaugeChart,\r\n HeatmapChart,\r\n CandlestickChart,\r\n BoxplotChart,\r\n TreemapChart,\r\n SunburstChart,\r\n FunnelChart,\r\n SankeyChart,\r\n CustomChart,\r\n GraphChart,\r\n EChartsParallelChart,\r\n GridComponent,\r\n TooltipComponent,\r\n TitleComponent,\r\n LegendComponent,\r\n DataZoomComponent,\r\n ToolboxComponent,\r\n MarkLineComponent,\r\n MarkAreaComponent,\r\n MarkPointComponent,\r\n VisualMapComponent,\r\n BrushComponent,\r\n PolarComponent,\r\n // Registers ECharts' free-form graphic elements — a circle, a line, drawn\r\n // straight onto the chart by id rather than as a series. Nothing in this\r\n // file uses it, but `chart.setOption({ graphic: [...] })` from outside —\r\n // a playhead, a crosshair, a hover dot, all things drawn imperatively so a\r\n // pointer move never rebuilds the whole option — depends on it, and\r\n // without it that call succeeds and draws nothing: ECharts does not\r\n // recognise the `graphic` key and silently no-ops, with no error to find.\r\n GraphicComponent,\r\n RadarComponent,\r\n GeoComponent,\r\n ParallelComponent,\r\n CanvasRenderer,\r\n SVGRenderer,\r\n]);\r\n\r\n// =============================================================================\r\n// Chart Instance Handle\r\n// =============================================================================\r\n\r\nexport interface AstroChartHandle {\r\n /** Get ECharts instance */\r\n getChart: () => ECharts | undefined;\r\n /** Resize chart */\r\n resize: () => void;\r\n /** Export as image (png, jpeg, svg) */\r\n exportImage: (\r\n type?: \"png\" | \"jpeg\" | \"jpg\" | \"svg\",\r\n pixelRatio?: number,\r\n ) => string | undefined;\r\n /** Export data as CSV */\r\n exportCSV: () => string;\r\n /** Suggested export filename from title, series names, and chart type (with optional extension and timestamp) */\r\n getExportFileName: (extension?: string, includeTimestamp?: boolean) => string;\r\n /** Append data for real-time updates */\r\n appendData: (seriesIndex: number, data: DataPoint | TimeSeriesPoint) => void;\r\n /** Clear all data */\r\n clearData: () => void;\r\n /** Dispatch action */\r\n dispatchAction: (action: unknown) => void;\r\n /**\r\n * Move a vertical cursor to an x-axis value, or clear it with `null`.\r\n *\r\n * Distinct from `annotations.markLines`, which is a prop: changing a prop\r\n * rebuilds the whole option, and rebuilding the option re-transforms every\r\n * point of every series. A chart following a playhead does that on each\r\n * frame, so on a few thousand points it stops being able to keep up. This\r\n * touches one graphic element and leaves the series alone.\r\n */\r\n setCursor: (value: number | null, options?: { color?: string }) => void;\r\n}\r\n\r\n// =============================================================================\r\n// Series Transformer\r\n// =============================================================================\r\n\r\nfunction transformSeriesToECharts(\r\n series: SeriesData[],\r\n chartType: ChartType,\r\n theme: ReturnType<typeof createAstroEChartsTheme>,\r\n): EChartsOption[\"series\"] {\r\n return series.map((s, index) => {\r\n const seriesType = s.type || chartType;\r\n const color = s.color || getSeriesColor(index);\r\n\r\n // Base series config\r\n const baseSeries: Record<string, unknown> = {\r\n id: s.id,\r\n name: s.name,\r\n type: mapChartType(seriesType),\r\n data: transformData(s.data, seriesType),\r\n yAxisIndex: s.yAxisIndex || 0,\r\n showSymbol: seriesType === \"scatter\" || s.symbol?.type !== \"none\",\r\n smooth: s.smooth ?? (seriesType === \"line\" || seriesType === \"area\"),\r\n };\r\n\r\n // Line/Area specific\r\n if (seriesType === \"line\" || seriesType === \"area\") {\r\n baseSeries.lineStyle = {\r\n width: s.lineStyle?.width ?? 2,\r\n type: s.lineStyle?.type ?? \"solid\",\r\n opacity: s.lineStyle?.opacity ?? 1,\r\n };\r\n baseSeries.itemStyle = { color };\r\n\r\n if (seriesType === \"area\") {\r\n baseSeries.areaStyle = s.areaStyle || {\r\n opacity: 0.3,\r\n color: createAreaGradient(color),\r\n };\r\n }\r\n\r\n if (s.symbol) {\r\n baseSeries.symbol = s.symbol.type || \"circle\";\r\n baseSeries.symbolSize = s.symbol.size || 6;\r\n }\r\n }\r\n\r\n // Bar specific\r\n if (seriesType === \"bar\") {\r\n baseSeries.itemStyle = {\r\n color,\r\n borderRadius: [4, 4, 0, 0],\r\n };\r\n if (s.stack) {\r\n baseSeries.stack = s.stack;\r\n }\r\n }\r\n\r\n // Pie/Donut specific — theme color, no white border on labels\r\n if (seriesType === \"pie\" || seriesType === \"donut\") {\r\n baseSeries.radius = seriesType === \"donut\" ? [\"40%\", \"70%\"] : \"70%\";\r\n baseSeries.label = {\r\n show: true,\r\n formatter: \"{b}: {d}%\",\r\n color: theme.textStyle.color,\r\n textBorderWidth: 0,\r\n textBorderColor: \"transparent\",\r\n };\r\n baseSeries.emphasis = {\r\n itemStyle: {\r\n shadowBlur: 10,\r\n shadowOffsetX: 0,\r\n shadowColor: \"rgba(0, 0, 0, 0.5)\",\r\n },\r\n };\r\n }\r\n\r\n // Scatter specific\r\n if (seriesType === \"scatter\") {\r\n baseSeries.symbolSize = s.symbol?.size || 10;\r\n baseSeries.itemStyle = { color };\r\n }\r\n\r\n // Gauge specific\r\n if (seriesType === \"gauge\") {\r\n baseSeries.detail = {\r\n formatter: \"{value}%\",\r\n fontSize: 20,\r\n color: theme.textStyle.color,\r\n };\r\n baseSeries.axisLine = {\r\n lineStyle: {\r\n width: 10,\r\n color: [\r\n [0.3, theme.color[1]], // Green\r\n [0.7, theme.color[3]], // Yellow\r\n [1, theme.color[2]], // Red\r\n ],\r\n },\r\n };\r\n }\r\n\r\n return baseSeries;\r\n });\r\n}\r\n\r\n/** The one graphic element `setCursor` owns, so repeated calls replace it. */\r\nconst CURSOR_ID = \"astro-chart-cursor\";\r\n\r\nfunction mapChartType(type: ChartType): string {\r\n switch (type) {\r\n case \"area\":\r\n return \"line\";\r\n case \"donut\":\r\n return \"pie\";\r\n default:\r\n return type;\r\n }\r\n}\r\n\r\nfunction transformData(data: SeriesData[\"data\"], type: ChartType): unknown[] {\r\n if (!data || data.length === 0) return [];\r\n\r\n // Handle array of numbers\r\n if (typeof data[0] === \"number\") {\r\n return data as number[];\r\n }\r\n\r\n // Handle [x, y] tuples\r\n if (Array.isArray(data[0])) {\r\n return data;\r\n }\r\n\r\n // Handle DataPoint or TimeSeriesPoint\r\n const firstItem = data[0] as DataPoint | TimeSeriesPoint;\r\n\r\n if (\"time\" in firstItem) {\r\n // TimeSeriesPoint\r\n return (data as TimeSeriesPoint[]).map((d) => [d.time, d.value]);\r\n }\r\n\r\n // For pie/donut charts, ECharts expects { name, value } format\r\n if (type === \"pie\" || type === \"donut\") {\r\n return (data as DataPoint[]).map((d) => ({\r\n name: String(d.x),\r\n value: d.y,\r\n // Apply color via itemStyle if specified\r\n ...(d.color ? { itemStyle: { color: d.color } } : {}),\r\n // Preserve any additional metadata\r\n ...(d.meta || {}),\r\n }));\r\n }\r\n\r\n // For scatter charts, preserve per-point color as itemStyle\r\n if (type === \"scatter\") {\r\n return (data as DataPoint[]).map((d) => ({\r\n value: [d.x, d.y],\r\n ...(d.color ? { itemStyle: { color: d.color } } : {}),\r\n ...(d.label ? { name: d.label } : {}),\r\n }));\r\n }\r\n\r\n // DataPoint for other chart types\r\n return (data as DataPoint[]).map((d) => [d.x, d.y]);\r\n}\r\n\r\n// =============================================================================\r\n// Options Builder\r\n// =============================================================================\r\n\r\nfunction buildEChartsOptions(\r\n props: AstroChartProps,\r\n theme: ReturnType<typeof createAstroEChartsTheme>,\r\n hideTitle = false,\r\n): EChartsOption {\r\n const {\r\n type,\r\n series,\r\n title,\r\n subtitle,\r\n xAxis,\r\n yAxis,\r\n tooltip,\r\n legend,\r\n zoom,\r\n annotations,\r\n loading,\r\n compactAxes = true, // Default: hide axis names for more chart space\r\n } = props;\r\n\r\n // Hide title when using custom header (transparent theme with icon)\r\n const showEChartsTitle = title && !hideTitle;\r\n\r\n // Note: When compactAxes is true, axis names are hidden from the chart\r\n // but developers can access xAxis.name and yAxis.name via props for custom tooltips\r\n\r\n const isPolar = type === \"radar\";\r\n const isPie = type === \"pie\" || type === \"donut\";\r\n const isGauge = type === \"gauge\";\r\n const isSankey = type === \"sankey\";\r\n const isTreemap = type === \"treemap\" || type === \"sunburst\";\r\n const isGraph =\r\n props.echartsOptions?.series &&\r\n Array.isArray(props.echartsOptions.series) &&\r\n props.echartsOptions.series.some(\r\n (s: unknown) => (s as { type?: string })?.type === \"graph\",\r\n );\r\n\r\n // Detect 3D charts (they use grid3D, xAxis3D, etc. - should NOT have 2D axes)\r\n const is3D = !!(\r\n props.echartsOptions?.grid3D ||\r\n props.echartsOptions?.xAxis3D ||\r\n props.echartsOptions?.globe ||\r\n (props.echartsOptions?.series &&\r\n Array.isArray(props.echartsOptions.series) &&\r\n props.echartsOptions.series.some((s: unknown) => {\r\n const seriesType = (s as { type?: string })?.type || \"\";\r\n return seriesType.includes(\"3D\") || seriesType.includes(\"3d\");\r\n }))\r\n );\r\n\r\n // Charts that don't need cartesian grid/axes\r\n const needsGrid =\r\n !isPolar &&\r\n !isPie &&\r\n !isGauge &&\r\n !isSankey &&\r\n !isTreemap &&\r\n !isGraph &&\r\n !is3D &&\r\n !props.hideAxes;\r\n\r\n /** Whether the right margin needs to leave room for a mark line's own label. */\r\n const hasEndLabelledMarkLine = !!annotations?.markLines?.data.some(\r\n (line) => line.labelPosition === \"end\",\r\n );\r\n\r\n // Calculate layout based on title and legend position\r\n // Default legend to bottom for better chart space utilization\r\n // When using custom header (hideTitle=true), we still need top spacing for the header\r\n const hasTitle = hideTitle ? true : !!title;\r\n const hasSubtitle = hideTitle ? false : !!subtitle;\r\n const legendPosition = legend?.position ?? \"bottom\"; // Default to bottom\r\n const legendAtTop = legendPosition === \"top\";\r\n const legendAtBottom = legendPosition === \"bottom\";\r\n const showLegend = legend?.show !== false;\r\n\r\n // Build options\r\n const options: EChartsOption = {\r\n // Title - positioned to avoid legend overlap (hidden when using custom header)\r\n title: showEChartsTitle\r\n ? {\r\n text: title,\r\n subtext: subtitle,\r\n left: \"center\",\r\n top: legendAtTop && showLegend ? 35 : 10,\r\n textStyle: {\r\n ...theme.title.textStyle,\r\n fontWeight:\r\n (theme.title.textStyle.fontWeight as \"normal\" | \"bold\") ??\r\n \"normal\",\r\n },\r\n subtextStyle: theme.title.subtextStyle,\r\n }\r\n : undefined,\r\n\r\n // Tooltip with improved contrast\r\n // When compactAxes is true, axis names are shown in the axisPointer label on hover\r\n tooltip:\r\n tooltip?.enabled !== false\r\n ? ({\r\n trigger: tooltip?.trigger || (isPie ? \"item\" : \"axis\"),\r\n axisPointer: {\r\n type:\r\n tooltip?.crosshair === true || tooltip?.crosshair === \"both\"\r\n ? \"cross\"\r\n : \"line\",\r\n lineStyle: theme.axisPointer.lineStyle,\r\n crossStyle: theme.axisPointer.crossStyle,\r\n // Show axis value labels on hover (includes axis name context)\r\n label: compactAxes\r\n ? {\r\n show: true,\r\n backgroundColor: theme.tooltip.backgroundColor,\r\n borderColor: theme.tooltip.borderColor,\r\n color: theme.tooltip.textStyle.color,\r\n fontSize: 11,\r\n padding: [4, 8],\r\n // Add axis name prefix to the label when in compact mode\r\n formatter: (params: {\r\n axisDimension: string;\r\n axisIndex: number;\r\n value: number | string;\r\n }) => {\r\n const axisName =\r\n params.axisDimension === \"x\"\r\n ? xAxis?.name\r\n : Array.isArray(yAxis)\r\n ? yAxis[params.axisIndex]?.name\r\n : yAxis?.name;\r\n const value =\r\n typeof params.value === \"number\"\r\n ? params.value.toLocaleString()\r\n : params.value;\r\n return axisName ? `${axisName}: ${value}` : String(value);\r\n },\r\n }\r\n : undefined,\r\n },\r\n backgroundColor: theme.tooltip.backgroundColor,\r\n borderColor: theme.tooltip.borderColor,\r\n borderWidth: theme.tooltip.borderWidth,\r\n textStyle: {\r\n ...theme.tooltip.textStyle,\r\n fontSize: 14, // AstroUXDS minimum 14pt\r\n },\r\n extraCssText: theme.tooltip.extraCssText,\r\n confine: tooltip?.confine ?? true,\r\n formatter: tooltip?.formatter as unknown as undefined,\r\n } as any)\r\n : undefined,\r\n\r\n // Legend - ultra-compact styling for maximum chart space\r\n legend: showLegend\r\n ? {\r\n show: true,\r\n type: \"scroll\",\r\n orient: legend?.orient || \"horizontal\",\r\n left:\r\n legend?.position === \"left\"\r\n ? \"left\"\r\n : legend?.position === \"right\"\r\n ? \"right\"\r\n : \"center\",\r\n top: legendAtTop ? 4 : undefined,\r\n bottom: legendAtBottom ? 2 : undefined,\r\n padding: [2, 6],\r\n itemGap: 8,\r\n itemWidth: 12,\r\n itemHeight: 6,\r\n textStyle: {\r\n ...theme.legend.textStyle,\r\n fontSize: 10, // Ultra-compact for maximum chart space\r\n },\r\n inactiveColor: theme.legend.inactiveColor,\r\n selectedMode: legend?.interactive !== false,\r\n }\r\n : { show: false },\r\n\r\n // Grid - minimal spacing for maximum chart plotting area\r\n // When compactAxes is true, axis names are hidden so we need minimal padding\r\n // containLabel: true ensures axis tick labels don't get cut off\r\n // Extra space only needed for slider zoom (inside zoom is invisible)\r\n grid: needsGrid\r\n ? {\r\n left: compactAxes ? 8 : 40,\r\n right:\r\n Array.isArray(yAxis) && yAxis.length > 1\r\n ? compactAxes\r\n ? 8\r\n : 40\r\n // A mark line labelled \"end\" reads its value off to the right\r\n // of the plot, past the axis — the 8px this chart otherwise\r\n // reserves is enough for the axis alone and clips the label's\r\n // own box.\r\n : hasEndLabelledMarkLine\r\n ? 70\r\n : 8,\r\n top: hasTitle\r\n ? hasSubtitle\r\n ? 50\r\n : 36\r\n : legendAtTop && showLegend\r\n ? 26\r\n : 4,\r\n bottom:\r\n (legendAtBottom && showLegend ? 26 : 16) +\r\n (zoom?.type === \"slider\" || zoom?.type === \"both\" ? 26 : 0),\r\n containLabel: true,\r\n }\r\n : undefined,\r\n\r\n // X Axis - compact font sizes for more chart space\r\n // In compact mode, hide axis name (shown in tooltip instead)\r\n xAxis: needsGrid\r\n ? {\r\n type: (xAxis?.type || \"time\") as\r\n | \"value\"\r\n | \"category\"\r\n | \"time\"\r\n | \"log\",\r\n name: compactAxes ? undefined : xAxis?.name,\r\n nameLocation: xAxis?.nameLocation || \"middle\",\r\n nameGap: 18,\r\n nameTextStyle: {\r\n ...theme.xAxis.nameTextStyle,\r\n fontSize: 10, // Ultra-compact for maximum chart space\r\n fontWeight: 500,\r\n },\r\n min: xAxis?.min,\r\n max: xAxis?.max,\r\n axisLine: {\r\n show: xAxis?.showLine !== false,\r\n lineStyle: theme.xAxis.axisLine.lineStyle,\r\n },\r\n axisTick: {\r\n lineStyle: theme.xAxis.axisTick.lineStyle,\r\n },\r\n axisLabel: {\r\n ...theme.xAxis.axisLabel,\r\n fontSize: 10, // Ultra-compact for maximum chart space\r\n // A caller's own spec wins over the shared theme, for this\r\n // axis only — see `labelStyle` on AxisOptions.\r\n ...xAxis?.labelStyle,\r\n formatter: xAxis?.labelFormatter as unknown as undefined,\r\n // Prevent label overlap - auto-hide overlapping labels\r\n hideOverlap: true,\r\n // Rotate labels slightly if needed for better fit\r\n rotate: 0,\r\n // Show fewer labels when space is tight\r\n interval: \"auto\",\r\n },\r\n splitLine: {\r\n show: xAxis?.showGrid !== false,\r\n lineStyle: {\r\n ...theme.xAxis.splitLine.lineStyle,\r\n type:\r\n (theme.xAxis.splitLine.lineStyle.type as\r\n | \"solid\"\r\n | \"dashed\"\r\n | \"dotted\") ?? \"solid\",\r\n ...xAxis?.gridStyle,\r\n },\r\n },\r\n inverse: xAxis?.inverse,\r\n }\r\n : undefined,\r\n\r\n // Y Axis - ultra-compact font sizes for maximum chart space\r\n // In compact mode, hide axis name (shown in tooltip instead)\r\n yAxis: (needsGrid\r\n ? (Array.isArray(yAxis) ? yAxis : [yAxis || {}]).map((axis, i) => ({\r\n type: (axis?.type || \"value\") as\r\n | \"value\"\r\n | \"category\"\r\n | \"time\"\r\n | \"log\",\r\n name: compactAxes ? undefined : axis?.name,\r\n nameLocation: axis?.nameLocation || \"middle\",\r\n nameGap: 28,\r\n nameTextStyle: {\r\n ...theme.yAxis.nameTextStyle,\r\n fontSize: 10, // Ultra-compact for maximum chart space\r\n fontWeight: 500,\r\n },\r\n position: axis?.position || (i === 0 ? \"left\" : \"right\"),\r\n min: axis?.min,\r\n max: axis?.max,\r\n splitNumber: axis?.splitNumber,\r\n axisLine: {\r\n show: axis?.showLine !== false,\r\n lineStyle: theme.yAxis.axisLine.lineStyle,\r\n },\r\n axisTick: {\r\n lineStyle: theme.yAxis.axisTick.lineStyle,\r\n },\r\n axisLabel: {\r\n ...theme.yAxis.axisLabel,\r\n fontSize: 10, // Ultra-compact for maximum chart space\r\n ...axis?.labelStyle,\r\n formatter: axis?.labelFormatter\r\n ? (axis.labelFormatter as unknown as undefined)\r\n : axis?.unit\r\n ? (v: number) => `${v}${axis.unit}`\r\n : undefined,\r\n },\r\n splitLine: {\r\n show: axis?.showGrid !== false,\r\n lineStyle: { ...theme.yAxis.splitLine.lineStyle, ...axis?.gridStyle },\r\n },\r\n inverse: axis?.inverse,\r\n }))\r\n : undefined) as EChartsOption[\"yAxis\"],\r\n\r\n // Radar (for radar charts) - transparent background for glassmorphic look\r\n radar: isPolar\r\n ? {\r\n indicator:\r\n series[0]?.data?.map((d: unknown) => {\r\n const point = d as DataPoint;\r\n return {\r\n name: point.label || point.category || String(point.x),\r\n max: 100,\r\n };\r\n }) || [],\r\n splitArea: {\r\n show: true,\r\n areaStyle: {\r\n color: [\r\n \"transparent\",\r\n \"transparent\",\r\n \"transparent\",\r\n \"transparent\",\r\n \"transparent\",\r\n ],\r\n },\r\n },\r\n splitLine: {\r\n lineStyle: {\r\n color: theme.xAxis.splitLine.lineStyle.color,\r\n opacity: 0.4,\r\n },\r\n },\r\n axisLine: {\r\n lineStyle: {\r\n color: theme.xAxis.axisLine.lineStyle.color,\r\n opacity: 0.4,\r\n },\r\n },\r\n axisName: {\r\n color: theme.xAxis.axisLabel.color,\r\n fontSize: 12,\r\n },\r\n }\r\n : undefined,\r\n\r\n // Data Zoom - inside zoom enabled by default for mouse wheel/pinch\r\n // Use zoom={{ enabled: false }} to disable, or zoom={{ type: 'slider' }} for slider\r\n dataZoom:\r\n zoom?.enabled === false\r\n ? undefined\r\n : [\r\n // Inside zoom (mouse wheel / pinch) - ENABLED BY DEFAULT\r\n ...(zoom?.type === \"inside\" || zoom?.type === \"both\" || !zoom?.type\r\n ? [\r\n {\r\n type: \"inside\" as const,\r\n xAxisIndex:\r\n zoom?.axis === \"x\" || zoom?.axis === \"both\" || !zoom?.axis\r\n ? 0\r\n : undefined,\r\n yAxisIndex:\r\n zoom?.axis === \"y\" || zoom?.axis === \"both\"\r\n ? 0\r\n : undefined,\r\n start: zoom?.start ?? 0,\r\n end: zoom?.end ?? 100,\r\n minSpan: zoom?.minSpan,\r\n maxSpan: zoom?.maxSpan,\r\n // Smooth zoom animation\r\n throttle: 50,\r\n },\r\n ]\r\n : []),\r\n // Slider zoom (only when explicitly requested)\r\n ...(zoom?.type === \"slider\" || zoom?.type === \"both\"\r\n ? [\r\n {\r\n type: \"slider\" as const,\r\n show: true,\r\n xAxisIndex:\r\n zoom?.axis === \"x\" || zoom?.axis === \"both\"\r\n ? 0\r\n : undefined,\r\n yAxisIndex:\r\n zoom?.axis === \"y\" || zoom?.axis === \"both\"\r\n ? 0\r\n : undefined,\r\n start: zoom?.start ?? 0,\r\n end: zoom?.end ?? 100,\r\n minSpan: zoom?.minSpan,\r\n maxSpan: zoom?.maxSpan,\r\n backgroundColor: theme.dataZoom.backgroundColor,\r\n borderColor: theme.dataZoom.borderColor,\r\n fillerColor: theme.dataZoom.fillerColor,\r\n handleStyle: { color: theme.dataZoom.handleColor },\r\n textStyle: theme.dataZoom.textStyle,\r\n height: 25,\r\n bottom: 10,\r\n },\r\n ]\r\n : []),\r\n ],\r\n\r\n // Toolbox (export) – always use custom dropdown for consistent UI\r\n toolbox: props.export?.enabled\r\n ? (() => {\r\n return {\r\n show: false, // Hide ECharts toolbox, use custom export button instead\r\n right: 20,\r\n top: 10,\r\n feature: {\r\n dataView: props.export.formats?.includes(\"json\")\r\n ? {\r\n show: true,\r\n readOnly: true,\r\n }\r\n : undefined,\r\n },\r\n iconStyle: {\r\n borderColor: theme.textStyle.color,\r\n },\r\n };\r\n })()\r\n : undefined,\r\n\r\n // Series\r\n series: transformSeriesToECharts(series, type, theme),\r\n\r\n // Animation\r\n animation: !loading,\r\n animationDuration: 300,\r\n animationEasing: \"cubicOut\",\r\n };\r\n\r\n // Add mark lines (thresholds)\r\n if (annotations?.markLines && options.series) {\r\n const seriesArray = options.series as Array<Record<string, unknown>>;\r\n if (seriesArray.length > 0) {\r\n seriesArray[0].markLine = {\r\n silent: true,\r\n symbol: \"none\",\r\n data: annotations.markLines.data.map((line) => ({\r\n ...line,\r\n lineStyle: {\r\n color: line.color || theme.color[2],\r\n type: line.lineStyle?.type || \"dashed\",\r\n width: line.lineStyle?.width || 1,\r\n },\r\n label: {\r\n show: !!line.label,\r\n formatter: line.label,\r\n position: line.labelPosition || \"middle\", // center by default, so the label doesn't overlap the y-axis at bottom\r\n distance: 8,\r\n color: theme.textStyle.color,\r\n fontWeight: 600,\r\n fontSize: 11,\r\n textBorderWidth: 0,\r\n textBorderColor: \"transparent\",\r\n backgroundColor: theme.tooltip.backgroundColor,\r\n borderColor: line.color || theme.color[2],\r\n borderWidth: 1,\r\n borderRadius: 4,\r\n padding: [2, 6],\r\n },\r\n })),\r\n };\r\n }\r\n }\r\n\r\n // Add mark areas (regions) — label style: no white border, crisp and slightly faded; optional icon\r\n if (annotations?.markAreas && options.series) {\r\n const seriesArray = options.series as Array<Record<string, unknown>>;\r\n const axisLabelColor =\r\n (theme.xAxis as { axisLabel?: { color?: string } })?.axisLabel?.color ??\r\n theme.textStyle?.color ??\r\n \"#94a3b8\";\r\n const fadedColor = axisLabelColor.startsWith(\"rgba\")\r\n ? axisLabelColor\r\n : `${axisLabelColor}cc`;\r\n const defaultLabelStyle = {\r\n show: true,\r\n color: fadedColor,\r\n fontSize: 11,\r\n fontWeight: \"500\" as const,\r\n textBorderWidth: 0,\r\n textBorderColor: \"transparent\",\r\n formatter: (params: { name?: string; value?: unknown }) => {\r\n const name =\r\n (params?.name ?? \"\") ||\r\n (typeof params?.value === \"string\" ? params.value : \"\");\r\n if (!name) return \"\";\r\n const icon = name.toLowerCase().includes(\"eclipse\") ? \"\\u25D0 \" : \"\"; // ◐ symbol for eclipse\r\n return icon ? `${icon}${name}` : name;\r\n },\r\n };\r\n if (seriesArray.length > 0) {\r\n seriesArray[0].markArea = {\r\n silent: true,\r\n data: annotations.markAreas.data.map(\r\n (\r\n pair: [\r\n { xAxis?: number | string; yAxis?: number; name?: string },\r\n { xAxis?: number | string; yAxis?: number },\r\n ],\r\n ) => {\r\n const [start, end] = pair;\r\n return [\r\n {\r\n ...start,\r\n label: {\r\n ...defaultLabelStyle,\r\n formatter: defaultLabelStyle.formatter,\r\n },\r\n },\r\n end,\r\n ];\r\n },\r\n ),\r\n itemStyle: annotations.markAreas.itemStyle,\r\n };\r\n }\r\n }\r\n\r\n // Merge custom options\r\n if (props.echartsOptions) {\r\n return mergeOptions(options, props.echartsOptions);\r\n }\r\n\r\n return options;\r\n}\r\n\r\nfunction mergeOptions(\r\n base: EChartsOption,\r\n custom: Partial<EChartsOption>,\r\n): EChartsOption {\r\n // Smart series merging logic\r\n let mergedSeries = base.series;\r\n\r\n if (custom.series) {\r\n const baseSeries =\r\n (base.series as Array<{ type?: string; data?: unknown[] }>) || [];\r\n const customSeries = custom.series as unknown[];\r\n\r\n // Check if all base series are empty (no data or empty data array)\r\n const allBaseSeriesEmpty =\r\n baseSeries.length === 0 ||\r\n baseSeries.every((s) => {\r\n if (!s) return true;\r\n if (!s.data) return true;\r\n if (Array.isArray(s.data) && s.data.length === 0) return true;\r\n return false;\r\n });\r\n\r\n // Single full override: presets (e.g. GaugeChart) pass one full series config; use it only so we don't render two gauges\r\n const baseLen = baseSeries.length;\r\n const customLen = customSeries.length;\r\n if (baseLen === 1 && customLen === 1) {\r\n const b = baseSeries[0];\r\n const c = customSeries[0] as { type?: string };\r\n if (b?.type === \"gauge\" && c?.type === \"gauge\") {\r\n mergedSeries = customSeries as EChartsOption[\"series\"];\r\n } else if (allBaseSeriesEmpty) {\r\n mergedSeries = customSeries as EChartsOption[\"series\"];\r\n } else {\r\n mergedSeries = [\r\n ...baseSeries,\r\n ...customSeries,\r\n ] as EChartsOption[\"series\"];\r\n }\r\n } else if (allBaseSeriesEmpty) {\r\n mergedSeries = customSeries as EChartsOption[\"series\"];\r\n } else {\r\n mergedSeries = [\r\n ...baseSeries,\r\n ...customSeries,\r\n ] as EChartsOption[\"series\"];\r\n }\r\n }\r\n\r\n return {\r\n ...base,\r\n ...custom,\r\n series: mergedSeries,\r\n };\r\n}\r\n\r\n// =============================================================================\r\n// Main Component\r\n// =============================================================================\r\n\r\nexport const AstroChart = memo(\r\n forwardRef<AstroChartHandle, AstroChartProps>(\r\n function AstroChart(props, ref) {\r\n const {\r\n width = \"100%\",\r\n height = 400,\r\n loading = false,\r\n emptyMessage = \"No data available\",\r\n className = \"\",\r\n style,\r\n ariaLabel,\r\n onChartReady,\r\n onClick,\r\n onHover,\r\n onZoomChange,\r\n onBrushSelect,\r\n realTime,\r\n series = [],\r\n infoTooltip,\r\n // Header icon/status system\r\n icon,\r\n iconStatus,\r\n statusMessage,\r\n showStatusBadge = false,\r\n } = props;\r\n\r\n const chartRef = useRef<ReactEChartsCore>(null);\r\n const { tokens, mode, theme: themeVariant } = useTheme();\r\n\r\n // Detect transparent theme for glassmorphic styling\r\n const isTransparentTheme =\r\n themeVariant === \"transparent\" ||\r\n themeVariant === \"transparent-bold\" ||\r\n themeVariant === \"transparent-minimal\";\r\n const isBoldVariant = themeVariant === \"transparent-bold\";\r\n const isMinimalVariant = themeVariant === \"transparent-minimal\";\r\n\r\n // Get card border style based on accentBorders setting\r\n const cardBorderStyle = useCardBorderStyle(tokens, isTransparentTheme);\r\n\r\n // Glass accent colors for bold/minimal themes (matching Container)\r\n const glassAccentColor = tokens.colors.accent.primary;\r\n const glassAccentMuted = `${glassAccentColor}66`;\r\n\r\n const headingConfig = tokens.layout?.card?.heading ?? {\r\n iconSize: 20,\r\n iconSizeCompact: 16,\r\n titleFontSize: \"1.125rem\",\r\n titleFontSizeChart: \"0.875rem\",\r\n titleFontWeight: 500,\r\n gap: 8,\r\n };\r\n const chartTitleFontSize =\r\n headingConfig.titleFontSizeChart ?? headingConfig.titleFontSize;\r\n\r\n // Generate theme - uses official AstroUXDS colors for 'astro' theme, Zendir colors for others\r\n const theme = useMemo(\r\n () => createAstroEChartsTheme(tokens, mode, themeVariant),\r\n [tokens, mode, themeVariant],\r\n );\r\n\r\n // Build options - hide ECharts title whenever we show custom header (top-left, all themes)\r\n const hideEChartsTitle = !!(icon || props.title);\r\n const options = useMemo(\r\n () => buildEChartsOptions(props, theme, hideEChartsTitle),\r\n [props, theme, hideEChartsTitle],\r\n );\r\n\r\n // Get chart instance.\r\n // echarts-for-react types the instance via echarts/core's internal\r\n // EChartsType, while callers here consume the public `ECharts` type from\r\n // 'echarts'. They are the same runtime object, so bridge the two nominal\r\n // declarations at this single boundary.\r\n const getChart = useCallback((): ECharts | undefined => {\r\n return chartRef.current?.getEchartsInstance() as unknown as\r\n | ECharts\r\n | undefined;\r\n }, []);\r\n\r\n // Dynamic export filename from title, series names, chart type\r\n const getExportFileName = useCallback(\r\n (extension?: string, includeTimestamp = true) => {\r\n const context: ExportFileNameContext = {\r\n title: props.title,\r\n subtitle: props.subtitle,\r\n seriesNames: series.map((s) => s.name),\r\n chartType: props.type,\r\n };\r\n let base: string;\r\n if (props.export?.getExportFileName) {\r\n base = props.export.getExportFileName(context);\r\n } else if (props.export?.fileName) {\r\n base = props.export.fileName;\r\n } else {\r\n base = derivedExportBaseName(\r\n props.title,\r\n context.seriesNames,\r\n props.type,\r\n );\r\n }\r\n if (includeTimestamp) {\r\n const d = new Date();\r\n const ts = d\r\n .toISOString()\r\n .slice(0, 19)\r\n .replace(/[-T:]/g, \"\")\r\n .replace(/(\\d{8})(\\d{6})/, \"$1-$2\");\r\n base = `${base}-${ts}`;\r\n }\r\n if (extension) base = `${base}.${extension.replace(/^\\./, \"\")}`;\r\n return base;\r\n },\r\n [props.title, props.subtitle, props.type, props.export, series],\r\n );\r\n\r\n // Expose imperative handle\r\n useImperativeHandle(\r\n ref,\r\n () => ({\r\n getChart,\r\n resize: () => getChart()?.resize(),\r\n getExportFileName,\r\n exportImage: (type = \"png\", pixelRatio = 2) => {\r\n const chart = getChart();\r\n if (!chart) return undefined;\r\n const imgType = type === \"jpg\" ? \"jpeg\" : type;\r\n return chart.getDataURL({\r\n type: imgType as \"png\" | \"jpeg\" | \"svg\",\r\n pixelRatio,\r\n backgroundColor: \"transparent\",\r\n });\r\n },\r\n exportCSV: () => {\r\n const rows: string[] = [];\r\n const headers = [\"Index\", ...series.map((s) => s.name)];\r\n rows.push(headers.join(\",\"));\r\n\r\n const maxLen = Math.max(...series.map((s) => s.data?.length || 0));\r\n for (let i = 0; i < maxLen; i++) {\r\n const row = [i.toString()];\r\n for (const s of series) {\r\n const data = s.data as unknown[];\r\n const value = data?.[i];\r\n if (typeof value === \"number\") {\r\n row.push(value.toString());\r\n } else if (Array.isArray(value)) {\r\n row.push(value[1]?.toString() || \"\");\r\n } else if (value && typeof value === \"object\") {\r\n const point = value as DataPoint | TimeSeriesPoint;\r\n row.push(\r\n (\"value\" in point ? point.value : point.y)?.toString() ||\r\n \"\",\r\n );\r\n } else {\r\n row.push(\"\");\r\n }\r\n }\r\n rows.push(row.join(\",\"));\r\n }\r\n return rows.join(\"\\n\");\r\n },\r\n appendData: (\r\n seriesIndex: number,\r\n data: DataPoint | TimeSeriesPoint,\r\n ) => {\r\n const chart = getChart();\r\n if (!chart) return;\r\n\r\n // Get current data\r\n const option = chart.getOption();\r\n const seriesOpt = (option.series as Array<{ data: unknown[] }>)?.[\r\n seriesIndex\r\n ];\r\n if (!seriesOpt) return;\r\n\r\n const currentData = [...(seriesOpt.data || [])];\r\n const newPoint =\r\n \"time\" in data ? [data.time, data.value] : [data.x, data.y];\r\n\r\n currentData.push(newPoint);\r\n\r\n // Apply max points limit\r\n const maxPoints = realTime?.maxPoints || 1000;\r\n while (currentData.length > maxPoints) {\r\n currentData.shift();\r\n }\r\n\r\n // Update chart\r\n chart.setOption(\r\n {\r\n series: [\r\n {\r\n data: currentData,\r\n },\r\n ],\r\n },\r\n {\r\n replaceMerge: [\"series\"],\r\n },\r\n );\r\n },\r\n clearData: () => {\r\n const chart = getChart();\r\n if (!chart) return;\r\n\r\n chart.setOption({\r\n series: series.map(() => ({ data: [] })),\r\n });\r\n },\r\n dispatchAction: (action: unknown) => {\r\n getChart()?.dispatchAction(\r\n action as Parameters<ECharts[\"dispatchAction\"]>[0],\r\n );\r\n },\r\n setCursor: (value: number | null, cursorOptions) => {\r\n const chart = getChart();\r\n if (!chart) return;\r\n\r\n if (value === null) {\r\n chart.setOption({\r\n graphic: [{ id: CURSOR_ID, $action: \"remove\" }],\r\n } as EChartsOption);\r\n return;\r\n }\r\n\r\n // Pixel position has to come from the chart, since the axis may be\r\n // zoomed, reversed or log-scaled and the caller knows none of that.\r\n const x = chart.convertToPixel({ xAxisIndex: 0 }, value);\r\n if (typeof x !== \"number\" || Number.isNaN(x)) return;\r\n\r\n chart.setOption({\r\n graphic: [\r\n {\r\n id: CURSOR_ID,\r\n type: \"line\",\r\n // Above the series but not interactive: a cursor that\r\n // swallowed pointer events would break hovering the very\r\n // chart it sits on.\r\n z: 100,\r\n silent: true,\r\n shape: { x1: x, y1: 0, x2: x, y2: chart.getHeight() },\r\n style: {\r\n stroke: cursorOptions?.color ?? \"#8744F3\",\r\n lineWidth: 1.5,\r\n lineDash: [3, 2],\r\n },\r\n },\r\n ],\r\n } as EChartsOption);\r\n },\r\n }),\r\n [getChart, series, realTime],\r\n );\r\n\r\n // Zoom preservation: ReactEChartsCore re-renders with notMerge=true whenever\r\n // `options` changes (which happens on every series-data update). Without this,\r\n // streaming charts snap back to start=0/end=100 each frame, defeating user\r\n // pan/zoom.\r\n //\r\n // We capture user pan/zoom on the `datazoom` event and replay it after every\r\n // option update. For streaming time-series we MUST use absolute axis values\r\n // (startValue/endValue) — preserving percent (start/end) would slide the\r\n // window forward as new data extends the axis, which feels exactly like a\r\n // zoom reset from the operator's perspective. Percent is saved as a fallback\r\n // for category/non-numeric axes where absolute values aren't meaningful.\r\n //\r\n // `programmaticRef` suppresses the datazoom event we ourselves dispatch so\r\n // we don't overwrite the saved range with the values just replayed.\r\n type ZoomState = {\r\n start?: number;\r\n end?: number;\r\n startValue?: number | string;\r\n endValue?: number | string;\r\n };\r\n const userZoomRef = useRef<ZoomState | null>(null);\r\n const programmaticRef = useRef(false);\r\n\r\n // Event handlers\r\n const handleEvents = useMemo(() => {\r\n const events: Record<string, (params: unknown) => void> = {};\r\n\r\n if (onClick) {\r\n events.click = (params: unknown) => {\r\n onClick(params as ChartEventParams);\r\n };\r\n }\r\n\r\n if (onHover) {\r\n events.mouseover = (params: unknown) => {\r\n onHover(params as ChartEventParams);\r\n };\r\n }\r\n\r\n // Always wire datazoom so we can persist zoom state across data updates.\r\n // `onZoomChange` (if provided) is invoked alongside.\r\n events.datazoom = (params: unknown) => {\r\n if (programmaticRef.current) {\r\n // This event fired as a result of our own dispatchAction replay — ignore\r\n // so we don't keep overwriting the saved range from the user's last gesture.\r\n programmaticRef.current = false;\r\n return;\r\n }\r\n type DZParams = {\r\n start?: number;\r\n end?: number;\r\n startValue?: number | string;\r\n endValue?: number | string;\r\n batch?: Array<{\r\n start?: number;\r\n end?: number;\r\n startValue?: number | string;\r\n endValue?: number | string;\r\n }>;\r\n };\r\n const p = params as DZParams;\r\n const b = p.batch?.[0];\r\n const start = b?.start ?? p.start ?? 0;\r\n const end = b?.end ?? p.end ?? 100;\r\n const startValue = b?.startValue ?? p.startValue;\r\n const endValue = b?.endValue ?? p.endValue;\r\n // Only remember user-applied zoom; ignore the no-op full range so a\r\n // double-click \"reset\" returns to auto-follow behavior on the next update.\r\n const isFullRange = start <= 0 && end >= 100;\r\n userZoomRef.current = isFullRange\r\n ? null\r\n : { start, end, startValue, endValue };\r\n if (onZoomChange) onZoomChange({ start, end });\r\n };\r\n\r\n if (onBrushSelect) {\r\n events.brushselected = (params: unknown) => {\r\n onBrushSelect(\r\n params as Parameters<NonNullable<typeof onBrushSelect>>[0],\r\n );\r\n };\r\n }\r\n\r\n return events;\r\n }, [onClick, onHover, onZoomChange, onBrushSelect]);\r\n\r\n // After every options update, replay the user's zoom selection so streaming\r\n // data doesn't reset their view. Prefer absolute axis values when available\r\n // so the window stays anchored to the time range the user picked, not a\r\n // percentage that slides as more data arrives.\r\n useEffect(() => {\r\n const saved = userZoomRef.current;\r\n if (!saved) return;\r\n const chart = getChart() as\r\n | (ECharts & { isDisposed?: () => boolean })\r\n | undefined;\r\n // Guard against firing into a disposed instance — happens when parents\r\n // toggle the chart in/out (e.g. `series.length > 0 ? <AstroChart/> : ...`)\r\n // and React's effect runs while the previous instance is being torn down.\r\n if (\r\n !chart ||\r\n (typeof chart.isDisposed === \"function\" && chart.isDisposed())\r\n )\r\n return;\r\n const hasAbsolute =\r\n saved.startValue !== undefined && saved.endValue !== undefined;\r\n programmaticRef.current = true;\r\n try {\r\n chart.dispatchAction(\r\n hasAbsolute\r\n ? {\r\n type: \"dataZoom\",\r\n startValue: saved.startValue,\r\n endValue: saved.endValue,\r\n }\r\n : { type: \"dataZoom\", start: saved.start, end: saved.end },\r\n );\r\n } catch {\r\n // Instance vanished between the isDisposed check and the call — ignore.\r\n programmaticRef.current = false;\r\n }\r\n }, [options, getChart]);\r\n\r\n // Chart ready callback\r\n const handleChartReady = useCallback(\r\n (chart: unknown) => {\r\n onChartReady?.(chart);\r\n },\r\n [onChartReady],\r\n );\r\n\r\n // Custom export dropdown: always show when export is enabled (for consistent UI)\r\n const exportFormats = props.export?.formats ?? [\"png\"];\r\n const useCustomExportDropdown = props.export?.enabled;\r\n const [exportMenuOpen, setExportMenuOpen] = useState(false);\r\n const exportMenuRef = useRef<HTMLDivElement>(null);\r\n\r\n // Info tooltip state\r\n const [infoTooltipOpen, setInfoTooltipOpen] = useState(false);\r\n const infoTooltipRef = useRef<HTMLDivElement>(null);\r\n\r\n // Close info tooltip on outside click\r\n useEffect(() => {\r\n if (!infoTooltip || !infoTooltipOpen) return;\r\n const handleClickOutside = (e: MouseEvent) => {\r\n if (\r\n infoTooltipRef.current &&\r\n !infoTooltipRef.current.contains(e.target as Node)\r\n ) {\r\n setInfoTooltipOpen(false);\r\n }\r\n };\r\n document.addEventListener(\"mousedown\", handleClickOutside);\r\n return () =>\r\n document.removeEventListener(\"mousedown\", handleClickOutside);\r\n }, [infoTooltip, infoTooltipOpen]);\r\n\r\n useEffect(() => {\r\n if (!useCustomExportDropdown || !exportMenuOpen) return;\r\n const handleClickOutside = (e: MouseEvent) => {\r\n if (\r\n exportMenuRef.current &&\r\n !exportMenuRef.current.contains(e.target as Node)\r\n ) {\r\n setExportMenuOpen(false);\r\n }\r\n };\r\n document.addEventListener(\"mousedown\", handleClickOutside);\r\n return () =>\r\n document.removeEventListener(\"mousedown\", handleClickOutside);\r\n }, [useCustomExportDropdown, exportMenuOpen]);\r\n\r\n const handleExportFormat = useCallback(\r\n (format: \"png\" | \"jpeg\" | \"svg\" | \"csv\") => {\r\n const chart = getChart();\r\n if (format === \"csv\") {\r\n const rows: string[] = [];\r\n const headers = [\"Index\", ...series.map((s) => s.name)];\r\n rows.push(headers.join(\",\"));\r\n const maxLen = Math.max(...series.map((s) => s.data?.length || 0));\r\n for (let i = 0; i < maxLen; i++) {\r\n const row = [i.toString()];\r\n for (const s of series) {\r\n const data = s.data as unknown[];\r\n const value = data?.[i];\r\n if (typeof value === \"number\") row.push(value.toString());\r\n else if (Array.isArray(value))\r\n row.push(value[1]?.toString() || \"\");\r\n else if (value && typeof value === \"object\") {\r\n const point = value as DataPoint | TimeSeriesPoint;\r\n row.push(\r\n (\"value\" in point ? point.value : point.y)?.toString() ||\r\n \"\",\r\n );\r\n } else row.push(\"\");\r\n }\r\n rows.push(row.join(\",\"));\r\n }\r\n const csv = rows.join(\"\\n\");\r\n const filename = getExportFileName(\"csv\", true);\r\n const blob = new Blob([csv], { type: \"text/csv;charset=utf-8\" });\r\n const url = URL.createObjectURL(blob);\r\n const a = document.createElement(\"a\");\r\n a.href = url;\r\n a.download = filename;\r\n a.click();\r\n URL.revokeObjectURL(url);\r\n } else if (chart) {\r\n const imgType = format === \"jpeg\" ? \"jpeg\" : format;\r\n const dataUrl = chart.getDataURL({\r\n type: imgType as \"png\" | \"jpeg\" | \"svg\",\r\n pixelRatio: props.export?.pixelRatio || 2,\r\n backgroundColor: props.export?.backgroundColor || \"transparent\",\r\n });\r\n if (dataUrl) {\r\n const ext = format === \"jpeg\" ? \"jpg\" : format;\r\n const filename = getExportFileName(ext, true);\r\n const a = document.createElement(\"a\");\r\n a.href = dataUrl;\r\n a.download = filename;\r\n a.click();\r\n }\r\n }\r\n setExportMenuOpen(false);\r\n },\r\n [\r\n getChart,\r\n series,\r\n getExportFileName,\r\n props.export?.pixelRatio,\r\n props.export?.backgroundColor,\r\n ],\r\n );\r\n\r\n // Resize observer (guard so we never resize after unmount/dispose)\r\n useEffect(() => {\r\n const chart = getChart();\r\n if (!chart) return;\r\n\r\n let mounted = true;\r\n const resizeObserver = new ResizeObserver(() => {\r\n if (!mounted) return;\r\n try {\r\n chart.resize();\r\n } catch {\r\n // Chart may be disposed; ignore\r\n }\r\n });\r\n\r\n const container = chartRef.current?.ele;\r\n if (container) {\r\n resizeObserver.observe(container);\r\n }\r\n\r\n return () => {\r\n mounted = false;\r\n resizeObserver.disconnect();\r\n };\r\n }, [getChart]);\r\n\r\n // echarts-gl 3D series require canvas renderer (SVG mode crashes with isSingleCanvas error)\r\n const needs3DCanvas = useMemo(() => {\r\n const eo = props.echartsOptions;\r\n if (!eo) return false;\r\n if (\r\n eo.grid3D ||\r\n eo.xAxis3D ||\r\n eo.yAxis3D ||\r\n eo.zAxis3D ||\r\n eo.globe ||\r\n eo.geo3D\r\n )\r\n return true;\r\n if (Array.isArray(eo.series)) {\r\n return eo.series.some((s: any) => {\r\n const t = s?.type || \"\";\r\n return (\r\n t.includes(\"3D\") ||\r\n t.includes(\"3d\") ||\r\n t === \"scatter3D\" ||\r\n t === \"bar3D\" ||\r\n t === \"surface\" ||\r\n t === \"lines3D\"\r\n );\r\n });\r\n }\r\n return false;\r\n }, [props.echartsOptions]);\r\n\r\n // Check if data is empty (consider both series prop and echartsOptions.series)\r\n const hasSeriesData = series.some((s) => s.data && s.data.length > 0);\r\n const hasEchartsOptionsSeries =\r\n props.echartsOptions?.series &&\r\n (Array.isArray(props.echartsOptions.series)\r\n ? props.echartsOptions.series.length > 0\r\n : props.echartsOptions.series !== undefined);\r\n const hasData = hasSeriesData || hasEchartsOptionsSeries;\r\n\r\n // Status color mapping for header icon\r\n const getStatusColor = (status: ChartStatus | undefined): string => {\r\n switch (status) {\r\n case \"critical\":\r\n return tokens.colors.status?.critical || \"#ff3838\";\r\n case \"serious\":\r\n return tokens.colors.status?.serious || \"#ffb302\";\r\n case \"caution\":\r\n return tokens.colors.status?.caution || \"#fce83a\";\r\n case \"normal\":\r\n return tokens.colors.status?.normal || \"#56f000\";\r\n case \"standby\":\r\n return tokens.colors.status?.standby || \"#2dccff\";\r\n case \"off\":\r\n return tokens.colors.status?.off || \"#a4abb6\";\r\n default:\r\n return tokens.colors.text.secondary;\r\n }\r\n };\r\n\r\n // Check if we need to render the header (has icon or title)\r\n const hasHeader = !!(icon || props.title);\r\n\r\n // Container styles - glassmorphic for transparent themes (matching Container component)\r\n // Bold and Minimal themes get special glass styling\r\n const useGlassMode = isBoldVariant || isMinimalVariant;\r\n\r\n const containerStyle: React.CSSProperties = {\r\n width: typeof width === \"number\" ? `${width}px` : width,\r\n height: typeof height === \"number\" ? `${height}px` : height,\r\n borderRadius: tokens.borderRadius.lg,\r\n padding: tokens.spacing.sm,\r\n position: \"relative\",\r\n overflow: \"hidden\",\r\n // Glass mode styling for bold/minimal (matching Container)\r\n ...(useGlassMode\r\n ? {\r\n backgroundColor: \"rgba(10, 15, 25, 0.35)\",\r\n backdropFilter: \"blur(12px)\",\r\n WebkitBackdropFilter: \"blur(12px)\",\r\n border: `1px solid ${glassAccentMuted}`,\r\n borderTop: isBoldVariant\r\n ? `2px solid ${glassAccentColor}`\r\n : `1px solid ${glassAccentColor}`,\r\n transition: \"all 0.3s ease-out, box-shadow 0.3s ease-out\",\r\n }\r\n : isTransparentTheme\r\n ? {\r\n // Regular transparent (glass) theme — match top cards (ISS): transparent + blur\r\n backgroundColor: \"transparent\",\r\n backdropFilter: \"blur(12px)\",\r\n WebkitBackdropFilter: \"blur(12px)\",\r\n ...cardBorderStyle,\r\n }\r\n : {\r\n // Non-transparent themes — use same border as Container (muted) so chart doesn't stand out\r\n backgroundColor: tokens.colors.background.surface,\r\n border: `1px solid ${tokens.colors.border.muted}`,\r\n }),\r\n ...style,\r\n };\r\n\r\n // Glass gradient for bold/minimal themes\r\n const glassGradient = useGlassMode\r\n ? `linear-gradient(135deg, ${glassAccentColor}10 0%, ${glassAccentColor}03 100%)`\r\n : undefined;\r\n\r\n // Empty state\r\n if (!hasData && !loading) {\r\n return (\r\n <div\r\n className={`astro-chart astro-chart--empty ${className}`}\r\n style={containerStyle}\r\n role=\"group\"\r\n aria-roledescription=\"chart\"\r\n aria-label={ariaLabel || \"Empty chart\"}\r\n >\r\n {/* Glass gradient overlay for bold/minimal themes */}\r\n {useGlassMode && glassGradient && (\r\n <div\r\n style={{\r\n position: \"absolute\",\r\n inset: 0,\r\n background: glassGradient,\r\n pointerEvents: \"none\",\r\n zIndex: 0,\r\n }}\r\n />\r\n )}\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n height: \"100%\",\r\n color: tokens.colors.text.secondary,\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n fontSize: tokens.typography.fontSize.sm,\r\n position: \"relative\",\r\n zIndex: 1,\r\n }}\r\n >\r\n {emptyMessage}\r\n </div>\r\n </div>\r\n );\r\n }\r\n\r\n const exportImageFormats = exportFormats.filter((f) =>\r\n [\"png\", \"jpeg\", \"jpg\", \"svg\"].includes(f),\r\n );\r\n const hasCsv = exportFormats.includes(\"csv\");\r\n\r\n // Calculate header height for chart area adjustment\r\n const _headerHeight = icon && iconStatus ? 32 : 0;\r\n\r\n return (\r\n <div\r\n className={`astro-chart ${className}`}\r\n style={containerStyle}\r\n role=\"group\"\r\n aria-roledescription=\"chart\"\r\n aria-label={ariaLabel || props.title || \"Chart\"}\r\n >\r\n {/* Glass gradient overlay for bold/minimal themes (matching Container) */}\r\n {useGlassMode && glassGradient && (\r\n <div\r\n style={{\r\n position: \"absolute\",\r\n inset: 0,\r\n background: glassGradient,\r\n pointerEvents: \"none\",\r\n zIndex: 0,\r\n }}\r\n />\r\n )}\r\n\r\n {/* Header - BOLD variant (matching Container) */}\r\n {hasHeader && isBoldVariant && (\r\n <div style={{ position: \"relative\", zIndex: 1 }}>\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"space-between\",\r\n padding: `12px ${tokens.spacing.sm} 0`,\r\n }}\r\n >\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: headingConfig.gap,\r\n }}\r\n >\r\n {/* Icon with status badge - card heading system */}\r\n {icon && iconStatus && (\r\n <HeaderIconWithStatus\r\n icon={icon as AstroIconName}\r\n size={headingConfig.iconSize}\r\n status={iconStatus}\r\n statusColor={getStatusColor(iconStatus)}\r\n />\r\n )}\r\n {icon && !iconStatus && (\r\n <AstroIcon\r\n name={icon as AstroIconName}\r\n size={headingConfig.iconSize}\r\n color={glassAccentColor}\r\n />\r\n )}\r\n {props.title && (\r\n <h3\r\n style={{\r\n margin: 0,\r\n fontSize: chartTitleFontSize,\r\n fontWeight: headingConfig.titleFontWeight,\r\n color: glassAccentColor,\r\n letterSpacing: \"0.5px\",\r\n textShadow: `0 0 20px ${glassAccentMuted}`,\r\n }}\r\n >\r\n {props.title}\r\n </h3>\r\n )}\r\n </div>\r\n </div>\r\n {/* Accent decorative line with status badge */}\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: \"8px\",\r\n padding: `10px ${tokens.spacing.sm} 0`,\r\n marginBottom: tokens.spacing.xs,\r\n }}\r\n >\r\n <div\r\n style={{\r\n flex: 1,\r\n height: \"2px\",\r\n background: `linear-gradient(90deg, ${glassAccentColor} 0%, ${glassAccentMuted} 50%, transparent 100%)`,\r\n boxShadow: `0 0 8px ${glassAccentMuted}`,\r\n }}\r\n />\r\n {/* Status badge at end of line (optional) */}\r\n {showStatusBadge && iconStatus && (\r\n <span\r\n style={{\r\n display: \"inline-flex\",\r\n alignItems: \"center\",\r\n gap: \"4px\",\r\n padding: \"2px 8px\",\r\n fontSize: \"0.625rem\", // 10px in rem\r\n fontWeight: 500, // AstroUXDS medium (not 600)\r\n letterSpacing: \"0.05em\",\r\n color: getStatusColor(iconStatus),\r\n backgroundColor: `${getStatusColor(iconStatus)}20`,\r\n border: `1px solid ${getStatusColor(iconStatus)}40`,\r\n borderRadius: \"3px\",\r\n whiteSpace: \"nowrap\",\r\n }}\r\n >\r\n {STATUS_SHAPES[iconStatus](6, getStatusColor(iconStatus))}\r\n {STATUS_LABELS[iconStatus]}\r\n </span>\r\n )}\r\n </div>\r\n </div>\r\n )}\r\n\r\n {/* Header - MINIMAL variant (inline thin line, matching Container) */}\r\n {hasHeader && isMinimalVariant && (\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n padding: `${tokens.spacing.xs} ${tokens.spacing.sm} 0`,\r\n marginBottom: tokens.spacing.xs,\r\n position: \"relative\",\r\n zIndex: 1,\r\n }}\r\n >\r\n {icon && iconStatus && (\r\n <HeaderIconWithStatus\r\n icon={icon as AstroIconName}\r\n size={headingConfig.iconSizeCompact}\r\n status={iconStatus}\r\n statusColor={getStatusColor(iconStatus)}\r\n style={{ marginRight: tokens.spacing.sm }}\r\n />\r\n )}\r\n {icon && !iconStatus && (\r\n <AstroIcon\r\n name={icon as AstroIconName}\r\n size={headingConfig.iconSizeCompact}\r\n color={glassAccentColor}\r\n style={{ marginRight: tokens.spacing.sm }}\r\n />\r\n )}\r\n {props.title && (\r\n <h3\r\n style={{\r\n margin: 0,\r\n fontSize: chartTitleFontSize,\r\n fontWeight: headingConfig.titleFontWeight,\r\n color: glassAccentColor,\r\n textShadow: `0 0 16px ${glassAccentMuted}`,\r\n letterSpacing: \"0.02em\",\r\n whiteSpace: \"nowrap\",\r\n marginRight: \"12px\",\r\n }}\r\n >\r\n {props.title}\r\n </h3>\r\n )}\r\n {/* Accent line with status badge */}\r\n <div\r\n style={{\r\n flex: 1,\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: headingConfig.gap,\r\n minWidth: 40,\r\n }}\r\n >\r\n <div\r\n style={{\r\n flex: 1,\r\n height: \"1px\",\r\n background: `linear-gradient(90deg, ${glassAccentColor} 0%, ${glassAccentMuted} 60%, transparent 100%)`,\r\n }}\r\n />\r\n {/* Status badge at end of line (optional) */}\r\n {showStatusBadge && iconStatus && (\r\n <span\r\n style={{\r\n display: \"inline-flex\",\r\n alignItems: \"center\",\r\n gap: \"3px\",\r\n padding: \"1px 6px\",\r\n fontSize: \"0.5625rem\", // 9px in rem (micro)\r\n fontWeight: 500, // AstroUXDS medium (not 600)\r\n letterSpacing: \"0.05em\",\r\n color: getStatusColor(iconStatus),\r\n backgroundColor: `${getStatusColor(iconStatus)}15`,\r\n border: `1px solid ${getStatusColor(iconStatus)}30`,\r\n borderRadius: \"2px\",\r\n whiteSpace: \"nowrap\",\r\n }}\r\n >\r\n {STATUS_SHAPES[iconStatus](5, getStatusColor(iconStatus))}\r\n {STATUS_LABELS[iconStatus]}\r\n </span>\r\n )}\r\n </div>\r\n </div>\r\n )}\r\n\r\n {/* Header - Regular glass theme (no box/border) */}\r\n {hasHeader && isTransparentTheme && !useGlassMode && (\r\n <div\r\n title={statusMessage}\r\n style={{\r\n position: \"absolute\",\r\n top: 6,\r\n left: 8,\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: headingConfig.gap,\r\n zIndex: 10,\r\n }}\r\n >\r\n {icon && iconStatus && (\r\n <HeaderIconWithStatus\r\n icon={icon as AstroIconName}\r\n size={headingConfig.iconSizeCompact}\r\n status={iconStatus}\r\n statusColor={getStatusColor(iconStatus)}\r\n />\r\n )}\r\n {icon && !iconStatus && (\r\n <AstroIcon\r\n name={icon as AstroIconName}\r\n size={headingConfig.iconSizeCompact}\r\n color={tokens.colors.text.tertiary}\r\n />\r\n )}\r\n {props.title && (\r\n <span\r\n style={{\r\n fontSize: chartTitleFontSize,\r\n fontWeight: headingConfig.titleFontWeight,\r\n color: tokens.colors.text.primary,\r\n }}\r\n >\r\n {props.title}\r\n </span>\r\n )}\r\n </div>\r\n )}\r\n\r\n {/* Header - Non-transparent themes (Zen/Astro): same top-left layout, no box/border */}\r\n {hasHeader && !isTransparentTheme && (\r\n <div\r\n title={statusMessage}\r\n style={{\r\n position: \"absolute\",\r\n top: 6,\r\n left: 8,\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: headingConfig.gap,\r\n zIndex: 10,\r\n }}\r\n >\r\n {icon && iconStatus && (\r\n <HeaderIconWithStatus\r\n icon={icon as AstroIconName}\r\n size={headingConfig.iconSizeCompact}\r\n status={iconStatus}\r\n statusColor={getStatusColor(iconStatus)}\r\n />\r\n )}\r\n {icon && !iconStatus && (\r\n <AstroIcon\r\n name={icon as AstroIconName}\r\n size={headingConfig.iconSizeCompact}\r\n color={tokens.colors.text.tertiary}\r\n />\r\n )}\r\n {props.title && (\r\n <span\r\n style={{\r\n fontSize: chartTitleFontSize,\r\n fontWeight: headingConfig.titleFontWeight,\r\n color: tokens.colors.text.primary,\r\n }}\r\n >\r\n {props.title}\r\n </span>\r\n )}\r\n </div>\r\n )}\r\n {/* SVG renderer by default for sharp text; canvas is forced when echarts-gl 3D series are present (gl requires canvas) */}\r\n <ReactEChartsCore\r\n ref={chartRef}\r\n echarts={echarts}\r\n option={options}\r\n opts={{\r\n renderer: props.renderer ?? (needs3DCanvas ? \"canvas\" : \"svg\"),\r\n ...((props.renderer === \"canvas\" || needs3DCanvas) &&\r\n typeof window !== \"undefined\" && {\r\n devicePixelRatio: window.devicePixelRatio || 1,\r\n }),\r\n }}\r\n style={{ width: \"100%\", height: \"100%\" }}\r\n notMerge={true}\r\n lazyUpdate={true}\r\n showLoading={loading}\r\n loadingOption={{\r\n text: \"Loading...\",\r\n color: tokens.colors.accent.primary,\r\n textColor: tokens.colors.text.primary,\r\n // Hex-alpha suffixing only works on hex tokens; surface is an\r\n // rgba() string in several themes, so use the overlay token,\r\n // which is defined with mask-appropriate alpha in every theme.\r\n maskColor: isTransparentTheme\r\n ? \"rgba(10, 15, 25, 0.6)\"\r\n : tokens.colors.background.overlay,\r\n }}\r\n onEvents={handleEvents}\r\n onChartReady={handleChartReady}\r\n />\r\n {/* Chart toolbar - compact, positioned top right */}\r\n <div\r\n style={{\r\n position: \"absolute\",\r\n right: 8,\r\n top: 6,\r\n zIndex: 10,\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: 4,\r\n }}\r\n >\r\n {/* Info tooltip button */}\r\n {infoTooltip && (\r\n <div ref={infoTooltipRef} style={{ position: \"relative\" }}>\r\n <button\r\n type=\"button\"\r\n onClick={() => setInfoTooltipOpen((v) => !v)}\r\n aria-label=\"Chart information\"\r\n aria-expanded={infoTooltipOpen}\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n width: 20,\r\n height: 20,\r\n padding: 4,\r\n border: `1px solid ${tokens.colors.border.default}`,\r\n borderRadius: \"50%\",\r\n background: infoTooltipOpen\r\n ? tokens.colors.accent.primary\r\n : isTransparentTheme\r\n ? \"rgba(10, 15, 25, 0.5)\"\r\n : tokens.colors.background.surface,\r\n color: infoTooltipOpen\r\n ? tokens.colors.background.base\r\n : tokens.colors.text.secondary,\r\n cursor: \"pointer\",\r\n transition: \"all 0.15s ease\",\r\n }}\r\n >\r\n {infoTooltip.icon || (\r\n <svg\r\n width=\"10\"\r\n height=\"10\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"currentColor\"\r\n aria-hidden=\"true\"\r\n >\r\n <path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z\" />\r\n </svg>\r\n )}\r\n </button>\r\n {infoTooltipOpen && (\r\n <div\r\n role=\"tooltip\"\r\n style={{\r\n position: \"absolute\",\r\n right: 0,\r\n top: \"100%\",\r\n marginTop: 8,\r\n minWidth: 200,\r\n maxWidth: infoTooltip.maxWidth || 300,\r\n padding: \"12px 16px\",\r\n background: isTransparentTheme\r\n ? \"rgba(10, 15, 25, 0.85)\"\r\n : tokens.colors.background.surface,\r\n backdropFilter: isTransparentTheme\r\n ? \"blur(12px)\"\r\n : undefined,\r\n border: `1px solid ${tokens.colors.border.default}`,\r\n borderRadius: tokens.borderRadius.md,\r\n boxShadow: \"0 4px 16px rgba(0,0,0,0.25)\",\r\n color: tokens.colors.text.primary,\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n fontSize: tokens.typography.fontSize.sm,\r\n lineHeight: 1.5,\r\n zIndex: 20,\r\n }}\r\n >\r\n {infoTooltip.content}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n\r\n {/* Export dropdown - compact icon only */}\r\n {useCustomExportDropdown && (\r\n <div ref={exportMenuRef} style={{ position: \"relative\" }}>\r\n <button\r\n type=\"button\"\r\n onClick={() => setExportMenuOpen((v) => !v)}\r\n aria-haspopup=\"true\"\r\n aria-expanded={exportMenuOpen}\r\n aria-label=\"Save chart as\"\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n width: 20,\r\n height: 20,\r\n padding: 4,\r\n border: `1px solid ${tokens.colors.border.default}`,\r\n borderRadius: \"50%\",\r\n background: isTransparentTheme\r\n ? \"rgba(10, 15, 25, 0.5)\"\r\n : tokens.colors.background.surface,\r\n color: tokens.colors.text.secondary,\r\n cursor: \"pointer\",\r\n transition: \"all 0.15s ease\",\r\n }}\r\n >\r\n <svg\r\n width=\"10\"\r\n height=\"10\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n aria-hidden=\"true\"\r\n >\r\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\r\n <polyline points=\"7 10 12 15 17 10\" />\r\n <line x1=\"12\" y1=\"15\" x2=\"12\" y2=\"3\" />\r\n </svg>\r\n </button>\r\n {exportMenuOpen && (\r\n <div\r\n role=\"menu\"\r\n style={{\r\n position: \"absolute\",\r\n right: 0,\r\n top: \"100%\",\r\n marginTop: 4,\r\n minWidth: 120,\r\n padding: 4,\r\n background: tokens.colors.background.surface,\r\n border: `1px solid ${tokens.colors.border.default}`,\r\n borderRadius: tokens.borderRadius.md,\r\n boxShadow: \"0 4px 12px rgba(0,0,0,0.2)\",\r\n }}\r\n >\r\n {exportImageFormats.map((f) => (\r\n <button\r\n key={f}\r\n type=\"button\"\r\n role=\"menuitem\"\r\n onClick={() =>\r\n handleExportFormat(\r\n f === \"jpg\"\r\n ? \"jpeg\"\r\n : (f as \"png\" | \"jpeg\" | \"svg\"),\r\n )\r\n }\r\n style={{\r\n display: \"block\",\r\n width: \"100%\",\r\n padding: \"8px 12px\",\r\n border: \"none\",\r\n borderRadius: tokens.borderRadius.sm,\r\n background: \"transparent\",\r\n color: tokens.colors.text.primary,\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n fontSize: tokens.typography.fontSize.sm,\r\n textAlign: \"left\",\r\n cursor: \"pointer\",\r\n }}\r\n >\r\n {f.toUpperCase()}\r\n </button>\r\n ))}\r\n {hasCsv && (\r\n <button\r\n type=\"button\"\r\n role=\"menuitem\"\r\n onClick={() => handleExportFormat(\"csv\")}\r\n style={{\r\n display: \"block\",\r\n width: \"100%\",\r\n padding: \"8px 12px\",\r\n border: \"none\",\r\n borderRadius: tokens.borderRadius.sm,\r\n background: \"transparent\",\r\n color: tokens.colors.text.primary,\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n fontSize: tokens.typography.fontSize.sm,\r\n textAlign: \"left\",\r\n cursor: \"pointer\",\r\n }}\r\n >\r\n CSV\r\n </button>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n );\r\n },\r\n ),\r\n);\r\n\r\nexport default AstroChart;\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AstroChart.js","names":[],"sources":["../../../../src/react/charts/unified/AstroChart.tsx"],"sourcesContent":["/**\r\n * @zendir/ui - AstroChart Component\r\n *\r\n * Enterprise-grade unified charting component built on ECharts.\r\n * Follows AstroUXD design patterns with full theme integration.\r\n *\r\n * Features:\r\n * - 15+ chart types\r\n * - Real-time streaming support\r\n * - Zoom, pan, brush selection\r\n * - Annotations (thresholds, regions)\r\n * - Export (PNG, SVG, CSV)\r\n * - Crosshair sync across charts\r\n * - Full accessibility support\r\n * - Dark/light theme support\r\n */\r\n\r\nimport React, {\r\n useRef,\r\n useEffect,\r\n useMemo,\r\n useCallback,\r\n useState,\r\n forwardRef,\r\n useImperativeHandle,\r\n memo,\r\n} from \"react\";\r\nimport ReactEChartsCore from \"echarts-for-react/lib/core\";\r\nimport * as echarts from \"echarts/core\";\r\nimport {\r\n LineChart,\r\n BarChart,\r\n PieChart,\r\n ScatterChart,\r\n RadarChart,\r\n GaugeChart,\r\n HeatmapChart,\r\n CandlestickChart,\r\n BoxplotChart,\r\n TreemapChart,\r\n SunburstChart,\r\n FunnelChart,\r\n SankeyChart,\r\n CustomChart,\r\n GraphChart,\r\n ParallelChart as EChartsParallelChart,\r\n} from \"echarts/charts\";\r\nimport {\r\n GridComponent,\r\n TooltipComponent,\r\n TitleComponent,\r\n LegendComponent,\r\n DataZoomComponent,\r\n ToolboxComponent,\r\n MarkLineComponent,\r\n MarkAreaComponent,\r\n MarkPointComponent,\r\n VisualMapComponent,\r\n BrushComponent,\r\n PolarComponent,\r\n GraphicComponent,\r\n RadarComponent,\r\n GeoComponent,\r\n ParallelComponent,\r\n} from \"echarts/components\";\r\nimport { CanvasRenderer, SVGRenderer } from \"echarts/renderers\";\r\nimport type { EChartsOption, ECharts } from \"echarts\";\r\n\r\nimport { useTheme } from \"../../theme\";\r\nimport { AstroIcon, type AstroIconName } from \"../../core/display/AstroIcon\";\r\nimport { HeaderIconWithStatus } from \"../../core/display/HeaderIconWithStatus\";\r\nimport { useCardBorderStyle } from \"../../context/DisplaySettingsContext\";\r\nimport {\r\n createAstroEChartsTheme,\r\n createAreaGradient,\r\n getSeriesColor,\r\n} from \"./theme\";\r\nimport type {\r\n AstroChartProps,\r\n SeriesData,\r\n ChartType,\r\n ChartEventParams,\r\n DataPoint,\r\n TimeSeriesPoint,\r\n ExportFileNameContext,\r\n ChartStatus,\r\n} from \"./types\";\r\n\r\n// =============================================================================\r\n// Status Shape SVGs (matching DataValue component for consistency)\r\n// =============================================================================\r\n\r\nconst STATUS_SHAPES: Record<\r\n ChartStatus,\r\n (size: number, color: string) => React.ReactNode\r\n> = {\r\n off: (size, color) => (\r\n <svg viewBox=\"0 0 12 12\" width={size} height={size}>\r\n <circle cx=\"6\" cy=\"6\" r=\"5\" fill={color} />\r\n </svg>\r\n ),\r\n standby: (size, color) => (\r\n <svg viewBox=\"0 0 12 12\" width={size} height={size}>\r\n <circle cx=\"6\" cy=\"6\" r=\"5\" fill={color} />\r\n </svg>\r\n ),\r\n normal: (size, color) => (\r\n <svg viewBox=\"0 0 12 12\" width={size} height={size}>\r\n <circle cx=\"6\" cy=\"6\" r=\"5\" fill={color} />\r\n </svg>\r\n ),\r\n caution: (size, color) => (\r\n <svg viewBox=\"0 0 12 12\" width={size} height={size}>\r\n <rect x=\"1\" y=\"1\" width=\"10\" height=\"10\" fill={color} />\r\n </svg>\r\n ),\r\n serious: (size, color) => (\r\n <svg viewBox=\"0 0 12 12\" width={size} height={size}>\r\n <polygon points=\"6,1 11,6 6,11 1,6\" fill={color} />\r\n </svg>\r\n ),\r\n critical: (size, color) => (\r\n <svg viewBox=\"0 0 12 12\" width={size} height={size}>\r\n <polygon points=\"6,11 1,2 11,2\" fill={color} />\r\n </svg>\r\n ),\r\n};\r\n\r\n/**\r\n * Astro UX Status Labels\r\n * Follows Astro UXDS naming conventions\r\n * https://www.astrouxds.com/patterns/status-system/\r\n */\r\nconst STATUS_LABELS: Record<ChartStatus, string> = {\r\n off: \"Off\",\r\n standby: \"Standby\",\r\n normal: \"Normal\",\r\n caution: \"Caution\",\r\n serious: \"Serious\",\r\n critical: \"Critical\",\r\n};\r\n\r\n/** Slug for safe filenames: lowercase, alphanumeric and hyphens only */\r\nfunction slug(str: string): string {\r\n return str\r\n .toLowerCase()\r\n .replace(/[^a-z0-9]+/g, \"-\")\r\n .replace(/^-|-$/g, \"\");\r\n}\r\n\r\n/** Build base export name from title, series names, and chart type */\r\nfunction derivedExportBaseName(\r\n title: string | undefined,\r\n seriesNames: string[],\r\n chartType: string,\r\n): string {\r\n const parts: string[] = [\"zendir\"];\r\n if (title) parts.push(slug(title));\r\n else parts.push(\"chart\");\r\n const seriesSlug = seriesNames\r\n .slice(0, 4)\r\n .map(slug)\r\n .filter(Boolean)\r\n .join(\"-\");\r\n if (seriesSlug) parts.push(seriesSlug);\r\n parts.push(slug(chartType));\r\n return parts.join(\"-\") || \"zendir-chart\";\r\n}\r\n\r\n// Register ECharts components\r\necharts.use([\r\n LineChart,\r\n BarChart,\r\n PieChart,\r\n ScatterChart,\r\n RadarChart,\r\n GaugeChart,\r\n HeatmapChart,\r\n CandlestickChart,\r\n BoxplotChart,\r\n TreemapChart,\r\n SunburstChart,\r\n FunnelChart,\r\n SankeyChart,\r\n CustomChart,\r\n GraphChart,\r\n EChartsParallelChart,\r\n GridComponent,\r\n TooltipComponent,\r\n TitleComponent,\r\n LegendComponent,\r\n DataZoomComponent,\r\n ToolboxComponent,\r\n MarkLineComponent,\r\n MarkAreaComponent,\r\n MarkPointComponent,\r\n VisualMapComponent,\r\n BrushComponent,\r\n PolarComponent,\r\n // Registers ECharts' free-form graphic elements — a circle, a line, drawn\r\n // straight onto the chart by id rather than as a series. Nothing in this\r\n // file uses it, but `chart.setOption({ graphic: [...] })` from outside —\r\n // a playhead, a crosshair, a hover dot, all things drawn imperatively so a\r\n // pointer move never rebuilds the whole option — depends on it, and\r\n // without it that call succeeds and draws nothing: ECharts does not\r\n // recognise the `graphic` key and silently no-ops, with no error to find.\r\n GraphicComponent,\r\n RadarComponent,\r\n GeoComponent,\r\n ParallelComponent,\r\n CanvasRenderer,\r\n SVGRenderer,\r\n]);\r\n\r\n// =============================================================================\r\n// Chart Instance Handle\r\n// =============================================================================\r\n\r\nexport interface AstroChartHandle {\r\n /** Get ECharts instance */\r\n getChart: () => ECharts | undefined;\r\n /** Resize chart */\r\n resize: () => void;\r\n /** Export as image (png, jpeg, svg) */\r\n exportImage: (\r\n type?: \"png\" | \"jpeg\" | \"jpg\" | \"svg\",\r\n pixelRatio?: number,\r\n ) => string | undefined;\r\n /** Export data as CSV */\r\n exportCSV: () => string;\r\n /** Suggested export filename from title, series names, and chart type (with optional extension and timestamp) */\r\n getExportFileName: (extension?: string, includeTimestamp?: boolean) => string;\r\n /** Append data for real-time updates */\r\n appendData: (seriesIndex: number, data: DataPoint | TimeSeriesPoint) => void;\r\n /** Clear all data */\r\n clearData: () => void;\r\n /** Dispatch action */\r\n dispatchAction: (action: unknown) => void;\r\n /**\r\n * Move a vertical cursor to an x-axis value, or clear it with `null`.\r\n *\r\n * Distinct from `annotations.markLines`, which is a prop: changing a prop\r\n * rebuilds the whole option, and rebuilding the option re-transforms every\r\n * point of every series. A chart following a playhead does that on each\r\n * frame, so on a few thousand points it stops being able to keep up. This\r\n * touches one graphic element and leaves the series alone.\r\n */\r\n setCursor: (value: number | null, options?: { color?: string }) => void;\r\n}\r\n\r\n// =============================================================================\r\n// Series Transformer\r\n// =============================================================================\r\n\r\nfunction transformSeriesToECharts(\r\n series: SeriesData[],\r\n chartType: ChartType,\r\n theme: ReturnType<typeof createAstroEChartsTheme>,\r\n): EChartsOption[\"series\"] {\r\n return series.map((s, index) => {\r\n const seriesType = s.type || chartType;\r\n const color = s.color || getSeriesColor(index);\r\n\r\n // Base series config\r\n const baseSeries: Record<string, unknown> = {\r\n id: s.id,\r\n name: s.name,\r\n type: mapChartType(seriesType),\r\n data: transformData(s.data, seriesType),\r\n yAxisIndex: s.yAxisIndex || 0,\r\n showSymbol: seriesType === \"scatter\" || s.symbol?.type !== \"none\",\r\n smooth: s.smooth ?? (seriesType === \"line\" || seriesType === \"area\"),\r\n };\r\n\r\n // Line/Area specific\r\n if (seriesType === \"line\" || seriesType === \"area\") {\r\n baseSeries.lineStyle = {\r\n width: s.lineStyle?.width ?? 2,\r\n type: s.lineStyle?.type ?? \"solid\",\r\n opacity: s.lineStyle?.opacity ?? 1,\r\n };\r\n baseSeries.itemStyle = { color };\r\n\r\n if (seriesType === \"area\") {\r\n baseSeries.areaStyle = s.areaStyle || {\r\n opacity: 0.3,\r\n color: createAreaGradient(color),\r\n };\r\n }\r\n\r\n if (s.symbol) {\r\n baseSeries.symbol = s.symbol.type || \"circle\";\r\n baseSeries.symbolSize = s.symbol.size || 6;\r\n }\r\n }\r\n\r\n // Bar specific\r\n if (seriesType === \"bar\") {\r\n baseSeries.itemStyle = {\r\n color,\r\n borderRadius: [4, 4, 0, 0],\r\n };\r\n if (s.stack) {\r\n baseSeries.stack = s.stack;\r\n }\r\n }\r\n\r\n // Pie/Donut specific — theme color, no white border on labels\r\n if (seriesType === \"pie\" || seriesType === \"donut\") {\r\n baseSeries.radius = seriesType === \"donut\" ? [\"40%\", \"70%\"] : \"70%\";\r\n baseSeries.label = {\r\n show: true,\r\n formatter: \"{b}: {d}%\",\r\n color: theme.textStyle.color,\r\n textBorderWidth: 0,\r\n textBorderColor: \"transparent\",\r\n };\r\n baseSeries.emphasis = {\r\n itemStyle: {\r\n shadowBlur: 10,\r\n shadowOffsetX: 0,\r\n shadowColor: \"rgba(0, 0, 0, 0.5)\",\r\n },\r\n };\r\n }\r\n\r\n // Scatter specific\r\n if (seriesType === \"scatter\") {\r\n baseSeries.symbolSize = s.symbol?.size || 10;\r\n baseSeries.itemStyle = { color };\r\n }\r\n\r\n // Gauge specific\r\n if (seriesType === \"gauge\") {\r\n baseSeries.detail = {\r\n formatter: \"{value}%\",\r\n fontSize: 20,\r\n color: theme.textStyle.color,\r\n };\r\n baseSeries.axisLine = {\r\n lineStyle: {\r\n width: 10,\r\n color: [\r\n [0.3, theme.color[1]], // Green\r\n [0.7, theme.color[3]], // Yellow\r\n [1, theme.color[2]], // Red\r\n ],\r\n },\r\n };\r\n }\r\n\r\n return baseSeries;\r\n });\r\n}\r\n\r\n/** The one graphic element `setCursor` owns, so repeated calls replace it. */\r\nconst CURSOR_ID = \"astro-chart-cursor\";\r\n\r\nfunction mapChartType(type: ChartType): string {\r\n switch (type) {\r\n case \"area\":\r\n return \"line\";\r\n case \"donut\":\r\n return \"pie\";\r\n default:\r\n return type;\r\n }\r\n}\r\n\r\nfunction transformData(data: SeriesData[\"data\"], type: ChartType): unknown[] {\r\n if (!data || data.length === 0) return [];\r\n\r\n // Handle array of numbers\r\n if (typeof data[0] === \"number\") {\r\n return data as number[];\r\n }\r\n\r\n // Handle [x, y] tuples\r\n if (Array.isArray(data[0])) {\r\n return data;\r\n }\r\n\r\n // Handle DataPoint or TimeSeriesPoint\r\n const firstItem = data[0] as DataPoint | TimeSeriesPoint;\r\n\r\n if (\"time\" in firstItem) {\r\n // TimeSeriesPoint\r\n return (data as TimeSeriesPoint[]).map((d) => [d.time, d.value]);\r\n }\r\n\r\n // For pie/donut charts, ECharts expects { name, value } format\r\n if (type === \"pie\" || type === \"donut\") {\r\n return (data as DataPoint[]).map((d) => ({\r\n name: String(d.x),\r\n value: d.y,\r\n // Apply color via itemStyle if specified\r\n ...(d.color ? { itemStyle: { color: d.color } } : {}),\r\n // Preserve any additional metadata\r\n ...(d.meta || {}),\r\n }));\r\n }\r\n\r\n // For scatter charts, preserve per-point color as itemStyle\r\n if (type === \"scatter\") {\r\n return (data as DataPoint[]).map((d) => ({\r\n value: [d.x, d.y],\r\n ...(d.color ? { itemStyle: { color: d.color } } : {}),\r\n ...(d.label ? { name: d.label } : {}),\r\n }));\r\n }\r\n\r\n // DataPoint for other chart types\r\n return (data as DataPoint[]).map((d) => [d.x, d.y]);\r\n}\r\n\r\n// =============================================================================\r\n// Options Builder\r\n// =============================================================================\r\n\r\nfunction buildEChartsOptions(\r\n props: AstroChartProps,\r\n theme: ReturnType<typeof createAstroEChartsTheme>,\r\n hideTitle = false,\r\n): EChartsOption {\r\n const {\r\n type,\r\n series,\r\n title,\r\n subtitle,\r\n xAxis,\r\n yAxis,\r\n tooltip,\r\n legend,\r\n zoom,\r\n annotations,\r\n loading,\r\n compactAxes = true, // Default: hide axis names for more chart space\r\n } = props;\r\n\r\n // Hide title when using custom header (transparent theme with icon)\r\n const showEChartsTitle = title && !hideTitle;\r\n\r\n // Note: When compactAxes is true, axis names are hidden from the chart\r\n // but developers can access xAxis.name and yAxis.name via props for custom tooltips\r\n\r\n const isPolar = type === \"radar\";\r\n const isPie = type === \"pie\" || type === \"donut\";\r\n const isGauge = type === \"gauge\";\r\n const isSankey = type === \"sankey\";\r\n const isTreemap = type === \"treemap\" || type === \"sunburst\";\r\n const isGraph =\r\n props.echartsOptions?.series &&\r\n Array.isArray(props.echartsOptions.series) &&\r\n props.echartsOptions.series.some(\r\n (s: unknown) => (s as { type?: string })?.type === \"graph\",\r\n );\r\n\r\n // Detect 3D charts (they use grid3D, xAxis3D, etc. - should NOT have 2D axes)\r\n const is3D = !!(\r\n props.echartsOptions?.grid3D ||\r\n props.echartsOptions?.xAxis3D ||\r\n props.echartsOptions?.globe ||\r\n (props.echartsOptions?.series &&\r\n Array.isArray(props.echartsOptions.series) &&\r\n props.echartsOptions.series.some((s: unknown) => {\r\n const seriesType = (s as { type?: string })?.type || \"\";\r\n return seriesType.includes(\"3D\") || seriesType.includes(\"3d\");\r\n }))\r\n );\r\n\r\n // Charts that don't need cartesian grid/axes\r\n const needsGrid =\r\n !isPolar &&\r\n !isPie &&\r\n !isGauge &&\r\n !isSankey &&\r\n !isTreemap &&\r\n !isGraph &&\r\n !is3D &&\r\n !props.hideAxes;\r\n\r\n /** Whether the right margin needs to leave room for a mark line's own label. */\r\n const hasEndLabelledMarkLine = !!annotations?.markLines?.data.some(\r\n (line) => line.labelPosition === \"end\",\r\n );\r\n\r\n // Calculate layout based on title and legend position\r\n // Default legend to bottom for better chart space utilization\r\n // When using custom header (hideTitle=true), we still need top spacing for the header\r\n const hasTitle = hideTitle ? true : !!title;\r\n const hasSubtitle = hideTitle ? false : !!subtitle;\r\n const legendPosition = legend?.position ?? \"bottom\"; // Default to bottom\r\n const legendAtTop = legendPosition === \"top\";\r\n const legendAtBottom = legendPosition === \"bottom\";\r\n const showLegend = legend?.show !== false;\r\n\r\n // Build options\r\n const options: EChartsOption = {\r\n // Title - positioned to avoid legend overlap (hidden when using custom header)\r\n title: showEChartsTitle\r\n ? {\r\n text: title,\r\n subtext: subtitle,\r\n left: \"center\",\r\n top: legendAtTop && showLegend ? 35 : 10,\r\n textStyle: {\r\n ...theme.title.textStyle,\r\n fontWeight:\r\n (theme.title.textStyle.fontWeight as \"normal\" | \"bold\") ??\r\n \"normal\",\r\n },\r\n subtextStyle: theme.title.subtextStyle,\r\n }\r\n : undefined,\r\n\r\n // Tooltip with improved contrast\r\n // When compactAxes is true, axis names are shown in the axisPointer label on hover\r\n tooltip:\r\n tooltip?.enabled !== false\r\n ? ({\r\n trigger: tooltip?.trigger || (isPie ? \"item\" : \"axis\"),\r\n axisPointer: {\r\n type:\r\n tooltip?.crosshair === true || tooltip?.crosshair === \"both\"\r\n ? \"cross\"\r\n : \"line\",\r\n lineStyle: theme.axisPointer.lineStyle,\r\n crossStyle: theme.axisPointer.crossStyle,\r\n // Show axis value labels on hover (includes axis name context)\r\n label: compactAxes\r\n ? {\r\n show: true,\r\n backgroundColor: theme.tooltip.backgroundColor,\r\n borderColor: theme.tooltip.borderColor,\r\n color: theme.tooltip.textStyle.color,\r\n fontSize: 11,\r\n padding: [4, 8],\r\n // Add axis name prefix to the label when in compact mode\r\n formatter: (params: {\r\n axisDimension: string;\r\n axisIndex: number;\r\n value: number | string;\r\n }) => {\r\n const axisName =\r\n params.axisDimension === \"x\"\r\n ? xAxis?.name\r\n : Array.isArray(yAxis)\r\n ? yAxis[params.axisIndex]?.name\r\n : yAxis?.name;\r\n const value =\r\n typeof params.value === \"number\"\r\n ? params.value.toLocaleString()\r\n : params.value;\r\n return axisName ? `${axisName}: ${value}` : String(value);\r\n },\r\n }\r\n : undefined,\r\n },\r\n backgroundColor: theme.tooltip.backgroundColor,\r\n borderColor: theme.tooltip.borderColor,\r\n borderWidth: theme.tooltip.borderWidth,\r\n textStyle: {\r\n ...theme.tooltip.textStyle,\r\n fontSize: 14, // AstroUXDS minimum 14pt\r\n },\r\n extraCssText: theme.tooltip.extraCssText,\r\n confine: tooltip?.confine ?? true,\r\n formatter: tooltip?.formatter as unknown as undefined,\r\n } as any)\r\n : undefined,\r\n\r\n // Legend - ultra-compact styling for maximum chart space\r\n legend: showLegend\r\n ? {\r\n show: true,\r\n type: \"scroll\",\r\n orient: legend?.orient || \"horizontal\",\r\n left:\r\n legend?.position === \"left\"\r\n ? \"left\"\r\n : legend?.position === \"right\"\r\n ? \"right\"\r\n : \"center\",\r\n top: legendAtTop ? 4 : undefined,\r\n bottom: legendAtBottom ? 2 : undefined,\r\n padding: [2, 6],\r\n itemGap: 8,\r\n itemWidth: 12,\r\n itemHeight: 6,\r\n textStyle: {\r\n ...theme.legend.textStyle,\r\n fontSize: 10, // Ultra-compact for maximum chart space\r\n },\r\n inactiveColor: theme.legend.inactiveColor,\r\n selectedMode: legend?.interactive !== false,\r\n }\r\n : { show: false },\r\n\r\n // Grid - minimal spacing for maximum chart plotting area\r\n // When compactAxes is true, axis names are hidden so we need minimal padding\r\n // containLabel: true ensures axis tick labels don't get cut off\r\n // Extra space only needed for slider zoom (inside zoom is invisible)\r\n grid: needsGrid\r\n ? {\r\n left: compactAxes ? 8 : 40,\r\n right:\r\n Array.isArray(yAxis) && yAxis.length > 1\r\n ? compactAxes\r\n ? 8\r\n : 40\r\n // A mark line labelled \"end\" reads its value off to the right\r\n // of the plot, past the axis — the 8px this chart otherwise\r\n // reserves is enough for the axis alone and clips the label's\r\n // own box.\r\n : hasEndLabelledMarkLine\r\n ? 70\r\n : 8,\r\n top: hasTitle\r\n ? hasSubtitle\r\n ? 50\r\n : 36\r\n : legendAtTop && showLegend\r\n ? 26\r\n : 4,\r\n bottom:\r\n (legendAtBottom && showLegend ? 26 : 16) +\r\n (zoom?.type === \"slider\" || zoom?.type === \"both\" ? 26 : 0),\r\n containLabel: true,\r\n }\r\n : undefined,\r\n\r\n // X Axis - compact font sizes for more chart space\r\n // In compact mode, hide axis name (shown in tooltip instead)\r\n xAxis: needsGrid\r\n ? {\r\n type: (xAxis?.type || \"time\") as\r\n | \"value\"\r\n | \"category\"\r\n | \"time\"\r\n | \"log\",\r\n name: compactAxes ? undefined : xAxis?.name,\r\n nameLocation: xAxis?.nameLocation || \"middle\",\r\n nameGap: 18,\r\n nameTextStyle: {\r\n ...theme.xAxis.nameTextStyle,\r\n fontSize: 10, // Ultra-compact for maximum chart space\r\n fontWeight: 500,\r\n },\r\n min: xAxis?.min,\r\n max: xAxis?.max,\r\n axisLine: {\r\n show: xAxis?.showLine !== false,\r\n lineStyle: theme.xAxis.axisLine.lineStyle,\r\n },\r\n axisTick: {\r\n lineStyle: theme.xAxis.axisTick.lineStyle,\r\n },\r\n axisLabel: {\r\n ...theme.xAxis.axisLabel,\r\n fontSize: 10, // Ultra-compact for maximum chart space\r\n // A caller's own spec wins over the shared theme, for this\r\n // axis only — see `labelStyle` on AxisOptions.\r\n ...xAxis?.labelStyle,\r\n formatter: xAxis?.labelFormatter as unknown as undefined,\r\n // Prevent label overlap - auto-hide overlapping labels\r\n hideOverlap: true,\r\n // Rotate labels slightly if needed for better fit\r\n rotate: 0,\r\n // Show fewer labels when space is tight\r\n interval: \"auto\",\r\n },\r\n splitLine: {\r\n show: xAxis?.showGrid !== false,\r\n lineStyle: {\r\n ...theme.xAxis.splitLine.lineStyle,\r\n type:\r\n (theme.xAxis.splitLine.lineStyle.type as\r\n | \"solid\"\r\n | \"dashed\"\r\n | \"dotted\") ?? \"solid\",\r\n ...xAxis?.gridStyle,\r\n },\r\n },\r\n inverse: xAxis?.inverse,\r\n }\r\n : undefined,\r\n\r\n // Y Axis - ultra-compact font sizes for maximum chart space\r\n // In compact mode, hide axis name (shown in tooltip instead)\r\n yAxis: (needsGrid\r\n ? (Array.isArray(yAxis) ? yAxis : [yAxis || {}]).map((axis, i) => ({\r\n type: (axis?.type || \"value\") as\r\n | \"value\"\r\n | \"category\"\r\n | \"time\"\r\n | \"log\",\r\n name: compactAxes ? undefined : axis?.name,\r\n nameLocation: axis?.nameLocation || \"middle\",\r\n nameGap: 28,\r\n nameTextStyle: {\r\n ...theme.yAxis.nameTextStyle,\r\n fontSize: 10, // Ultra-compact for maximum chart space\r\n fontWeight: 500,\r\n },\r\n position: axis?.position || (i === 0 ? \"left\" : \"right\"),\r\n min: axis?.min,\r\n max: axis?.max,\r\n splitNumber: axis?.splitNumber,\r\n axisLine: {\r\n show: axis?.showLine !== false,\r\n lineStyle: theme.yAxis.axisLine.lineStyle,\r\n },\r\n axisTick: {\r\n lineStyle: theme.yAxis.axisTick.lineStyle,\r\n },\r\n axisLabel: {\r\n ...theme.yAxis.axisLabel,\r\n fontSize: 10, // Ultra-compact for maximum chart space\r\n ...axis?.labelStyle,\r\n formatter: axis?.labelFormatter\r\n ? (axis.labelFormatter as unknown as undefined)\r\n : axis?.unit\r\n ? (v: number) => `${v}${axis.unit}`\r\n : undefined,\r\n },\r\n splitLine: {\r\n show: axis?.showGrid !== false,\r\n lineStyle: { ...theme.yAxis.splitLine.lineStyle, ...axis?.gridStyle },\r\n },\r\n inverse: axis?.inverse,\r\n }))\r\n : undefined) as EChartsOption[\"yAxis\"],\r\n\r\n // Radar (for radar charts) - transparent background for glassmorphic look\r\n radar: isPolar\r\n ? {\r\n indicator:\r\n series[0]?.data?.map((d: unknown) => {\r\n const point = d as DataPoint;\r\n return {\r\n name: point.label || point.category || String(point.x),\r\n max: 100,\r\n };\r\n }) || [],\r\n splitArea: {\r\n show: true,\r\n areaStyle: {\r\n color: [\r\n \"transparent\",\r\n \"transparent\",\r\n \"transparent\",\r\n \"transparent\",\r\n \"transparent\",\r\n ],\r\n },\r\n },\r\n splitLine: {\r\n lineStyle: {\r\n color: theme.xAxis.splitLine.lineStyle.color,\r\n opacity: 0.4,\r\n },\r\n },\r\n axisLine: {\r\n lineStyle: {\r\n color: theme.xAxis.axisLine.lineStyle.color,\r\n opacity: 0.4,\r\n },\r\n },\r\n axisName: {\r\n color: theme.xAxis.axisLabel.color,\r\n fontSize: 12,\r\n },\r\n }\r\n : undefined,\r\n\r\n // Data Zoom - inside zoom enabled by default for mouse wheel/pinch\r\n // Use zoom={{ enabled: false }} to disable, or zoom={{ type: 'slider' }} for slider\r\n dataZoom:\r\n zoom?.enabled === false\r\n ? undefined\r\n : [\r\n // Inside zoom (mouse wheel / pinch) - ENABLED BY DEFAULT\r\n ...(zoom?.type === \"inside\" || zoom?.type === \"both\" || !zoom?.type\r\n ? [\r\n {\r\n type: \"inside\" as const,\r\n xAxisIndex:\r\n zoom?.axis === \"x\" || zoom?.axis === \"both\" || !zoom?.axis\r\n ? 0\r\n : undefined,\r\n yAxisIndex:\r\n zoom?.axis === \"y\" || zoom?.axis === \"both\"\r\n ? 0\r\n : undefined,\r\n start: zoom?.start ?? 0,\r\n end: zoom?.end ?? 100,\r\n minSpan: zoom?.minSpan,\r\n maxSpan: zoom?.maxSpan,\r\n // Smooth zoom animation\r\n throttle: 50,\r\n },\r\n ]\r\n : []),\r\n // Slider zoom (only when explicitly requested)\r\n ...(zoom?.type === \"slider\" || zoom?.type === \"both\"\r\n ? [\r\n {\r\n type: \"slider\" as const,\r\n show: true,\r\n xAxisIndex:\r\n zoom?.axis === \"x\" || zoom?.axis === \"both\"\r\n ? 0\r\n : undefined,\r\n yAxisIndex:\r\n zoom?.axis === \"y\" || zoom?.axis === \"both\"\r\n ? 0\r\n : undefined,\r\n start: zoom?.start ?? 0,\r\n end: zoom?.end ?? 100,\r\n minSpan: zoom?.minSpan,\r\n maxSpan: zoom?.maxSpan,\r\n backgroundColor: theme.dataZoom.backgroundColor,\r\n borderColor: theme.dataZoom.borderColor,\r\n fillerColor: theme.dataZoom.fillerColor,\r\n handleStyle: { color: theme.dataZoom.handleColor },\r\n textStyle: theme.dataZoom.textStyle,\r\n height: 25,\r\n bottom: 10,\r\n },\r\n ]\r\n : []),\r\n ],\r\n\r\n // Toolbox (export) – always use custom dropdown for consistent UI\r\n toolbox: props.export?.enabled\r\n ? (() => {\r\n return {\r\n show: false, // Hide ECharts toolbox, use custom export button instead\r\n right: 20,\r\n top: 10,\r\n feature: {\r\n dataView: props.export.formats?.includes(\"json\")\r\n ? {\r\n show: true,\r\n readOnly: true,\r\n }\r\n : undefined,\r\n },\r\n iconStyle: {\r\n borderColor: theme.textStyle.color,\r\n },\r\n };\r\n })()\r\n : undefined,\r\n\r\n // Series\r\n series: transformSeriesToECharts(series, type, theme),\r\n\r\n // Animation\r\n animation: !loading,\r\n animationDuration: 300,\r\n animationEasing: \"cubicOut\",\r\n };\r\n\r\n // Add mark lines (thresholds)\r\n if (annotations?.markLines && options.series) {\r\n const seriesArray = options.series as Array<Record<string, unknown>>;\r\n if (seriesArray.length > 0) {\r\n seriesArray[0].markLine = {\r\n silent: true,\r\n symbol: \"none\",\r\n // A threshold is a fact about the data, already there when the chart\r\n // first draws — animating it in, on every redraw the chart's own\r\n // `notMerge` gives it, read as the line restarting rather than\r\n // holding still.\r\n animation: false,\r\n data: annotations.markLines.data.map((line) => ({\r\n ...line,\r\n lineStyle: {\r\n color: line.color || theme.color[2],\r\n type: line.lineStyle?.type || \"dashed\",\r\n width: line.lineStyle?.width || 1,\r\n },\r\n label: {\r\n show: !!line.label,\r\n formatter: line.label,\r\n position: line.labelPosition || \"middle\", // center by default, so the label doesn't overlap the y-axis at bottom\r\n distance: 6,\r\n color: theme.textStyle.color,\r\n fontWeight: 500,\r\n fontSize: 9.5,\r\n textBorderWidth: 0,\r\n textBorderColor: \"transparent\",\r\n backgroundColor: theme.tooltip.backgroundColor,\r\n borderColor: line.color || theme.color[2],\r\n borderWidth: 0.75,\r\n borderRadius: 3,\r\n padding: [1, 5],\r\n },\r\n })),\r\n };\r\n }\r\n }\r\n\r\n // Add mark areas (regions) — label style: no white border, crisp and slightly faded; optional icon\r\n if (annotations?.markAreas && options.series) {\r\n const seriesArray = options.series as Array<Record<string, unknown>>;\r\n const axisLabelColor =\r\n (theme.xAxis as { axisLabel?: { color?: string } })?.axisLabel?.color ??\r\n theme.textStyle?.color ??\r\n \"#94a3b8\";\r\n const fadedColor = axisLabelColor.startsWith(\"rgba\")\r\n ? axisLabelColor\r\n : `${axisLabelColor}cc`;\r\n const defaultLabelStyle = {\r\n show: true,\r\n color: fadedColor,\r\n fontSize: 11,\r\n fontWeight: \"500\" as const,\r\n textBorderWidth: 0,\r\n textBorderColor: \"transparent\",\r\n formatter: (params: { name?: string; value?: unknown }) => {\r\n const name =\r\n (params?.name ?? \"\") ||\r\n (typeof params?.value === \"string\" ? params.value : \"\");\r\n if (!name) return \"\";\r\n const icon = name.toLowerCase().includes(\"eclipse\") ? \"\\u25D0 \" : \"\"; // ◐ symbol for eclipse\r\n return icon ? `${icon}${name}` : name;\r\n },\r\n };\r\n if (seriesArray.length > 0) {\r\n seriesArray[0].markArea = {\r\n silent: true,\r\n data: annotations.markAreas.data.map(\r\n (\r\n pair: [\r\n { xAxis?: number | string; yAxis?: number; name?: string },\r\n { xAxis?: number | string; yAxis?: number },\r\n ],\r\n ) => {\r\n const [start, end] = pair;\r\n return [\r\n {\r\n ...start,\r\n label: {\r\n ...defaultLabelStyle,\r\n formatter: defaultLabelStyle.formatter,\r\n },\r\n },\r\n end,\r\n ];\r\n },\r\n ),\r\n itemStyle: annotations.markAreas.itemStyle,\r\n };\r\n }\r\n }\r\n\r\n // Merge custom options\r\n if (props.echartsOptions) {\r\n return mergeOptions(options, props.echartsOptions);\r\n }\r\n\r\n return options;\r\n}\r\n\r\nfunction mergeOptions(\r\n base: EChartsOption,\r\n custom: Partial<EChartsOption>,\r\n): EChartsOption {\r\n // Smart series merging logic\r\n let mergedSeries = base.series;\r\n\r\n if (custom.series) {\r\n const baseSeries =\r\n (base.series as Array<{ type?: string; data?: unknown[] }>) || [];\r\n const customSeries = custom.series as unknown[];\r\n\r\n // Check if all base series are empty (no data or empty data array)\r\n const allBaseSeriesEmpty =\r\n baseSeries.length === 0 ||\r\n baseSeries.every((s) => {\r\n if (!s) return true;\r\n if (!s.data) return true;\r\n if (Array.isArray(s.data) && s.data.length === 0) return true;\r\n return false;\r\n });\r\n\r\n // Single full override: presets (e.g. GaugeChart) pass one full series config; use it only so we don't render two gauges\r\n const baseLen = baseSeries.length;\r\n const customLen = customSeries.length;\r\n if (baseLen === 1 && customLen === 1) {\r\n const b = baseSeries[0];\r\n const c = customSeries[0] as { type?: string };\r\n if (b?.type === \"gauge\" && c?.type === \"gauge\") {\r\n mergedSeries = customSeries as EChartsOption[\"series\"];\r\n } else if (allBaseSeriesEmpty) {\r\n mergedSeries = customSeries as EChartsOption[\"series\"];\r\n } else {\r\n mergedSeries = [\r\n ...baseSeries,\r\n ...customSeries,\r\n ] as EChartsOption[\"series\"];\r\n }\r\n } else if (allBaseSeriesEmpty) {\r\n mergedSeries = customSeries as EChartsOption[\"series\"];\r\n } else {\r\n mergedSeries = [\r\n ...baseSeries,\r\n ...customSeries,\r\n ] as EChartsOption[\"series\"];\r\n }\r\n }\r\n\r\n return {\r\n ...base,\r\n ...custom,\r\n series: mergedSeries,\r\n };\r\n}\r\n\r\n// =============================================================================\r\n// Main Component\r\n// =============================================================================\r\n\r\nexport const AstroChart = memo(\r\n forwardRef<AstroChartHandle, AstroChartProps>(\r\n function AstroChart(props, ref) {\r\n const {\r\n width = \"100%\",\r\n height = 400,\r\n loading = false,\r\n emptyMessage = \"No data available\",\r\n className = \"\",\r\n style,\r\n ariaLabel,\r\n onChartReady,\r\n onClick,\r\n onHover,\r\n onZoomChange,\r\n onBrushSelect,\r\n realTime,\r\n series = [],\r\n infoTooltip,\r\n // Header icon/status system\r\n icon,\r\n iconStatus,\r\n statusMessage,\r\n showStatusBadge = false,\r\n } = props;\r\n\r\n const chartRef = useRef<ReactEChartsCore>(null);\r\n const { tokens, mode, theme: themeVariant } = useTheme();\r\n\r\n // Detect transparent theme for glassmorphic styling\r\n const isTransparentTheme =\r\n themeVariant === \"transparent\" ||\r\n themeVariant === \"transparent-bold\" ||\r\n themeVariant === \"transparent-minimal\";\r\n const isBoldVariant = themeVariant === \"transparent-bold\";\r\n const isMinimalVariant = themeVariant === \"transparent-minimal\";\r\n\r\n // Get card border style based on accentBorders setting\r\n const cardBorderStyle = useCardBorderStyle(tokens, isTransparentTheme);\r\n\r\n // Glass accent colors for bold/minimal themes (matching Container)\r\n const glassAccentColor = tokens.colors.accent.primary;\r\n const glassAccentMuted = `${glassAccentColor}66`;\r\n\r\n const headingConfig = tokens.layout?.card?.heading ?? {\r\n iconSize: 20,\r\n iconSizeCompact: 16,\r\n titleFontSize: \"1.125rem\",\r\n titleFontSizeChart: \"0.875rem\",\r\n titleFontWeight: 500,\r\n gap: 8,\r\n };\r\n const chartTitleFontSize =\r\n headingConfig.titleFontSizeChart ?? headingConfig.titleFontSize;\r\n\r\n // Generate theme - uses official AstroUXDS colors for 'astro' theme, Zendir colors for others\r\n const theme = useMemo(\r\n () => createAstroEChartsTheme(tokens, mode, themeVariant),\r\n [tokens, mode, themeVariant],\r\n );\r\n\r\n // Build options - hide ECharts title whenever we show custom header (top-left, all themes)\r\n const hideEChartsTitle = !!(icon || props.title);\r\n const options = useMemo(\r\n () => buildEChartsOptions(props, theme, hideEChartsTitle),\r\n [props, theme, hideEChartsTitle],\r\n );\r\n\r\n // Get chart instance.\r\n // echarts-for-react types the instance via echarts/core's internal\r\n // EChartsType, while callers here consume the public `ECharts` type from\r\n // 'echarts'. They are the same runtime object, so bridge the two nominal\r\n // declarations at this single boundary.\r\n const getChart = useCallback((): ECharts | undefined => {\r\n return chartRef.current?.getEchartsInstance() as unknown as\r\n | ECharts\r\n | undefined;\r\n }, []);\r\n\r\n // Dynamic export filename from title, series names, chart type\r\n const getExportFileName = useCallback(\r\n (extension?: string, includeTimestamp = true) => {\r\n const context: ExportFileNameContext = {\r\n title: props.title,\r\n subtitle: props.subtitle,\r\n seriesNames: series.map((s) => s.name),\r\n chartType: props.type,\r\n };\r\n let base: string;\r\n if (props.export?.getExportFileName) {\r\n base = props.export.getExportFileName(context);\r\n } else if (props.export?.fileName) {\r\n base = props.export.fileName;\r\n } else {\r\n base = derivedExportBaseName(\r\n props.title,\r\n context.seriesNames,\r\n props.type,\r\n );\r\n }\r\n if (includeTimestamp) {\r\n const d = new Date();\r\n const ts = d\r\n .toISOString()\r\n .slice(0, 19)\r\n .replace(/[-T:]/g, \"\")\r\n .replace(/(\\d{8})(\\d{6})/, \"$1-$2\");\r\n base = `${base}-${ts}`;\r\n }\r\n if (extension) base = `${base}.${extension.replace(/^\\./, \"\")}`;\r\n return base;\r\n },\r\n [props.title, props.subtitle, props.type, props.export, series],\r\n );\r\n\r\n // Expose imperative handle\r\n useImperativeHandle(\r\n ref,\r\n () => ({\r\n getChart,\r\n resize: () => getChart()?.resize(),\r\n getExportFileName,\r\n exportImage: (type = \"png\", pixelRatio = 2) => {\r\n const chart = getChart();\r\n if (!chart) return undefined;\r\n const imgType = type === \"jpg\" ? \"jpeg\" : type;\r\n return chart.getDataURL({\r\n type: imgType as \"png\" | \"jpeg\" | \"svg\",\r\n pixelRatio,\r\n backgroundColor: \"transparent\",\r\n });\r\n },\r\n exportCSV: () => {\r\n const rows: string[] = [];\r\n const headers = [\"Index\", ...series.map((s) => s.name)];\r\n rows.push(headers.join(\",\"));\r\n\r\n const maxLen = Math.max(...series.map((s) => s.data?.length || 0));\r\n for (let i = 0; i < maxLen; i++) {\r\n const row = [i.toString()];\r\n for (const s of series) {\r\n const data = s.data as unknown[];\r\n const value = data?.[i];\r\n if (typeof value === \"number\") {\r\n row.push(value.toString());\r\n } else if (Array.isArray(value)) {\r\n row.push(value[1]?.toString() || \"\");\r\n } else if (value && typeof value === \"object\") {\r\n const point = value as DataPoint | TimeSeriesPoint;\r\n row.push(\r\n (\"value\" in point ? point.value : point.y)?.toString() ||\r\n \"\",\r\n );\r\n } else {\r\n row.push(\"\");\r\n }\r\n }\r\n rows.push(row.join(\",\"));\r\n }\r\n return rows.join(\"\\n\");\r\n },\r\n appendData: (\r\n seriesIndex: number,\r\n data: DataPoint | TimeSeriesPoint,\r\n ) => {\r\n const chart = getChart();\r\n if (!chart) return;\r\n\r\n // Get current data\r\n const option = chart.getOption();\r\n const seriesOpt = (option.series as Array<{ data: unknown[] }>)?.[\r\n seriesIndex\r\n ];\r\n if (!seriesOpt) return;\r\n\r\n const currentData = [...(seriesOpt.data || [])];\r\n const newPoint =\r\n \"time\" in data ? [data.time, data.value] : [data.x, data.y];\r\n\r\n currentData.push(newPoint);\r\n\r\n // Apply max points limit\r\n const maxPoints = realTime?.maxPoints || 1000;\r\n while (currentData.length > maxPoints) {\r\n currentData.shift();\r\n }\r\n\r\n // Update chart\r\n chart.setOption(\r\n {\r\n series: [\r\n {\r\n data: currentData,\r\n },\r\n ],\r\n },\r\n {\r\n replaceMerge: [\"series\"],\r\n },\r\n );\r\n },\r\n clearData: () => {\r\n const chart = getChart();\r\n if (!chart) return;\r\n\r\n chart.setOption({\r\n series: series.map(() => ({ data: [] })),\r\n });\r\n },\r\n dispatchAction: (action: unknown) => {\r\n getChart()?.dispatchAction(\r\n action as Parameters<ECharts[\"dispatchAction\"]>[0],\r\n );\r\n },\r\n setCursor: (value: number | null, cursorOptions) => {\r\n const chart = getChart();\r\n if (!chart) return;\r\n\r\n if (value === null) {\r\n chart.setOption({\r\n graphic: [{ id: CURSOR_ID, $action: \"remove\" }],\r\n } as EChartsOption);\r\n return;\r\n }\r\n\r\n // Pixel position has to come from the chart, since the axis may be\r\n // zoomed, reversed or log-scaled and the caller knows none of that.\r\n const x = chart.convertToPixel({ xAxisIndex: 0 }, value);\r\n if (typeof x !== \"number\" || Number.isNaN(x)) return;\r\n\r\n chart.setOption({\r\n graphic: [\r\n {\r\n id: CURSOR_ID,\r\n type: \"line\",\r\n // Above the series but not interactive: a cursor that\r\n // swallowed pointer events would break hovering the very\r\n // chart it sits on.\r\n z: 100,\r\n silent: true,\r\n shape: { x1: x, y1: 0, x2: x, y2: chart.getHeight() },\r\n style: {\r\n stroke: cursorOptions?.color ?? \"#8744F3\",\r\n lineWidth: 1.5,\r\n lineDash: [3, 2],\r\n },\r\n },\r\n ],\r\n } as EChartsOption);\r\n },\r\n }),\r\n [getChart, series, realTime],\r\n );\r\n\r\n // Zoom preservation: ReactEChartsCore re-renders with notMerge=true whenever\r\n // `options` changes (which happens on every series-data update). Without this,\r\n // streaming charts snap back to start=0/end=100 each frame, defeating user\r\n // pan/zoom.\r\n //\r\n // We capture user pan/zoom on the `datazoom` event and replay it after every\r\n // option update. For streaming time-series we MUST use absolute axis values\r\n // (startValue/endValue) — preserving percent (start/end) would slide the\r\n // window forward as new data extends the axis, which feels exactly like a\r\n // zoom reset from the operator's perspective. Percent is saved as a fallback\r\n // for category/non-numeric axes where absolute values aren't meaningful.\r\n //\r\n // `programmaticRef` suppresses the datazoom event we ourselves dispatch so\r\n // we don't overwrite the saved range with the values just replayed.\r\n type ZoomState = {\r\n start?: number;\r\n end?: number;\r\n startValue?: number | string;\r\n endValue?: number | string;\r\n };\r\n const userZoomRef = useRef<ZoomState | null>(null);\r\n const programmaticRef = useRef(false);\r\n\r\n // Event handlers\r\n const handleEvents = useMemo(() => {\r\n const events: Record<string, (params: unknown) => void> = {};\r\n\r\n if (onClick) {\r\n events.click = (params: unknown) => {\r\n onClick(params as ChartEventParams);\r\n };\r\n }\r\n\r\n if (onHover) {\r\n events.mouseover = (params: unknown) => {\r\n onHover(params as ChartEventParams);\r\n };\r\n }\r\n\r\n // Always wire datazoom so we can persist zoom state across data updates.\r\n // `onZoomChange` (if provided) is invoked alongside.\r\n events.datazoom = (params: unknown) => {\r\n if (programmaticRef.current) {\r\n // This event fired as a result of our own dispatchAction replay — ignore\r\n // so we don't keep overwriting the saved range from the user's last gesture.\r\n programmaticRef.current = false;\r\n return;\r\n }\r\n type DZParams = {\r\n start?: number;\r\n end?: number;\r\n startValue?: number | string;\r\n endValue?: number | string;\r\n batch?: Array<{\r\n start?: number;\r\n end?: number;\r\n startValue?: number | string;\r\n endValue?: number | string;\r\n }>;\r\n };\r\n const p = params as DZParams;\r\n const b = p.batch?.[0];\r\n const start = b?.start ?? p.start ?? 0;\r\n const end = b?.end ?? p.end ?? 100;\r\n const startValue = b?.startValue ?? p.startValue;\r\n const endValue = b?.endValue ?? p.endValue;\r\n // Only remember user-applied zoom; ignore the no-op full range so a\r\n // double-click \"reset\" returns to auto-follow behavior on the next update.\r\n const isFullRange = start <= 0 && end >= 100;\r\n userZoomRef.current = isFullRange\r\n ? null\r\n : { start, end, startValue, endValue };\r\n if (onZoomChange) onZoomChange({ start, end });\r\n };\r\n\r\n if (onBrushSelect) {\r\n events.brushselected = (params: unknown) => {\r\n onBrushSelect(\r\n params as Parameters<NonNullable<typeof onBrushSelect>>[0],\r\n );\r\n };\r\n }\r\n\r\n return events;\r\n }, [onClick, onHover, onZoomChange, onBrushSelect]);\r\n\r\n // After every options update, replay the user's zoom selection so streaming\r\n // data doesn't reset their view. Prefer absolute axis values when available\r\n // so the window stays anchored to the time range the user picked, not a\r\n // percentage that slides as more data arrives.\r\n useEffect(() => {\r\n const saved = userZoomRef.current;\r\n if (!saved) return;\r\n const chart = getChart() as\r\n | (ECharts & { isDisposed?: () => boolean })\r\n | undefined;\r\n // Guard against firing into a disposed instance — happens when parents\r\n // toggle the chart in/out (e.g. `series.length > 0 ? <AstroChart/> : ...`)\r\n // and React's effect runs while the previous instance is being torn down.\r\n if (\r\n !chart ||\r\n (typeof chart.isDisposed === \"function\" && chart.isDisposed())\r\n )\r\n return;\r\n const hasAbsolute =\r\n saved.startValue !== undefined && saved.endValue !== undefined;\r\n programmaticRef.current = true;\r\n try {\r\n chart.dispatchAction(\r\n hasAbsolute\r\n ? {\r\n type: \"dataZoom\",\r\n startValue: saved.startValue,\r\n endValue: saved.endValue,\r\n }\r\n : { type: \"dataZoom\", start: saved.start, end: saved.end },\r\n );\r\n } catch {\r\n // Instance vanished between the isDisposed check and the call — ignore.\r\n programmaticRef.current = false;\r\n }\r\n }, [options, getChart]);\r\n\r\n // Chart ready callback\r\n const handleChartReady = useCallback(\r\n (chart: unknown) => {\r\n onChartReady?.(chart);\r\n },\r\n [onChartReady],\r\n );\r\n\r\n // Custom export dropdown: always show when export is enabled (for consistent UI)\r\n const exportFormats = props.export?.formats ?? [\"png\"];\r\n const useCustomExportDropdown = props.export?.enabled;\r\n const [exportMenuOpen, setExportMenuOpen] = useState(false);\r\n const exportMenuRef = useRef<HTMLDivElement>(null);\r\n\r\n // Info tooltip state\r\n const [infoTooltipOpen, setInfoTooltipOpen] = useState(false);\r\n const infoTooltipRef = useRef<HTMLDivElement>(null);\r\n\r\n // Close info tooltip on outside click\r\n useEffect(() => {\r\n if (!infoTooltip || !infoTooltipOpen) return;\r\n const handleClickOutside = (e: MouseEvent) => {\r\n if (\r\n infoTooltipRef.current &&\r\n !infoTooltipRef.current.contains(e.target as Node)\r\n ) {\r\n setInfoTooltipOpen(false);\r\n }\r\n };\r\n document.addEventListener(\"mousedown\", handleClickOutside);\r\n return () =>\r\n document.removeEventListener(\"mousedown\", handleClickOutside);\r\n }, [infoTooltip, infoTooltipOpen]);\r\n\r\n useEffect(() => {\r\n if (!useCustomExportDropdown || !exportMenuOpen) return;\r\n const handleClickOutside = (e: MouseEvent) => {\r\n if (\r\n exportMenuRef.current &&\r\n !exportMenuRef.current.contains(e.target as Node)\r\n ) {\r\n setExportMenuOpen(false);\r\n }\r\n };\r\n document.addEventListener(\"mousedown\", handleClickOutside);\r\n return () =>\r\n document.removeEventListener(\"mousedown\", handleClickOutside);\r\n }, [useCustomExportDropdown, exportMenuOpen]);\r\n\r\n const handleExportFormat = useCallback(\r\n (format: \"png\" | \"jpeg\" | \"svg\" | \"csv\") => {\r\n const chart = getChart();\r\n if (format === \"csv\") {\r\n const rows: string[] = [];\r\n const headers = [\"Index\", ...series.map((s) => s.name)];\r\n rows.push(headers.join(\",\"));\r\n const maxLen = Math.max(...series.map((s) => s.data?.length || 0));\r\n for (let i = 0; i < maxLen; i++) {\r\n const row = [i.toString()];\r\n for (const s of series) {\r\n const data = s.data as unknown[];\r\n const value = data?.[i];\r\n if (typeof value === \"number\") row.push(value.toString());\r\n else if (Array.isArray(value))\r\n row.push(value[1]?.toString() || \"\");\r\n else if (value && typeof value === \"object\") {\r\n const point = value as DataPoint | TimeSeriesPoint;\r\n row.push(\r\n (\"value\" in point ? point.value : point.y)?.toString() ||\r\n \"\",\r\n );\r\n } else row.push(\"\");\r\n }\r\n rows.push(row.join(\",\"));\r\n }\r\n const csv = rows.join(\"\\n\");\r\n const filename = getExportFileName(\"csv\", true);\r\n const blob = new Blob([csv], { type: \"text/csv;charset=utf-8\" });\r\n const url = URL.createObjectURL(blob);\r\n const a = document.createElement(\"a\");\r\n a.href = url;\r\n a.download = filename;\r\n a.click();\r\n URL.revokeObjectURL(url);\r\n } else if (chart) {\r\n const imgType = format === \"jpeg\" ? \"jpeg\" : format;\r\n const dataUrl = chart.getDataURL({\r\n type: imgType as \"png\" | \"jpeg\" | \"svg\",\r\n pixelRatio: props.export?.pixelRatio || 2,\r\n backgroundColor: props.export?.backgroundColor || \"transparent\",\r\n });\r\n if (dataUrl) {\r\n const ext = format === \"jpeg\" ? \"jpg\" : format;\r\n const filename = getExportFileName(ext, true);\r\n const a = document.createElement(\"a\");\r\n a.href = dataUrl;\r\n a.download = filename;\r\n a.click();\r\n }\r\n }\r\n setExportMenuOpen(false);\r\n },\r\n [\r\n getChart,\r\n series,\r\n getExportFileName,\r\n props.export?.pixelRatio,\r\n props.export?.backgroundColor,\r\n ],\r\n );\r\n\r\n // Resize observer (guard so we never resize after unmount/dispose)\r\n useEffect(() => {\r\n const chart = getChart();\r\n if (!chart) return;\r\n\r\n let mounted = true;\r\n const resizeObserver = new ResizeObserver(() => {\r\n if (!mounted) return;\r\n try {\r\n chart.resize();\r\n } catch {\r\n // Chart may be disposed; ignore\r\n }\r\n });\r\n\r\n const container = chartRef.current?.ele;\r\n if (container) {\r\n resizeObserver.observe(container);\r\n }\r\n\r\n return () => {\r\n mounted = false;\r\n resizeObserver.disconnect();\r\n };\r\n }, [getChart]);\r\n\r\n // echarts-gl 3D series require canvas renderer (SVG mode crashes with isSingleCanvas error)\r\n const needs3DCanvas = useMemo(() => {\r\n const eo = props.echartsOptions;\r\n if (!eo) return false;\r\n if (\r\n eo.grid3D ||\r\n eo.xAxis3D ||\r\n eo.yAxis3D ||\r\n eo.zAxis3D ||\r\n eo.globe ||\r\n eo.geo3D\r\n )\r\n return true;\r\n if (Array.isArray(eo.series)) {\r\n return eo.series.some((s: any) => {\r\n const t = s?.type || \"\";\r\n return (\r\n t.includes(\"3D\") ||\r\n t.includes(\"3d\") ||\r\n t === \"scatter3D\" ||\r\n t === \"bar3D\" ||\r\n t === \"surface\" ||\r\n t === \"lines3D\"\r\n );\r\n });\r\n }\r\n return false;\r\n }, [props.echartsOptions]);\r\n\r\n // Check if data is empty (consider both series prop and echartsOptions.series)\r\n const hasSeriesData = series.some((s) => s.data && s.data.length > 0);\r\n const hasEchartsOptionsSeries =\r\n props.echartsOptions?.series &&\r\n (Array.isArray(props.echartsOptions.series)\r\n ? props.echartsOptions.series.length > 0\r\n : props.echartsOptions.series !== undefined);\r\n const hasData = hasSeriesData || hasEchartsOptionsSeries;\r\n\r\n // Status color mapping for header icon\r\n const getStatusColor = (status: ChartStatus | undefined): string => {\r\n switch (status) {\r\n case \"critical\":\r\n return tokens.colors.status?.critical || \"#ff3838\";\r\n case \"serious\":\r\n return tokens.colors.status?.serious || \"#ffb302\";\r\n case \"caution\":\r\n return tokens.colors.status?.caution || \"#fce83a\";\r\n case \"normal\":\r\n return tokens.colors.status?.normal || \"#56f000\";\r\n case \"standby\":\r\n return tokens.colors.status?.standby || \"#2dccff\";\r\n case \"off\":\r\n return tokens.colors.status?.off || \"#a4abb6\";\r\n default:\r\n return tokens.colors.text.secondary;\r\n }\r\n };\r\n\r\n // Check if we need to render the header (has icon or title)\r\n const hasHeader = !!(icon || props.title);\r\n\r\n // Container styles - glassmorphic for transparent themes (matching Container component)\r\n // Bold and Minimal themes get special glass styling\r\n const useGlassMode = isBoldVariant || isMinimalVariant;\r\n\r\n const containerStyle: React.CSSProperties = {\r\n width: typeof width === \"number\" ? `${width}px` : width,\r\n height: typeof height === \"number\" ? `${height}px` : height,\r\n borderRadius: tokens.borderRadius.lg,\r\n padding: tokens.spacing.sm,\r\n position: \"relative\",\r\n overflow: \"hidden\",\r\n // Glass mode styling for bold/minimal (matching Container)\r\n ...(useGlassMode\r\n ? {\r\n backgroundColor: \"rgba(10, 15, 25, 0.35)\",\r\n backdropFilter: \"blur(12px)\",\r\n WebkitBackdropFilter: \"blur(12px)\",\r\n border: `1px solid ${glassAccentMuted}`,\r\n borderTop: isBoldVariant\r\n ? `2px solid ${glassAccentColor}`\r\n : `1px solid ${glassAccentColor}`,\r\n transition: \"all 0.3s ease-out, box-shadow 0.3s ease-out\",\r\n }\r\n : isTransparentTheme\r\n ? {\r\n // Regular transparent (glass) theme — match top cards (ISS): transparent + blur\r\n backgroundColor: \"transparent\",\r\n backdropFilter: \"blur(12px)\",\r\n WebkitBackdropFilter: \"blur(12px)\",\r\n ...cardBorderStyle,\r\n }\r\n : {\r\n // Non-transparent themes — use same border as Container (muted) so chart doesn't stand out\r\n backgroundColor: tokens.colors.background.surface,\r\n border: `1px solid ${tokens.colors.border.muted}`,\r\n }),\r\n ...style,\r\n };\r\n\r\n // Glass gradient for bold/minimal themes\r\n const glassGradient = useGlassMode\r\n ? `linear-gradient(135deg, ${glassAccentColor}10 0%, ${glassAccentColor}03 100%)`\r\n : undefined;\r\n\r\n // Empty state\r\n if (!hasData && !loading) {\r\n return (\r\n <div\r\n className={`astro-chart astro-chart--empty ${className}`}\r\n style={containerStyle}\r\n role=\"group\"\r\n aria-roledescription=\"chart\"\r\n aria-label={ariaLabel || \"Empty chart\"}\r\n >\r\n {/* Glass gradient overlay for bold/minimal themes */}\r\n {useGlassMode && glassGradient && (\r\n <div\r\n style={{\r\n position: \"absolute\",\r\n inset: 0,\r\n background: glassGradient,\r\n pointerEvents: \"none\",\r\n zIndex: 0,\r\n }}\r\n />\r\n )}\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n height: \"100%\",\r\n color: tokens.colors.text.secondary,\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n fontSize: tokens.typography.fontSize.sm,\r\n position: \"relative\",\r\n zIndex: 1,\r\n }}\r\n >\r\n {emptyMessage}\r\n </div>\r\n </div>\r\n );\r\n }\r\n\r\n const exportImageFormats = exportFormats.filter((f) =>\r\n [\"png\", \"jpeg\", \"jpg\", \"svg\"].includes(f),\r\n );\r\n const hasCsv = exportFormats.includes(\"csv\");\r\n\r\n // Calculate header height for chart area adjustment\r\n const _headerHeight = icon && iconStatus ? 32 : 0;\r\n\r\n return (\r\n <div\r\n className={`astro-chart ${className}`}\r\n style={containerStyle}\r\n role=\"group\"\r\n aria-roledescription=\"chart\"\r\n aria-label={ariaLabel || props.title || \"Chart\"}\r\n >\r\n {/* Glass gradient overlay for bold/minimal themes (matching Container) */}\r\n {useGlassMode && glassGradient && (\r\n <div\r\n style={{\r\n position: \"absolute\",\r\n inset: 0,\r\n background: glassGradient,\r\n pointerEvents: \"none\",\r\n zIndex: 0,\r\n }}\r\n />\r\n )}\r\n\r\n {/* Header - BOLD variant (matching Container) */}\r\n {hasHeader && isBoldVariant && (\r\n <div style={{ position: \"relative\", zIndex: 1 }}>\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"space-between\",\r\n padding: `12px ${tokens.spacing.sm} 0`,\r\n }}\r\n >\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: headingConfig.gap,\r\n }}\r\n >\r\n {/* Icon with status badge - card heading system */}\r\n {icon && iconStatus && (\r\n <HeaderIconWithStatus\r\n icon={icon as AstroIconName}\r\n size={headingConfig.iconSize}\r\n status={iconStatus}\r\n statusColor={getStatusColor(iconStatus)}\r\n />\r\n )}\r\n {icon && !iconStatus && (\r\n <AstroIcon\r\n name={icon as AstroIconName}\r\n size={headingConfig.iconSize}\r\n color={glassAccentColor}\r\n />\r\n )}\r\n {props.title && (\r\n <h3\r\n style={{\r\n margin: 0,\r\n fontSize: chartTitleFontSize,\r\n fontWeight: headingConfig.titleFontWeight,\r\n color: glassAccentColor,\r\n letterSpacing: \"0.5px\",\r\n textShadow: `0 0 20px ${glassAccentMuted}`,\r\n }}\r\n >\r\n {props.title}\r\n </h3>\r\n )}\r\n </div>\r\n </div>\r\n {/* Accent decorative line with status badge */}\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: \"8px\",\r\n padding: `10px ${tokens.spacing.sm} 0`,\r\n marginBottom: tokens.spacing.xs,\r\n }}\r\n >\r\n <div\r\n style={{\r\n flex: 1,\r\n height: \"2px\",\r\n background: `linear-gradient(90deg, ${glassAccentColor} 0%, ${glassAccentMuted} 50%, transparent 100%)`,\r\n boxShadow: `0 0 8px ${glassAccentMuted}`,\r\n }}\r\n />\r\n {/* Status badge at end of line (optional) */}\r\n {showStatusBadge && iconStatus && (\r\n <span\r\n style={{\r\n display: \"inline-flex\",\r\n alignItems: \"center\",\r\n gap: \"4px\",\r\n padding: \"2px 8px\",\r\n fontSize: \"0.625rem\", // 10px in rem\r\n fontWeight: 500, // AstroUXDS medium (not 600)\r\n letterSpacing: \"0.05em\",\r\n color: getStatusColor(iconStatus),\r\n backgroundColor: `${getStatusColor(iconStatus)}20`,\r\n border: `1px solid ${getStatusColor(iconStatus)}40`,\r\n borderRadius: \"3px\",\r\n whiteSpace: \"nowrap\",\r\n }}\r\n >\r\n {STATUS_SHAPES[iconStatus](6, getStatusColor(iconStatus))}\r\n {STATUS_LABELS[iconStatus]}\r\n </span>\r\n )}\r\n </div>\r\n </div>\r\n )}\r\n\r\n {/* Header - MINIMAL variant (inline thin line, matching Container) */}\r\n {hasHeader && isMinimalVariant && (\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n padding: `${tokens.spacing.xs} ${tokens.spacing.sm} 0`,\r\n marginBottom: tokens.spacing.xs,\r\n position: \"relative\",\r\n zIndex: 1,\r\n }}\r\n >\r\n {icon && iconStatus && (\r\n <HeaderIconWithStatus\r\n icon={icon as AstroIconName}\r\n size={headingConfig.iconSizeCompact}\r\n status={iconStatus}\r\n statusColor={getStatusColor(iconStatus)}\r\n style={{ marginRight: tokens.spacing.sm }}\r\n />\r\n )}\r\n {icon && !iconStatus && (\r\n <AstroIcon\r\n name={icon as AstroIconName}\r\n size={headingConfig.iconSizeCompact}\r\n color={glassAccentColor}\r\n style={{ marginRight: tokens.spacing.sm }}\r\n />\r\n )}\r\n {props.title && (\r\n <h3\r\n style={{\r\n margin: 0,\r\n fontSize: chartTitleFontSize,\r\n fontWeight: headingConfig.titleFontWeight,\r\n color: glassAccentColor,\r\n textShadow: `0 0 16px ${glassAccentMuted}`,\r\n letterSpacing: \"0.02em\",\r\n whiteSpace: \"nowrap\",\r\n marginRight: \"12px\",\r\n }}\r\n >\r\n {props.title}\r\n </h3>\r\n )}\r\n {/* Accent line with status badge */}\r\n <div\r\n style={{\r\n flex: 1,\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: headingConfig.gap,\r\n minWidth: 40,\r\n }}\r\n >\r\n <div\r\n style={{\r\n flex: 1,\r\n height: \"1px\",\r\n background: `linear-gradient(90deg, ${glassAccentColor} 0%, ${glassAccentMuted} 60%, transparent 100%)`,\r\n }}\r\n />\r\n {/* Status badge at end of line (optional) */}\r\n {showStatusBadge && iconStatus && (\r\n <span\r\n style={{\r\n display: \"inline-flex\",\r\n alignItems: \"center\",\r\n gap: \"3px\",\r\n padding: \"1px 6px\",\r\n fontSize: \"0.5625rem\", // 9px in rem (micro)\r\n fontWeight: 500, // AstroUXDS medium (not 600)\r\n letterSpacing: \"0.05em\",\r\n color: getStatusColor(iconStatus),\r\n backgroundColor: `${getStatusColor(iconStatus)}15`,\r\n border: `1px solid ${getStatusColor(iconStatus)}30`,\r\n borderRadius: \"2px\",\r\n whiteSpace: \"nowrap\",\r\n }}\r\n >\r\n {STATUS_SHAPES[iconStatus](5, getStatusColor(iconStatus))}\r\n {STATUS_LABELS[iconStatus]}\r\n </span>\r\n )}\r\n </div>\r\n </div>\r\n )}\r\n\r\n {/* Header - Regular glass theme (no box/border) */}\r\n {hasHeader && isTransparentTheme && !useGlassMode && (\r\n <div\r\n title={statusMessage}\r\n style={{\r\n position: \"absolute\",\r\n top: 6,\r\n left: 8,\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: headingConfig.gap,\r\n zIndex: 10,\r\n }}\r\n >\r\n {icon && iconStatus && (\r\n <HeaderIconWithStatus\r\n icon={icon as AstroIconName}\r\n size={headingConfig.iconSizeCompact}\r\n status={iconStatus}\r\n statusColor={getStatusColor(iconStatus)}\r\n />\r\n )}\r\n {icon && !iconStatus && (\r\n <AstroIcon\r\n name={icon as AstroIconName}\r\n size={headingConfig.iconSizeCompact}\r\n color={tokens.colors.text.tertiary}\r\n />\r\n )}\r\n {props.title && (\r\n <span\r\n style={{\r\n fontSize: chartTitleFontSize,\r\n fontWeight: headingConfig.titleFontWeight,\r\n color: tokens.colors.text.primary,\r\n }}\r\n >\r\n {props.title}\r\n </span>\r\n )}\r\n </div>\r\n )}\r\n\r\n {/* Header - Non-transparent themes (Zen/Astro): same top-left layout, no box/border */}\r\n {hasHeader && !isTransparentTheme && (\r\n <div\r\n title={statusMessage}\r\n style={{\r\n position: \"absolute\",\r\n top: 6,\r\n left: 8,\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: headingConfig.gap,\r\n zIndex: 10,\r\n }}\r\n >\r\n {icon && iconStatus && (\r\n <HeaderIconWithStatus\r\n icon={icon as AstroIconName}\r\n size={headingConfig.iconSizeCompact}\r\n status={iconStatus}\r\n statusColor={getStatusColor(iconStatus)}\r\n />\r\n )}\r\n {icon && !iconStatus && (\r\n <AstroIcon\r\n name={icon as AstroIconName}\r\n size={headingConfig.iconSizeCompact}\r\n color={tokens.colors.text.tertiary}\r\n />\r\n )}\r\n {props.title && (\r\n <span\r\n style={{\r\n fontSize: chartTitleFontSize,\r\n fontWeight: headingConfig.titleFontWeight,\r\n color: tokens.colors.text.primary,\r\n }}\r\n >\r\n {props.title}\r\n </span>\r\n )}\r\n </div>\r\n )}\r\n {/* SVG renderer by default for sharp text; canvas is forced when echarts-gl 3D series are present (gl requires canvas) */}\r\n <ReactEChartsCore\r\n ref={chartRef}\r\n echarts={echarts}\r\n option={options}\r\n opts={{\r\n renderer: props.renderer ?? (needs3DCanvas ? \"canvas\" : \"svg\"),\r\n ...((props.renderer === \"canvas\" || needs3DCanvas) &&\r\n typeof window !== \"undefined\" && {\r\n devicePixelRatio: window.devicePixelRatio || 1,\r\n }),\r\n }}\r\n style={{ width: \"100%\", height: \"100%\" }}\r\n notMerge={true}\r\n lazyUpdate={true}\r\n showLoading={loading}\r\n loadingOption={{\r\n text: \"Loading...\",\r\n color: tokens.colors.accent.primary,\r\n textColor: tokens.colors.text.primary,\r\n // Hex-alpha suffixing only works on hex tokens; surface is an\r\n // rgba() string in several themes, so use the overlay token,\r\n // which is defined with mask-appropriate alpha in every theme.\r\n maskColor: isTransparentTheme\r\n ? \"rgba(10, 15, 25, 0.6)\"\r\n : tokens.colors.background.overlay,\r\n }}\r\n onEvents={handleEvents}\r\n onChartReady={handleChartReady}\r\n />\r\n {/* Chart toolbar - compact, positioned top right */}\r\n <div\r\n style={{\r\n position: \"absolute\",\r\n right: 8,\r\n top: 6,\r\n zIndex: 10,\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: 4,\r\n }}\r\n >\r\n {/* Info tooltip button */}\r\n {infoTooltip && (\r\n <div ref={infoTooltipRef} style={{ position: \"relative\" }}>\r\n <button\r\n type=\"button\"\r\n onClick={() => setInfoTooltipOpen((v) => !v)}\r\n aria-label=\"Chart information\"\r\n aria-expanded={infoTooltipOpen}\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n width: 20,\r\n height: 20,\r\n padding: 4,\r\n border: `1px solid ${tokens.colors.border.default}`,\r\n borderRadius: \"50%\",\r\n background: infoTooltipOpen\r\n ? tokens.colors.accent.primary\r\n : isTransparentTheme\r\n ? \"rgba(10, 15, 25, 0.5)\"\r\n : tokens.colors.background.surface,\r\n color: infoTooltipOpen\r\n ? tokens.colors.background.base\r\n : tokens.colors.text.secondary,\r\n cursor: \"pointer\",\r\n transition: \"all 0.15s ease\",\r\n }}\r\n >\r\n {infoTooltip.icon || (\r\n <svg\r\n width=\"10\"\r\n height=\"10\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"currentColor\"\r\n aria-hidden=\"true\"\r\n >\r\n <path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z\" />\r\n </svg>\r\n )}\r\n </button>\r\n {infoTooltipOpen && (\r\n <div\r\n role=\"tooltip\"\r\n style={{\r\n position: \"absolute\",\r\n right: 0,\r\n top: \"100%\",\r\n marginTop: 8,\r\n minWidth: 200,\r\n maxWidth: infoTooltip.maxWidth || 300,\r\n padding: \"12px 16px\",\r\n background: isTransparentTheme\r\n ? \"rgba(10, 15, 25, 0.85)\"\r\n : tokens.colors.background.surface,\r\n backdropFilter: isTransparentTheme\r\n ? \"blur(12px)\"\r\n : undefined,\r\n border: `1px solid ${tokens.colors.border.default}`,\r\n borderRadius: tokens.borderRadius.md,\r\n boxShadow: \"0 4px 16px rgba(0,0,0,0.25)\",\r\n color: tokens.colors.text.primary,\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n fontSize: tokens.typography.fontSize.sm,\r\n lineHeight: 1.5,\r\n zIndex: 20,\r\n }}\r\n >\r\n {infoTooltip.content}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n\r\n {/* Export dropdown - compact icon only */}\r\n {useCustomExportDropdown && (\r\n <div ref={exportMenuRef} style={{ position: \"relative\" }}>\r\n <button\r\n type=\"button\"\r\n onClick={() => setExportMenuOpen((v) => !v)}\r\n aria-haspopup=\"true\"\r\n aria-expanded={exportMenuOpen}\r\n aria-label=\"Save chart as\"\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n width: 20,\r\n height: 20,\r\n padding: 4,\r\n border: `1px solid ${tokens.colors.border.default}`,\r\n borderRadius: \"50%\",\r\n background: isTransparentTheme\r\n ? \"rgba(10, 15, 25, 0.5)\"\r\n : tokens.colors.background.surface,\r\n color: tokens.colors.text.secondary,\r\n cursor: \"pointer\",\r\n transition: \"all 0.15s ease\",\r\n }}\r\n >\r\n <svg\r\n width=\"10\"\r\n height=\"10\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n strokeLinejoin=\"round\"\r\n aria-hidden=\"true\"\r\n >\r\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\r\n <polyline points=\"7 10 12 15 17 10\" />\r\n <line x1=\"12\" y1=\"15\" x2=\"12\" y2=\"3\" />\r\n </svg>\r\n </button>\r\n {exportMenuOpen && (\r\n <div\r\n role=\"menu\"\r\n style={{\r\n position: \"absolute\",\r\n right: 0,\r\n top: \"100%\",\r\n marginTop: 4,\r\n minWidth: 120,\r\n padding: 4,\r\n background: tokens.colors.background.surface,\r\n border: `1px solid ${tokens.colors.border.default}`,\r\n borderRadius: tokens.borderRadius.md,\r\n boxShadow: \"0 4px 12px rgba(0,0,0,0.2)\",\r\n }}\r\n >\r\n {exportImageFormats.map((f) => (\r\n <button\r\n key={f}\r\n type=\"button\"\r\n role=\"menuitem\"\r\n onClick={() =>\r\n handleExportFormat(\r\n f === \"jpg\"\r\n ? \"jpeg\"\r\n : (f as \"png\" | \"jpeg\" | \"svg\"),\r\n )\r\n }\r\n style={{\r\n display: \"block\",\r\n width: \"100%\",\r\n padding: \"8px 12px\",\r\n border: \"none\",\r\n borderRadius: tokens.borderRadius.sm,\r\n background: \"transparent\",\r\n color: tokens.colors.text.primary,\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n fontSize: tokens.typography.fontSize.sm,\r\n textAlign: \"left\",\r\n cursor: \"pointer\",\r\n }}\r\n >\r\n {f.toUpperCase()}\r\n </button>\r\n ))}\r\n {hasCsv && (\r\n <button\r\n type=\"button\"\r\n role=\"menuitem\"\r\n onClick={() => handleExportFormat(\"csv\")}\r\n style={{\r\n display: \"block\",\r\n width: \"100%\",\r\n padding: \"8px 12px\",\r\n border: \"none\",\r\n borderRadius: tokens.borderRadius.sm,\r\n background: \"transparent\",\r\n color: tokens.colors.text.primary,\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n fontSize: tokens.typography.fontSize.sm,\r\n textAlign: \"left\",\r\n cursor: \"pointer\",\r\n }}\r\n >\r\n CSV\r\n </button>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n );\r\n },\r\n ),\r\n);\r\n\r\nexport default AstroChart;\r\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DataTable.js","names":[],"sources":["../../../../src/react/core/data/DataTable.tsx"],"sourcesContent":["/**\r\n * @zendir/ui - DataTable Component\r\n *\r\n * Structured data display with expandable groups, responsive columns,\r\n * and theme-aware styling. Designed for telemetry parameter grids,\r\n * link budget displays, spacecraft information panels.\r\n *\r\n * Astro UX Compliance:\r\n * - Tabular monospace numbers for value columns\r\n * - Status-based row coloring\r\n * - Compact mode support\r\n * - Accessible table semantics\r\n *\r\n * Enhanced Tooltip System:\r\n * - Row-level tooltips (string or rich ReactNode via rowTooltipContent)\r\n * - Cell-level tooltips per column definition\r\n * - Header tooltips for column descriptions\r\n * - Built-in DataTableRowDetail component for structured detail views\r\n * - Portal-rendered, WCAG-compliant, animated tooltips\r\n *\r\n * @example\r\n * ```tsx\r\n * <DataTable\r\n * columns={[\r\n * { key: 'label', header: 'Parameter', width: '40%', headerTooltip: 'Telemetry parameter name' },\r\n * { key: 'value', header: 'Value', align: 'right', mono: true, cellTooltip: (v, row) => `Raw: ${v}` },\r\n * { key: 'unit', header: 'Unit', width: 80 },\r\n * ]}\r\n * data={[\r\n * { label: 'Frequency', value: '437.500', unit: 'MHz' },\r\n * { label: 'Bandwidth', value: '25.000', unit: 'kHz' },\r\n * ]}\r\n * rowTooltipContent={(row) => (\r\n * <DataTableRowDetail\r\n * title={row.label}\r\n * fields={[\r\n * { label: 'Value', value: `${row.value} ${row.unit}` },\r\n * ]}\r\n * />\r\n * )}\r\n * />\r\n * ```\r\n */\r\n\r\nimport React, { memo, useState, useCallback, useMemo } from \"react\";\r\nimport { useTheme } from \"../../theme\";\r\nimport { useBreakpoint } from \"../layout/useBreakpoint\";\r\nimport { Tooltip } from \"../overlays/Tooltip\";\r\nimport type { TooltipPlacement } from \"../overlays/Tooltip\";\r\nimport { addAlpha } from \"../../utils\";\r\n\r\n// ─── Types ───────────────────────────────────────────────────────────────────\r\n\r\nexport interface DataTableColumn<T = Record<string, unknown>> {\r\n /** Key in data object */\r\n key: string;\r\n /** Column header text */\r\n header: string;\r\n /** Column width (CSS value or number for px) */\r\n width?: string | number;\r\n /** Text alignment */\r\n align?: \"left\" | \"center\" | \"right\";\r\n /** Use monospace font (good for numbers) */\r\n mono?: boolean;\r\n /** Custom cell renderer */\r\n render?: (value: unknown, row: T, index: number) => React.ReactNode;\r\n /** Hide column on mobile */\r\n hideOnMobile?: boolean;\r\n /** Tooltip for the column header (explains what this column represents) */\r\n headerTooltip?: string;\r\n /** Per-cell tooltip — return string or ReactNode, or undefined to skip */\r\n cellTooltip?: (\r\n value: unknown,\r\n row: T,\r\n index: number,\r\n ) => React.ReactNode | undefined;\r\n}\r\n\r\nexport interface DataTableProps<T = Record<string, unknown>> {\r\n /** Column definitions */\r\n columns: DataTableColumn<T>[];\r\n /** Data rows */\r\n data: T[];\r\n /** Key field for React keys (default: index) */\r\n keyField?: string;\r\n /** Group rows by this field */\r\n groupBy?: string;\r\n /** Allow expanding/collapsing groups */\r\n expandable?: boolean;\r\n /** Default expanded state for groups */\r\n defaultExpanded?: boolean;\r\n /** Striped rows */\r\n striped?: boolean;\r\n /** Compact row padding */\r\n compact?: boolean;\r\n /** Show header row */\r\n showHeader?: boolean;\r\n /** Empty state message */\r\n emptyMessage?: string;\r\n /** Row click handler */\r\n onRowClick?: (row: T, index: number) => void;\r\n /** Simple string tooltip for each row (legacy — uses native title) */\r\n rowTooltip?: (row: T) => string | undefined;\r\n /** Rich tooltip content for each row — renders in a portal-based Tooltip */\r\n rowTooltipContent?: (row: T, index: number) => React.ReactNode | undefined;\r\n /** Placement for row and cell tooltips (default: 'top') */\r\n tooltipPlacement?: TooltipPlacement;\r\n /** Delay before tooltips appear in ms (default: 300) */\r\n tooltipDelay?: number;\r\n /** Max height with scroll */\r\n maxHeight?: string | number;\r\n /** Color for boolean ✓/✗ values (e.g. '#fff' for white). Defaults to text.primary or row status. */\r\n booleanColor?: string;\r\n /** Optional row status key or getter — boolean cells use this row's status color when set (matches white/status design). */\r\n getRowStatus?: (\r\n row: T,\r\n index: number,\r\n ) =>\r\n | \"normal\"\r\n | \"caution\"\r\n | \"serious\"\r\n | \"critical\"\r\n | \"standby\"\r\n | \"off\"\r\n | undefined;\r\n /** Custom style */\r\n style?: React.CSSProperties;\r\n}\r\n\r\n// ─── DataTableRowDetail ──────────────────────────────────────────────────────\r\n\r\nexport interface DataTableRowDetailField {\r\n /** Field label */\r\n label: string;\r\n /** Field value (string, number, or ReactNode for custom rendering) */\r\n value: React.ReactNode;\r\n /** Optional status color for the value */\r\n status?: \"normal\" | \"caution\" | \"serious\" | \"critical\" | \"standby\" | \"off\";\r\n /** Use monospace font for this value */\r\n mono?: boolean;\r\n}\r\n\r\nexport interface DataTableRowDetailProps {\r\n /** Title shown at the top of the tooltip */\r\n title?: React.ReactNode;\r\n /** Optional subtitle / secondary info */\r\n subtitle?: React.ReactNode;\r\n /** Structured key-value fields to display */\r\n fields?: DataTableRowDetailField[];\r\n /** Optional footer content (timestamps, actions, etc.) */\r\n footer?: React.ReactNode;\r\n /** Optional icon or status indicator beside the title */\r\n icon?: React.ReactNode;\r\n /** Maximum width of the tooltip (default: 320) */\r\n maxWidth?: number;\r\n}\r\n\r\n/**\r\n * Pre-built tooltip content renderer for DataTable rows.\r\n * Provides a structured layout: title, key-value fields, and optional footer.\r\n *\r\n * @example\r\n * ```tsx\r\n * <DataTable\r\n * columns={columns}\r\n * data={data}\r\n * rowTooltipContent={(row) => (\r\n * <DataTableRowDetail\r\n * title={row.parameter}\r\n * subtitle={row.category}\r\n * fields={[\r\n * { label: 'Value', value: row.value, mono: true },\r\n * { label: 'Status', value: row.status, status: row.status },\r\n * ]}\r\n * footer={`Last updated: ${row.timestamp}`}\r\n * />\r\n * )}\r\n * />\r\n * ```\r\n */\r\nexport const DataTableRowDetail = memo(function DataTableRowDetail({\r\n title,\r\n subtitle,\r\n fields,\r\n footer,\r\n icon,\r\n maxWidth = 320,\r\n}: DataTableRowDetailProps): React.ReactElement {\r\n const { tokens } = useTheme();\r\n\r\n return (\r\n <div style={{ maxWidth, minWidth: 180 }}>\r\n {/* Header */}\r\n {(title || icon) && (\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: tokens.spacing.sm,\r\n marginBottom: fields?.length || subtitle ? tokens.spacing.sm : 0,\r\n }}\r\n >\r\n {icon}\r\n <div style={{ flex: 1, minWidth: 0 }}>\r\n {title && (\r\n <div\r\n style={{\r\n fontWeight: tokens.typography.fontWeight.semibold,\r\n fontSize: tokens.typography.fontSize.sm,\r\n color: tokens.colors.text.primary,\r\n lineHeight: 1.3,\r\n overflow: \"hidden\",\r\n textOverflow: \"ellipsis\",\r\n }}\r\n >\r\n {title}\r\n </div>\r\n )}\r\n {subtitle && (\r\n <div\r\n style={{\r\n fontSize: tokens.typography.fontSize.xxs,\r\n color: tokens.colors.text.tertiary,\r\n marginTop: 2,\r\n }}\r\n >\r\n {subtitle}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n )}\r\n\r\n {/* Fields */}\r\n {fields && fields.length > 0 && (\r\n <div\r\n style={{\r\n display: \"grid\",\r\n gridTemplateColumns: \"auto 1fr\",\r\n gap: `${tokens.spacing.xs} ${tokens.spacing.md}`,\r\n fontSize: tokens.typography.fontSize.xs,\r\n lineHeight: 1.5,\r\n }}\r\n >\r\n {fields.map((field, i) => (\r\n <React.Fragment key={i}>\r\n <span\r\n style={{\r\n color: tokens.colors.text.tertiary,\r\n whiteSpace: \"nowrap\",\r\n }}\r\n >\r\n {field.label}\r\n </span>\r\n <span\r\n style={{\r\n color: field.status\r\n ? tokens.colors.status[field.status] ||\r\n tokens.colors.text.primary\r\n : tokens.colors.text.primary,\r\n fontFamily: field.mono\r\n ? tokens.typography.fontFamily.mono\r\n : \"inherit\",\r\n fontVariantNumeric: field.mono ? \"tabular-nums\" : \"normal\",\r\n textAlign: \"right\",\r\n overflow: \"hidden\",\r\n textOverflow: \"ellipsis\",\r\n whiteSpace: \"nowrap\",\r\n }}\r\n >\r\n {field.value}\r\n </span>\r\n </React.Fragment>\r\n ))}\r\n </div>\r\n )}\r\n\r\n {/* Footer */}\r\n {footer && (\r\n <div\r\n style={{\r\n marginTop: tokens.spacing.sm,\r\n paddingTop: tokens.spacing.xs,\r\n borderTop: `1px solid ${addAlpha(tokens.colors.border.muted, 0.19)}`,\r\n fontSize: tokens.typography.fontSize.xxs,\r\n color: tokens.colors.text.tertiary,\r\n }}\r\n >\r\n {footer}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n});\r\n\r\n// ─── Cell Tooltip Wrapper ────────────────────────────────────────────────────\r\n\r\ninterface CellTooltipWrapperProps {\r\n content: React.ReactNode;\r\n placement: TooltipPlacement;\r\n delay: number;\r\n children: React.ReactElement;\r\n}\r\n\r\nconst CellTooltipWrapper = memo(function CellTooltipWrapper({\r\n content,\r\n placement,\r\n delay,\r\n children,\r\n}: CellTooltipWrapperProps): React.ReactElement {\r\n if (!content) return children;\r\n return (\r\n <Tooltip content={content} placement={placement} delay={delay}>\r\n {children}\r\n </Tooltip>\r\n );\r\n});\r\n\r\n// ─── DataTable ───────────────────────────────────────────────────────────────\r\n\r\nexport const DataTable = memo(function DataTable<\r\n T extends Record<string, unknown>,\r\n>({\r\n columns,\r\n data,\r\n keyField,\r\n groupBy,\r\n expandable = false,\r\n defaultExpanded = true,\r\n striped = false,\r\n compact = false,\r\n showHeader = true,\r\n emptyMessage = \"No data available\",\r\n onRowClick,\r\n rowTooltip,\r\n rowTooltipContent,\r\n tooltipPlacement = \"top\",\r\n tooltipDelay = 300,\r\n maxHeight,\r\n booleanColor,\r\n getRowStatus,\r\n style,\r\n}: DataTableProps<T>): React.ReactElement {\r\n const { tokens } = useTheme();\r\n const { isMobile: isMobileView } = useBreakpoint();\r\n const statusColorMap = useMemo(\r\n () => ({\r\n normal: tokens.colors.status.normal,\r\n caution: tokens.colors.status.caution,\r\n serious: tokens.colors.status.serious,\r\n critical: tokens.colors.status.critical,\r\n standby: tokens.colors.status.standby,\r\n off: tokens.colors.status.off,\r\n }),\r\n [tokens.colors.status],\r\n );\r\n const visibleColumns = isMobileView\r\n ? columns.filter((col) => !col.hideOnMobile)\r\n : columns;\r\n\r\n const [expandedGroups, setExpandedGroups] = useState<Set<string>>(\r\n defaultExpanded ? new Set([\"__all__\"]) : new Set(),\r\n );\r\n const [hoveredRow, setHoveredRow] = useState<number | null>(null);\r\n const visibleColumnCount = Math.max(1, visibleColumns.length);\r\n\r\n const cellPadding = compact\r\n ? `${tokens.spacing.xs} ${tokens.spacing.sm}`\r\n : `${tokens.spacing.sm} ${tokens.spacing.md}`;\r\n\r\n const toggleGroup = useCallback((group: string) => {\r\n setExpandedGroups((prev) => {\r\n const next = new Set(prev);\r\n if (next.has(group)) next.delete(group);\r\n else next.add(group);\r\n return next;\r\n });\r\n }, []);\r\n\r\n const isGroupExpanded = useCallback(\r\n (group: string) => {\r\n return expandedGroups.has(\"__all__\") || expandedGroups.has(group);\r\n },\r\n [expandedGroups],\r\n );\r\n\r\n const groups = useMemo(() => {\r\n if (!groupBy) return [{ key: \"__default__\", label: \"\", rows: data }];\r\n\r\n const map = new Map<string, T[]>();\r\n data.forEach((row) => {\r\n const key = String((row as Record<string, unknown>)[groupBy] ?? \"Other\");\r\n if (!map.has(key)) map.set(key, []);\r\n map.get(key)!.push(row);\r\n });\r\n\r\n return Array.from(map.entries()).map(([key, rows]) => ({\r\n key,\r\n label: key,\r\n rows,\r\n }));\r\n }, [data, groupBy]);\r\n\r\n const resolveWidth = (w: string | number | undefined): string | undefined => {\r\n if (w === undefined) return undefined;\r\n if (typeof w === \"number\") return `${w}px`;\r\n return w;\r\n };\r\n\r\n const renderCell = (col: DataTableColumn<T>, row: T, rowIndex: number) => {\r\n const value = row[col.key];\r\n if (col.render) return col.render(value, row, rowIndex);\r\n if (value === null || value === undefined) return \"—\";\r\n if (typeof value === \"boolean\") {\r\n const label = value ? \"✓\" : \"✗\";\r\n const color =\r\n booleanColor ??\r\n (getRowStatus\r\n ? (() => {\r\n const s = getRowStatus(row, rowIndex);\r\n return s ? statusColorMap[s] : undefined;\r\n })()\r\n : undefined) ??\r\n tokens.colors.text.primary;\r\n return <span style={{ color }}>{label}</span>;\r\n }\r\n return String(value);\r\n };\r\n\r\n const renderCellWithTooltip = (\r\n col: DataTableColumn<T>,\r\n row: T,\r\n rowIndex: number,\r\n ) => {\r\n const cellContent = renderCell(col, row, rowIndex);\r\n if (!col.cellTooltip) return cellContent;\r\n\r\n const tooltipContent = col.cellTooltip(row[col.key], row, rowIndex);\r\n if (!tooltipContent) return cellContent;\r\n\r\n return (\r\n <CellTooltipWrapper\r\n content={tooltipContent}\r\n placement={tooltipPlacement}\r\n delay={tooltipDelay}\r\n >\r\n <span style={{ cursor: \"help\" }}>{cellContent}</span>\r\n </CellTooltipWrapper>\r\n );\r\n };\r\n\r\n const renderHeaderCell = (col: DataTableColumn<T>) => {\r\n const headerContent = (\r\n <span style={{ display: \"inline-flex\", alignItems: \"center\", gap: 4 }}>\r\n {col.header}\r\n {col.headerTooltip && (\r\n <svg\r\n width=\"12\"\r\n height=\"12\"\r\n viewBox=\"0 0 16 16\"\r\n fill=\"none\"\r\n style={{ opacity: 0.5, flexShrink: 0 }}\r\n >\r\n <circle\r\n cx=\"8\"\r\n cy=\"8\"\r\n r=\"7\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"1.5\"\r\n />\r\n <path\r\n d=\"M8 7v4\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"1.5\"\r\n strokeLinecap=\"round\"\r\n />\r\n <circle cx=\"8\" cy=\"5\" r=\"0.75\" fill=\"currentColor\" />\r\n </svg>\r\n )}\r\n </span>\r\n );\r\n\r\n if (!col.headerTooltip) return headerContent;\r\n\r\n return (\r\n <Tooltip\r\n content={col.headerTooltip}\r\n placement=\"bottom\"\r\n delay={tooltipDelay}\r\n >\r\n {headerContent}\r\n </Tooltip>\r\n );\r\n };\r\n\r\n const chevron = (expanded: boolean) => (\r\n <svg\r\n width=\"14\"\r\n height=\"14\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n style={{\r\n transition: tokens.animation.fast,\r\n transform: expanded ? \"rotate(90deg)\" : \"rotate(0deg)\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n <polyline points=\"9 18 15 12 9 6\" />\r\n </svg>\r\n );\r\n\r\n const hasRichRowTooltip = !!rowTooltipContent;\r\n\r\n const renderRowContent = (row: T, rowIdx: number, localIndex: number) => {\r\n const isHovered = hoveredRow === rowIdx;\r\n const rowKey = keyField ? String(row[keyField]) : rowIdx;\r\n const useNativeTitle = !hasRichRowTooltip && rowTooltip;\r\n\r\n const tr = (\r\n <tr\r\n key={rowKey}\r\n tabIndex={onRowClick ? 0 : undefined}\r\n onClick={onRowClick ? () => onRowClick(row, rowIdx) : undefined}\r\n onKeyDown={\r\n onRowClick\r\n ? (e) => {\r\n if (e.key === \"Enter\" || e.key === \" \") {\r\n e.preventDefault();\r\n onRowClick(row, rowIdx);\r\n }\r\n }\r\n : undefined\r\n }\r\n onMouseEnter={() => setHoveredRow(rowIdx)}\r\n onMouseLeave={() => setHoveredRow(null)}\r\n title={useNativeTitle ? rowTooltip(row) : undefined}\r\n style={{\r\n cursor: onRowClick ? \"pointer\" : \"default\",\r\n backgroundColor: isHovered\r\n ? addAlpha(tokens.colors.accent.primary, 0.08)\r\n : striped && localIndex % 2 === 1\r\n ? addAlpha(tokens.colors.background.surface, 0.5)\r\n : \"transparent\",\r\n transition: \"background-color 150ms ease\",\r\n }}\r\n >\r\n {visibleColumns.map((col) => (\r\n <td\r\n key={col.key}\r\n style={{\r\n padding: cellPadding,\r\n textAlign: col.align || \"left\",\r\n color: tokens.colors.text.primary,\r\n borderBottom: `1px solid ${addAlpha(tokens.colors.border.muted, 0.08)}`,\r\n fontFamily: col.mono\r\n ? tokens.typography.fontFamily.mono\r\n : \"inherit\",\r\n fontVariantNumeric: col.mono ? \"tabular-nums\" : \"normal\",\r\n whiteSpace: \"nowrap\",\r\n overflow: \"hidden\",\r\n textOverflow: \"ellipsis\",\r\n maxWidth: resolveWidth(col.width) || \"300px\",\r\n }}\r\n >\r\n {renderCellWithTooltip(col, row, rowIdx)}\r\n </td>\r\n ))}\r\n </tr>\r\n );\r\n\r\n if (hasRichRowTooltip) {\r\n const tooltipContent = rowTooltipContent(row, rowIdx);\r\n if (tooltipContent) {\r\n return (\r\n <Tooltip\r\n key={rowKey}\r\n content={tooltipContent}\r\n placement={tooltipPlacement}\r\n delay={tooltipDelay}\r\n >\r\n {tr}\r\n </Tooltip>\r\n );\r\n }\r\n }\r\n\r\n return tr;\r\n };\r\n\r\n let globalRowIndex = 0;\r\n\r\n return (\r\n <div\r\n style={{\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n overflow: \"auto\",\r\n WebkitOverflowScrolling: \"touch\",\r\n maxHeight: typeof maxHeight === \"number\" ? `${maxHeight}px` : maxHeight,\r\n borderRadius: tokens.borderRadius.md,\r\n border: tokens.borders.divider,\r\n ...style,\r\n }}\r\n >\r\n <table\r\n style={{\r\n width: \"100%\",\r\n borderCollapse: \"collapse\",\r\n fontSize: compact\r\n ? tokens.typography.fontSize.xs\r\n : tokens.typography.fontSize.sm,\r\n }}\r\n >\r\n {showHeader && (\r\n <thead>\r\n <tr>\r\n {visibleColumns.map((col) => (\r\n <th\r\n key={col.key}\r\n style={{\r\n padding: cellPadding,\r\n textAlign: col.align || \"left\",\r\n fontSize: tokens.typography.fontSize.xxs,\r\n fontWeight: tokens.typography.fontWeight.bold,\r\n color: tokens.colors.text.tertiary,\r\n textTransform: \"uppercase\",\r\n letterSpacing: \"0.06em\",\r\n backgroundColor: tokens.colors.background.surface,\r\n borderBottom: tokens.borders.divider,\r\n width: resolveWidth(col.width),\r\n position: \"sticky\",\r\n top: 0,\r\n zIndex: 1,\r\n whiteSpace: \"nowrap\",\r\n }}\r\n >\r\n {renderHeaderCell(col)}\r\n </th>\r\n ))}\r\n </tr>\r\n </thead>\r\n )}\r\n\r\n <tbody>\r\n {data.length === 0 && (\r\n <tr>\r\n <td\r\n colSpan={visibleColumnCount}\r\n style={{\r\n padding: tokens.spacing.xl,\r\n textAlign: \"center\",\r\n color: tokens.colors.text.tertiary,\r\n fontSize: tokens.typography.fontSize.sm,\r\n }}\r\n >\r\n {emptyMessage}\r\n </td>\r\n </tr>\r\n )}\r\n\r\n {groups.map((group) => {\r\n const isExpanded = !groupBy || isGroupExpanded(group.key);\r\n\r\n return (\r\n <React.Fragment key={group.key}>\r\n {groupBy && group.label && (\r\n <tr\r\n style={{\r\n backgroundColor: addAlpha(\r\n tokens.colors.accent.primary,\r\n 0.08,\r\n ),\r\n }}\r\n >\r\n <td\r\n colSpan={visibleColumnCount}\r\n style={{\r\n padding: 0,\r\n borderBottom: tokens.borders.divider,\r\n }}\r\n >\r\n {expandable ? (\r\n <button\r\n type=\"button\"\r\n aria-expanded={isExpanded}\r\n onClick={() => toggleGroup(group.key)}\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: tokens.spacing.xs,\r\n width: \"100%\",\r\n padding: cellPadding,\r\n margin: 0,\r\n border: \"none\",\r\n background: \"none\",\r\n cursor: \"pointer\",\r\n fontWeight: tokens.typography.fontWeight.semibold,\r\n color: tokens.colors.text.primary,\r\n fontSize: compact\r\n ? tokens.typography.fontSize.xs\r\n : tokens.typography.fontSize.sm,\r\n fontFamily: \"inherit\",\r\n textAlign: \"left\",\r\n }}\r\n >\r\n {chevron(isExpanded)}\r\n {group.label}\r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.xxs,\r\n color: tokens.colors.text.tertiary,\r\n fontWeight: tokens.typography.fontWeight.normal,\r\n }}\r\n >\r\n ({group.rows.length})\r\n </span>\r\n </button>\r\n ) : (\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: tokens.spacing.xs,\r\n padding: cellPadding,\r\n fontWeight: tokens.typography.fontWeight.semibold,\r\n color: tokens.colors.text.primary,\r\n fontSize: compact\r\n ? tokens.typography.fontSize.xs\r\n : tokens.typography.fontSize.sm,\r\n }}\r\n >\r\n {group.label}\r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.xxs,\r\n color: tokens.colors.text.tertiary,\r\n fontWeight: tokens.typography.fontWeight.normal,\r\n }}\r\n >\r\n ({group.rows.length})\r\n </span>\r\n </div>\r\n )}\r\n </td>\r\n </tr>\r\n )}\r\n\r\n {isExpanded &&\r\n group.rows.map((row, localIndex) => {\r\n const rowIdx = globalRowIndex++;\r\n return renderRowContent(row, rowIdx, localIndex);\r\n })}\r\n </React.Fragment>\r\n );\r\n })}\r\n </tbody>\r\n </table>\r\n </div>\r\n );\r\n}) as <T extends Record<string, unknown>>(\r\n props: DataTableProps<T>,\r\n) => React.ReactElement;\r\n\r\nexport default DataTable;\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DataTable.js","names":[],"sources":["../../../../src/react/core/data/DataTable.tsx"],"sourcesContent":["/**\r\n * @zendir/ui - DataTable Component\r\n *\r\n * Structured data display with expandable groups, responsive columns,\r\n * and theme-aware styling. Designed for telemetry parameter grids,\r\n * link budget displays, spacecraft information panels.\r\n *\r\n * Astro UX Compliance:\r\n * - Tabular monospace numbers for value columns\r\n * - Status-based row coloring\r\n * - Compact mode support\r\n * - Accessible table semantics\r\n *\r\n * Enhanced Tooltip System:\r\n * - Row-level tooltips (string or rich ReactNode via rowTooltipContent)\r\n * - Cell-level tooltips per column definition\r\n * - Header tooltips for column descriptions\r\n * - Built-in DataTableRowDetail component for structured detail views\r\n * - Portal-rendered, WCAG-compliant, animated tooltips\r\n *\r\n * @example\r\n * ```tsx\r\n * <DataTable\r\n * columns={[\r\n * { key: 'label', header: 'Parameter', width: '40%', headerTooltip: 'Telemetry parameter name' },\r\n * { key: 'value', header: 'Value', align: 'right', mono: true, cellTooltip: (v, row) => `Raw: ${v}` },\r\n * { key: 'unit', header: 'Unit', width: 80 },\r\n * ]}\r\n * data={[\r\n * { label: 'Frequency', value: '437.500', unit: 'MHz' },\r\n * { label: 'Bandwidth', value: '25.000', unit: 'kHz' },\r\n * ]}\r\n * rowTooltipContent={(row) => (\r\n * <DataTableRowDetail\r\n * title={row.label}\r\n * fields={[\r\n * { label: 'Value', value: `${row.value} ${row.unit}` },\r\n * ]}\r\n * />\r\n * )}\r\n * />\r\n * ```\r\n */\r\n\r\nimport React, { memo, useState, useCallback, useMemo } from \"react\";\r\nimport { useTheme } from \"../../theme\";\r\nimport { useBreakpoint } from \"../layout/useBreakpoint\";\r\nimport { Tooltip } from \"../overlays/Tooltip\";\r\nimport type { TooltipPlacement } from \"../overlays/Tooltip\";\r\nimport { addAlpha } from \"../../utils\";\r\n\r\n// ─── Types ───────────────────────────────────────────────────────────────────\r\n\r\nexport interface DataTableColumn<T = Record<string, unknown>> {\r\n /** Key in data object */\r\n key: string;\r\n /** Column header text */\r\n header: string;\r\n /** Column width (CSS value or number for px) */\r\n width?: string | number;\r\n /** Text alignment */\r\n align?: \"left\" | \"center\" | \"right\";\r\n /** Use monospace font (good for numbers) */\r\n mono?: boolean;\r\n /** Custom cell renderer */\r\n render?: (value: unknown, row: T, index: number) => React.ReactNode;\r\n /** Hide column on mobile */\r\n hideOnMobile?: boolean;\r\n /** Tooltip for the column header (explains what this column represents) */\r\n headerTooltip?: string;\r\n /** Per-cell tooltip — return string or ReactNode, or undefined to skip */\r\n cellTooltip?: (\r\n value: unknown,\r\n row: T,\r\n index: number,\r\n ) => React.ReactNode | undefined;\r\n}\r\n\r\nexport interface DataTableProps<T = Record<string, unknown>> {\r\n /** Column definitions */\r\n columns: DataTableColumn<T>[];\r\n /** Data rows */\r\n data: T[];\r\n /** Key field for React keys (default: index) */\r\n keyField?: string;\r\n /** Group rows by this field */\r\n groupBy?: string;\r\n /** Allow expanding/collapsing groups */\r\n expandable?: boolean;\r\n /** Default expanded state for groups */\r\n defaultExpanded?: boolean;\r\n /** Striped rows */\r\n striped?: boolean;\r\n /** Compact row padding */\r\n compact?: boolean;\r\n /** Show header row */\r\n showHeader?: boolean;\r\n /** Empty state message */\r\n emptyMessage?: string;\r\n /** Row click handler */\r\n onRowClick?: (row: T, index: number) => void;\r\n /** Simple string tooltip for each row (legacy — uses native title) */\r\n rowTooltip?: (row: T) => string | undefined;\r\n /** Rich tooltip content for each row — renders in a portal-based Tooltip */\r\n rowTooltipContent?: (row: T, index: number) => React.ReactNode | undefined;\r\n /** Placement for row and cell tooltips (default: 'top') */\r\n tooltipPlacement?: TooltipPlacement;\r\n /** Delay before tooltips appear in ms (default: 300) */\r\n tooltipDelay?: number;\r\n /** Max height with scroll */\r\n maxHeight?: string | number;\r\n /** Color for boolean ✓/✗ values (e.g. '#fff' for white). Defaults to text.primary or row status. */\r\n booleanColor?: string;\r\n /** Optional row status key or getter — boolean cells use this row's status color when set (matches white/status design). */\r\n getRowStatus?: (\r\n row: T,\r\n index: number,\r\n ) =>\r\n | \"normal\"\r\n | \"caution\"\r\n | \"serious\"\r\n | \"critical\"\r\n | \"standby\"\r\n | \"off\"\r\n | undefined;\r\n /** Custom style */\r\n style?: React.CSSProperties;\r\n}\r\n\r\n// ─── DataTableRowDetail ──────────────────────────────────────────────────────\r\n\r\nexport interface DataTableRowDetailField {\r\n /** Field label */\r\n label: string;\r\n /** Field value (string, number, or ReactNode for custom rendering) */\r\n value: React.ReactNode;\r\n /** Optional status color for the value */\r\n status?: \"normal\" | \"caution\" | \"serious\" | \"critical\" | \"standby\" | \"off\";\r\n /** Use monospace font for this value */\r\n mono?: boolean;\r\n}\r\n\r\nexport interface DataTableRowDetailProps {\r\n /** Title shown at the top of the tooltip */\r\n title?: React.ReactNode;\r\n /** Optional subtitle / secondary info */\r\n subtitle?: React.ReactNode;\r\n /** Structured key-value fields to display */\r\n fields?: DataTableRowDetailField[];\r\n /** Optional footer content (timestamps, actions, etc.) */\r\n footer?: React.ReactNode;\r\n /** Optional icon or status indicator beside the title */\r\n icon?: React.ReactNode;\r\n /** Maximum width of the tooltip (default: 320) */\r\n maxWidth?: number;\r\n}\r\n\r\n/**\r\n * Pre-built tooltip content renderer for DataTable rows.\r\n * Provides a structured layout: title, key-value fields, and optional footer.\r\n *\r\n * @example\r\n * ```tsx\r\n * <DataTable\r\n * columns={columns}\r\n * data={data}\r\n * rowTooltipContent={(row) => (\r\n * <DataTableRowDetail\r\n * title={row.parameter}\r\n * subtitle={row.category}\r\n * fields={[\r\n * { label: 'Value', value: row.value, mono: true },\r\n * { label: 'Status', value: row.status, status: row.status },\r\n * ]}\r\n * footer={`Last updated: ${row.timestamp}`}\r\n * />\r\n * )}\r\n * />\r\n * ```\r\n */\r\nexport const DataTableRowDetail = memo(function DataTableRowDetail({\r\n title,\r\n subtitle,\r\n fields,\r\n footer,\r\n icon,\r\n maxWidth = 320,\r\n}: DataTableRowDetailProps): React.ReactElement {\r\n const { tokens } = useTheme();\r\n\r\n return (\r\n <div style={{ maxWidth, minWidth: 180 }}>\r\n {/* Header */}\r\n {(title || icon) && (\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: tokens.spacing.sm,\r\n marginBottom: fields?.length || subtitle ? tokens.spacing.sm : 0,\r\n }}\r\n >\r\n {icon}\r\n <div style={{ flex: 1, minWidth: 0 }}>\r\n {title && (\r\n <div\r\n style={{\r\n fontWeight: tokens.typography.fontWeight.semibold,\r\n fontSize: tokens.typography.fontSize.sm,\r\n color: tokens.colors.text.primary,\r\n lineHeight: 1.3,\r\n overflow: \"hidden\",\r\n textOverflow: \"ellipsis\",\r\n }}\r\n >\r\n {title}\r\n </div>\r\n )}\r\n {subtitle && (\r\n <div\r\n style={{\r\n fontSize: tokens.typography.fontSize.xxs,\r\n color: tokens.colors.text.tertiary,\r\n marginTop: 2,\r\n }}\r\n >\r\n {subtitle}\r\n </div>\r\n )}\r\n </div>\r\n </div>\r\n )}\r\n\r\n {/* Fields */}\r\n {fields && fields.length > 0 && (\r\n <div\r\n style={{\r\n display: \"grid\",\r\n gridTemplateColumns: \"auto 1fr\",\r\n gap: `${tokens.spacing.xs} ${tokens.spacing.md}`,\r\n fontSize: tokens.typography.fontSize.xs,\r\n lineHeight: 1.5,\r\n }}\r\n >\r\n {fields.map((field, i) => (\r\n <React.Fragment key={i}>\r\n <span\r\n style={{\r\n color: tokens.colors.text.tertiary,\r\n whiteSpace: \"nowrap\",\r\n }}\r\n >\r\n {field.label}\r\n </span>\r\n <span\r\n style={{\r\n color: field.status\r\n ? tokens.colors.status[field.status] ||\r\n tokens.colors.text.primary\r\n : tokens.colors.text.primary,\r\n fontFamily: field.mono\r\n ? tokens.typography.fontFamily.mono\r\n : \"inherit\",\r\n fontVariantNumeric: field.mono ? \"tabular-nums\" : \"normal\",\r\n textAlign: \"right\",\r\n overflow: \"hidden\",\r\n textOverflow: \"ellipsis\",\r\n whiteSpace: \"nowrap\",\r\n }}\r\n >\r\n {field.value}\r\n </span>\r\n </React.Fragment>\r\n ))}\r\n </div>\r\n )}\r\n\r\n {/* Footer */}\r\n {footer && (\r\n <div\r\n style={{\r\n marginTop: tokens.spacing.sm,\r\n paddingTop: tokens.spacing.xs,\r\n borderTop: `1px solid ${addAlpha(tokens.colors.border.muted, 0.19)}`,\r\n fontSize: tokens.typography.fontSize.xxs,\r\n color: tokens.colors.text.tertiary,\r\n }}\r\n >\r\n {footer}\r\n </div>\r\n )}\r\n </div>\r\n );\r\n});\r\n\r\n// ─── Cell Tooltip Wrapper ────────────────────────────────────────────────────\r\n\r\ninterface CellTooltipWrapperProps {\r\n content: React.ReactNode;\r\n placement: TooltipPlacement;\r\n delay: number;\r\n children: React.ReactElement;\r\n}\r\n\r\nconst CellTooltipWrapper = memo(function CellTooltipWrapper({\r\n content,\r\n placement,\r\n delay,\r\n children,\r\n}: CellTooltipWrapperProps): React.ReactElement {\r\n if (!content) return children;\r\n return (\r\n <Tooltip content={content} placement={placement} delay={delay}>\r\n {children}\r\n </Tooltip>\r\n );\r\n});\r\n\r\n// ─── DataTable ───────────────────────────────────────────────────────────────\r\n\r\nexport const DataTable = memo(function DataTable<\r\n T extends Record<string, unknown>,\r\n>({\r\n columns,\r\n data,\r\n keyField,\r\n groupBy,\r\n expandable = false,\r\n defaultExpanded = true,\r\n striped = false,\r\n compact = false,\r\n showHeader = true,\r\n emptyMessage = \"No data available\",\r\n onRowClick,\r\n rowTooltip,\r\n rowTooltipContent,\r\n tooltipPlacement = \"top\",\r\n tooltipDelay = 300,\r\n maxHeight,\r\n booleanColor,\r\n getRowStatus,\r\n style,\r\n}: DataTableProps<T>): React.ReactElement {\r\n const { tokens } = useTheme();\r\n const { isMobile: isMobileView } = useBreakpoint();\r\n const statusColorMap = useMemo(\r\n () => ({\r\n normal: tokens.colors.status.normal,\r\n caution: tokens.colors.status.caution,\r\n serious: tokens.colors.status.serious,\r\n critical: tokens.colors.status.critical,\r\n standby: tokens.colors.status.standby,\r\n off: tokens.colors.status.off,\r\n }),\r\n [tokens.colors.status],\r\n );\r\n const visibleColumns = isMobileView\r\n ? columns.filter((col) => !col.hideOnMobile)\r\n : columns;\r\n\r\n const [expandedGroups, setExpandedGroups] = useState<Set<string>>(\r\n defaultExpanded ? new Set([\"__all__\"]) : new Set(),\r\n );\r\n const [hoveredRow, setHoveredRow] = useState<number | null>(null);\r\n const visibleColumnCount = Math.max(1, visibleColumns.length);\r\n\r\n const cellPadding = compact\r\n ? `${tokens.spacing.xs} ${tokens.spacing.sm}`\r\n : `${tokens.spacing.sm} ${tokens.spacing.md}`;\r\n\r\n const toggleGroup = useCallback((group: string) => {\r\n setExpandedGroups((prev) => {\r\n const next = new Set(prev);\r\n if (next.has(group)) next.delete(group);\r\n else next.add(group);\r\n return next;\r\n });\r\n }, []);\r\n\r\n const isGroupExpanded = useCallback(\r\n (group: string) => {\r\n return expandedGroups.has(\"__all__\") || expandedGroups.has(group);\r\n },\r\n [expandedGroups],\r\n );\r\n\r\n const groups = useMemo(() => {\r\n if (!groupBy) return [{ key: \"__default__\", label: \"\", rows: data }];\r\n\r\n const map = new Map<string, T[]>();\r\n data.forEach((row) => {\r\n const key = String((row as Record<string, unknown>)[groupBy] ?? \"Other\");\r\n if (!map.has(key)) map.set(key, []);\r\n map.get(key)!.push(row);\r\n });\r\n\r\n return Array.from(map.entries()).map(([key, rows]) => ({\r\n key,\r\n label: key,\r\n rows,\r\n }));\r\n }, [data, groupBy]);\r\n\r\n const resolveWidth = (w: string | number | undefined): string | undefined => {\r\n if (w === undefined) return undefined;\r\n if (typeof w === \"number\") return `${w}px`;\r\n return w;\r\n };\r\n\r\n const renderCell = (col: DataTableColumn<T>, row: T, rowIndex: number) => {\r\n const value = row[col.key];\r\n if (col.render) return col.render(value, row, rowIndex);\r\n if (value === null || value === undefined) return \"—\";\r\n if (typeof value === \"boolean\") {\r\n const label = value ? \"✓\" : \"✗\";\r\n const color =\r\n booleanColor ??\r\n (getRowStatus\r\n ? (() => {\r\n const s = getRowStatus(row, rowIndex);\r\n return s ? statusColorMap[s] : undefined;\r\n })()\r\n : undefined) ??\r\n tokens.colors.text.primary;\r\n return <span style={{ color }}>{label}</span>;\r\n }\r\n return String(value);\r\n };\r\n\r\n const renderCellWithTooltip = (\r\n col: DataTableColumn<T>,\r\n row: T,\r\n rowIndex: number,\r\n ) => {\r\n const cellContent = renderCell(col, row, rowIndex);\r\n if (!col.cellTooltip) return cellContent;\r\n\r\n const tooltipContent = col.cellTooltip(row[col.key], row, rowIndex);\r\n if (!tooltipContent) return cellContent;\r\n\r\n return (\r\n <CellTooltipWrapper\r\n content={tooltipContent}\r\n placement={tooltipPlacement}\r\n delay={tooltipDelay}\r\n >\r\n <span style={{ cursor: \"help\" }}>{cellContent}</span>\r\n </CellTooltipWrapper>\r\n );\r\n };\r\n\r\n const renderHeaderCell = (col: DataTableColumn<T>) => {\r\n const headerContent = (\r\n <span style={{ display: \"inline-flex\", alignItems: \"center\", gap: 4 }}>\r\n {col.header}\r\n {col.headerTooltip && (\r\n <svg\r\n width=\"12\"\r\n height=\"12\"\r\n viewBox=\"0 0 16 16\"\r\n fill=\"none\"\r\n style={{ opacity: 0.5, flexShrink: 0 }}\r\n >\r\n <circle\r\n cx=\"8\"\r\n cy=\"8\"\r\n r=\"7\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"1.5\"\r\n />\r\n <path\r\n d=\"M8 7v4\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"1.5\"\r\n strokeLinecap=\"round\"\r\n />\r\n <circle cx=\"8\" cy=\"5\" r=\"0.75\" fill=\"currentColor\" />\r\n </svg>\r\n )}\r\n </span>\r\n );\r\n\r\n if (!col.headerTooltip) return headerContent;\r\n\r\n return (\r\n <Tooltip\r\n content={col.headerTooltip}\r\n placement=\"bottom\"\r\n delay={tooltipDelay}\r\n >\r\n {headerContent}\r\n </Tooltip>\r\n );\r\n };\r\n\r\n const chevron = (expanded: boolean) => (\r\n <svg\r\n width=\"14\"\r\n height=\"14\"\r\n viewBox=\"0 0 24 24\"\r\n fill=\"none\"\r\n stroke=\"currentColor\"\r\n strokeWidth=\"2\"\r\n strokeLinecap=\"round\"\r\n style={{\r\n transition: tokens.animation.fast,\r\n transform: expanded ? \"rotate(90deg)\" : \"rotate(0deg)\",\r\n flexShrink: 0,\r\n }}\r\n >\r\n <polyline points=\"9 18 15 12 9 6\" />\r\n </svg>\r\n );\r\n\r\n const hasRichRowTooltip = !!rowTooltipContent;\r\n\r\n const renderRowContent = (row: T, rowIdx: number, localIndex: number) => {\r\n const isHovered = hoveredRow === rowIdx;\r\n const rowKey = keyField ? String(row[keyField]) : rowIdx;\r\n const useNativeTitle = !hasRichRowTooltip && rowTooltip;\r\n\r\n const tr = (\r\n <tr\r\n key={rowKey}\r\n tabIndex={onRowClick ? 0 : undefined}\r\n onClick={onRowClick ? () => onRowClick(row, rowIdx) : undefined}\r\n onKeyDown={\r\n onRowClick\r\n ? (e) => {\r\n if (e.key === \"Enter\" || e.key === \" \") {\r\n e.preventDefault();\r\n onRowClick(row, rowIdx);\r\n }\r\n }\r\n : undefined\r\n }\r\n onMouseEnter={() => setHoveredRow(rowIdx)}\r\n onMouseLeave={() => setHoveredRow(null)}\r\n title={useNativeTitle ? rowTooltip(row) : undefined}\r\n style={{\r\n cursor: onRowClick ? \"pointer\" : \"default\",\r\n backgroundColor: isHovered\r\n ? addAlpha(tokens.colors.accent.primary, 0.08)\r\n : striped && localIndex % 2 === 1\r\n ? addAlpha(tokens.colors.background.surface, 0.5)\r\n : \"transparent\",\r\n transition: \"background-color 150ms ease\",\r\n }}\r\n >\r\n {visibleColumns.map((col) => (\r\n <td\r\n key={col.key}\r\n style={{\r\n padding: cellPadding,\r\n textAlign: col.align || \"left\",\r\n color: tokens.colors.text.primary,\r\n borderBottom: `1px solid ${addAlpha(tokens.colors.border.muted, 0.08)}`,\r\n fontFamily: col.mono\r\n ? tokens.typography.fontFamily.mono\r\n : \"inherit\",\r\n fontVariantNumeric: col.mono ? \"tabular-nums\" : \"normal\",\r\n whiteSpace: \"nowrap\",\r\n overflow: \"hidden\",\r\n textOverflow: \"ellipsis\",\r\n maxWidth: resolveWidth(col.width) || \"300px\",\r\n }}\r\n >\r\n {renderCellWithTooltip(col, row, rowIdx)}\r\n </td>\r\n ))}\r\n </tr>\r\n );\r\n\r\n if (hasRichRowTooltip) {\r\n const tooltipContent = rowTooltipContent(row, rowIdx);\r\n if (tooltipContent) {\r\n return (\r\n <Tooltip\r\n key={rowKey}\r\n content={tooltipContent}\r\n placement={tooltipPlacement}\r\n delay={tooltipDelay}\r\n // Attached to the row, never wrapped around it. A wrapper here\r\n // is a div between `tbody` and `tr`, which is not a table row —\r\n // the row stops taking part in the table's layout and its cells\r\n // reflow as inline text, taking the whole table apart.\r\n asChild\r\n >\r\n {tr}\r\n </Tooltip>\r\n );\r\n }\r\n }\r\n\r\n return tr;\r\n };\r\n\r\n let globalRowIndex = 0;\r\n\r\n return (\r\n <div\r\n style={{\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n overflow: \"auto\",\r\n WebkitOverflowScrolling: \"touch\",\r\n maxHeight: typeof maxHeight === \"number\" ? `${maxHeight}px` : maxHeight,\r\n borderRadius: tokens.borderRadius.md,\r\n border: tokens.borders.divider,\r\n ...style,\r\n }}\r\n >\r\n <table\r\n style={{\r\n width: \"100%\",\r\n borderCollapse: \"collapse\",\r\n fontSize: compact\r\n ? tokens.typography.fontSize.xs\r\n : tokens.typography.fontSize.sm,\r\n }}\r\n >\r\n {showHeader && (\r\n <thead>\r\n <tr>\r\n {visibleColumns.map((col) => (\r\n <th\r\n key={col.key}\r\n style={{\r\n padding: cellPadding,\r\n textAlign: col.align || \"left\",\r\n fontSize: tokens.typography.fontSize.xxs,\r\n fontWeight: tokens.typography.fontWeight.bold,\r\n color: tokens.colors.text.tertiary,\r\n textTransform: \"uppercase\",\r\n letterSpacing: \"0.06em\",\r\n backgroundColor: tokens.colors.background.surface,\r\n borderBottom: tokens.borders.divider,\r\n width: resolveWidth(col.width),\r\n position: \"sticky\",\r\n top: 0,\r\n zIndex: 1,\r\n whiteSpace: \"nowrap\",\r\n }}\r\n >\r\n {renderHeaderCell(col)}\r\n </th>\r\n ))}\r\n </tr>\r\n </thead>\r\n )}\r\n\r\n <tbody>\r\n {data.length === 0 && (\r\n <tr>\r\n <td\r\n colSpan={visibleColumnCount}\r\n style={{\r\n padding: tokens.spacing.xl,\r\n textAlign: \"center\",\r\n color: tokens.colors.text.tertiary,\r\n fontSize: tokens.typography.fontSize.sm,\r\n }}\r\n >\r\n {emptyMessage}\r\n </td>\r\n </tr>\r\n )}\r\n\r\n {groups.map((group) => {\r\n const isExpanded = !groupBy || isGroupExpanded(group.key);\r\n\r\n return (\r\n <React.Fragment key={group.key}>\r\n {groupBy && group.label && (\r\n <tr\r\n style={{\r\n backgroundColor: addAlpha(\r\n tokens.colors.accent.primary,\r\n 0.08,\r\n ),\r\n }}\r\n >\r\n <td\r\n colSpan={visibleColumnCount}\r\n style={{\r\n padding: 0,\r\n borderBottom: tokens.borders.divider,\r\n }}\r\n >\r\n {expandable ? (\r\n <button\r\n type=\"button\"\r\n aria-expanded={isExpanded}\r\n onClick={() => toggleGroup(group.key)}\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: tokens.spacing.xs,\r\n width: \"100%\",\r\n padding: cellPadding,\r\n margin: 0,\r\n border: \"none\",\r\n background: \"none\",\r\n cursor: \"pointer\",\r\n fontWeight: tokens.typography.fontWeight.semibold,\r\n color: tokens.colors.text.primary,\r\n fontSize: compact\r\n ? tokens.typography.fontSize.xs\r\n : tokens.typography.fontSize.sm,\r\n fontFamily: \"inherit\",\r\n textAlign: \"left\",\r\n }}\r\n >\r\n {chevron(isExpanded)}\r\n {group.label}\r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.xxs,\r\n color: tokens.colors.text.tertiary,\r\n fontWeight: tokens.typography.fontWeight.normal,\r\n }}\r\n >\r\n ({group.rows.length})\r\n </span>\r\n </button>\r\n ) : (\r\n <div\r\n style={{\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n gap: tokens.spacing.xs,\r\n padding: cellPadding,\r\n fontWeight: tokens.typography.fontWeight.semibold,\r\n color: tokens.colors.text.primary,\r\n fontSize: compact\r\n ? tokens.typography.fontSize.xs\r\n : tokens.typography.fontSize.sm,\r\n }}\r\n >\r\n {group.label}\r\n <span\r\n style={{\r\n fontSize: tokens.typography.fontSize.xxs,\r\n color: tokens.colors.text.tertiary,\r\n fontWeight: tokens.typography.fontWeight.normal,\r\n }}\r\n >\r\n ({group.rows.length})\r\n </span>\r\n </div>\r\n )}\r\n </td>\r\n </tr>\r\n )}\r\n\r\n {isExpanded &&\r\n group.rows.map((row, localIndex) => {\r\n const rowIdx = globalRowIndex++;\r\n return renderRowContent(row, rowIdx, localIndex);\r\n })}\r\n </React.Fragment>\r\n );\r\n })}\r\n </tbody>\r\n </table>\r\n </div>\r\n );\r\n}) as <T extends Record<string, unknown>>(\r\n props: DataTableProps<T>,\r\n) => React.ReactElement;\r\n\r\nexport default DataTable;\r\n"],"mappings":"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"}
|
|
@@ -11,6 +11,20 @@ export interface TooltipProps {
|
|
|
11
11
|
disabled?: boolean;
|
|
12
12
|
/** Children element to trigger tooltip */
|
|
13
13
|
children: React.ReactElement;
|
|
14
|
+
/**
|
|
15
|
+
* Attach to the child rather than wrapping it.
|
|
16
|
+
*
|
|
17
|
+
* The wrapper is a `div`, which most of the time is invisible and fine.
|
|
18
|
+
* Somewhere its parent dictates what its children may be it is neither: a
|
|
19
|
+
* `div` between `tbody` and `tr` is not a table row, so the row it holds
|
|
20
|
+
* stops taking part in the table's layout and its cells reflow as inline
|
|
21
|
+
* content — the table comes apart. Cloning the child to carry the
|
|
22
|
+
* handlers and the ref leaves the markup its parent requires intact.
|
|
23
|
+
*
|
|
24
|
+
* The child has to take a ref and spread the handlers, which a DOM
|
|
25
|
+
* element does and an arbitrary component may not.
|
|
26
|
+
*/
|
|
27
|
+
asChild?: boolean;
|
|
14
28
|
/** Custom className */
|
|
15
29
|
className?: string;
|
|
16
30
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useTheme } from "../../theme/ThemeProvider.js";
|
|
2
2
|
import { classNames } from "../../utils/index.js";
|
|
3
|
-
import { memo, useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
3
|
+
import React, { memo, useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
4
4
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
5
5
|
import ReactDOM from "react-dom";
|
|
6
6
|
//#region src/react/core/overlays/Tooltip.tsx
|
|
@@ -18,7 +18,7 @@ import ReactDOM from "react-dom";
|
|
|
18
18
|
* </Tooltip>
|
|
19
19
|
* ```
|
|
20
20
|
*/
|
|
21
|
-
var Tooltip = memo(function Tooltip({ content, placement = "top", delay = 200, disabled = false, children, className = "" }) {
|
|
21
|
+
var Tooltip = memo(function Tooltip({ content, placement = "top", delay = 200, disabled = false, children, className = "", asChild = false }) {
|
|
22
22
|
const { tokens } = useTheme();
|
|
23
23
|
const [visible, setVisible] = useState(false);
|
|
24
24
|
const [positioned, setPositioned] = useState(false);
|
|
@@ -120,7 +120,23 @@ var Tooltip = memo(function Tooltip({ content, placement = "top", delay = 200, d
|
|
|
120
120
|
if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);
|
|
121
121
|
};
|
|
122
122
|
}, []);
|
|
123
|
-
|
|
123
|
+
/**
|
|
124
|
+
* Composed rather than replaced: a child given these already is a child
|
|
125
|
+
* that does something with them, and taking that away to add a tooltip
|
|
126
|
+
* would be a surprising thing for a tooltip to do.
|
|
127
|
+
*/
|
|
128
|
+
const compose = (own, theirs) => (e) => {
|
|
129
|
+
theirs?.(e);
|
|
130
|
+
own();
|
|
131
|
+
};
|
|
132
|
+
const childProps = children.props;
|
|
133
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [asChild ? React.cloneElement(children, {
|
|
134
|
+
ref: triggerRef,
|
|
135
|
+
onMouseEnter: compose(showTooltip, childProps.onMouseEnter),
|
|
136
|
+
onMouseLeave: compose(hideTooltip, childProps.onMouseLeave),
|
|
137
|
+
onFocus: compose(showTooltip, childProps.onFocus),
|
|
138
|
+
onBlur: compose(hideTooltip, childProps.onBlur)
|
|
139
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
124
140
|
ref: triggerRef,
|
|
125
141
|
className: classNames("zendir-tooltip-trigger", className),
|
|
126
142
|
style: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Tooltip.js","names":[],"sources":["../../../../src/react/core/overlays/Tooltip.tsx"],"sourcesContent":["/**\r\n * @zendir/ui - Tooltip Component\r\n *\r\n * Tooltip following Astro UX Design System.\r\n * Uses a portal to render in document.body, ensuring correct positioning\r\n * even when the trigger is inside containers with CSS transforms or overflow.\r\n *\r\n * @example\r\n * ```tsx\r\n * <Tooltip content=\"Click to enable telemetry\">\r\n * <Button>Enable</Button>\r\n * </Tooltip>\r\n * ```\r\n */\r\n\r\nimport React, {\r\n memo,\r\n useState,\r\n useRef,\r\n useEffect,\r\n useLayoutEffect,\r\n useCallback,\r\n} from \"react\";\r\nimport ReactDOM from \"react-dom\";\r\nimport { useTheme } from \"../../theme\";\r\nimport { classNames } from \"../../utils\";\r\n\r\nexport type TooltipPlacement = \"top\" | \"bottom\" | \"left\" | \"right\";\r\n\r\nexport interface TooltipProps {\r\n /** Tooltip content */\r\n content: React.ReactNode;\r\n /** Placement */\r\n placement?: TooltipPlacement;\r\n /** Delay before showing (ms) */\r\n delay?: number;\r\n /** Disabled state */\r\n disabled?: boolean;\r\n /** Children element to trigger tooltip */\r\n children: React.ReactElement;\r\n /** Custom className */\r\n className?: string;\r\n}\r\n\r\nexport const Tooltip = memo(function Tooltip({\r\n content,\r\n placement = \"top\",\r\n delay = 200,\r\n disabled = false,\r\n children,\r\n className = \"\",\r\n}: TooltipProps): React.ReactElement {\r\n const { tokens } = useTheme();\r\n const [visible, setVisible] = useState(false);\r\n const [positioned, setPositioned] = useState(false);\r\n const [position, setPosition] = useState({ top: 0, left: 0 });\r\n const triggerRef = useRef<HTMLDivElement>(null);\r\n const tooltipRef = useRef<HTMLDivElement>(null);\r\n const timeoutRef = useRef<number>();\r\n const hideTimeoutRef = useRef<number>();\r\n\r\n const showTooltip = useCallback(() => {\r\n if (disabled) return;\r\n // Cancel any pending hide so moving between trigger ↔ tooltip keeps it open\r\n if (hideTimeoutRef.current) {\r\n clearTimeout(hideTimeoutRef.current);\r\n hideTimeoutRef.current = undefined;\r\n }\r\n timeoutRef.current = window.setTimeout(() => {\r\n setVisible(true);\r\n setPositioned(false);\r\n }, delay);\r\n }, [disabled, delay]);\r\n\r\n const hideTooltip = useCallback(() => {\r\n if (timeoutRef.current) {\r\n clearTimeout(timeoutRef.current);\r\n }\r\n // Small delay so user can move pointer from trigger to tooltip (WCAG 1.4.13)\r\n hideTimeoutRef.current = window.setTimeout(() => {\r\n setVisible(false);\r\n setPositioned(false);\r\n }, 100);\r\n }, []);\r\n\r\n const hideImmediately = useCallback(() => {\r\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\r\n if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);\r\n setVisible(false);\r\n setPositioned(false);\r\n }, []);\r\n\r\n const calculatePosition = useCallback(() => {\r\n if (!triggerRef.current || !tooltipRef.current) return;\r\n\r\n const triggerRect = triggerRef.current.getBoundingClientRect();\r\n const tooltipRect = tooltipRef.current.getBoundingClientRect();\r\n const gap = 8;\r\n const viewportWidth = window.innerWidth;\r\n const viewportHeight = window.innerHeight;\r\n\r\n let top = 0;\r\n let left = 0;\r\n\r\n switch (placement) {\r\n case \"top\":\r\n top = triggerRect.top - tooltipRect.height - gap;\r\n left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2;\r\n break;\r\n case \"bottom\":\r\n top = triggerRect.bottom + gap;\r\n left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2;\r\n break;\r\n case \"left\":\r\n top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2;\r\n left = triggerRect.left - tooltipRect.width - gap;\r\n break;\r\n case \"right\":\r\n top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2;\r\n left = triggerRect.right + gap;\r\n break;\r\n }\r\n\r\n // Keep tooltip within viewport bounds\r\n if (left < gap) left = gap;\r\n if (left + tooltipRect.width > viewportWidth - gap) {\r\n left = viewportWidth - tooltipRect.width - gap;\r\n }\r\n if (top < gap) top = gap;\r\n if (top + tooltipRect.height > viewportHeight - gap) {\r\n top = viewportHeight - tooltipRect.height - gap;\r\n }\r\n\r\n setPosition({ top, left });\r\n setPositioned(true);\r\n }, [placement]);\r\n\r\n // Use useLayoutEffect for initial positioning - runs synchronously\r\n // before browser paint, preventing flash at wrong position\r\n useLayoutEffect(() => {\r\n if (visible && triggerRef.current && tooltipRef.current) {\r\n calculatePosition();\r\n }\r\n }, [visible, calculatePosition]);\r\n\r\n // Use useEffect for scroll/resize listeners\r\n useEffect(() => {\r\n if (!visible) return;\r\n\r\n const handleScrollOrResize = () => {\r\n calculatePosition();\r\n };\r\n\r\n window.addEventListener(\"scroll\", handleScrollOrResize, true);\r\n window.addEventListener(\"resize\", handleScrollOrResize);\r\n\r\n return () => {\r\n window.removeEventListener(\"scroll\", handleScrollOrResize, true);\r\n window.removeEventListener(\"resize\", handleScrollOrResize);\r\n };\r\n }, [visible, calculatePosition]);\r\n\r\n // Escape key dismisses tooltip (WCAG 1.4.13 Content on Hover or Focus)\r\n useEffect(() => {\r\n if (!visible) return;\r\n const handleEscape = (e: KeyboardEvent) => {\r\n if (e.key === \"Escape\") {\r\n hideImmediately();\r\n }\r\n };\r\n document.addEventListener(\"keydown\", handleEscape);\r\n return () => document.removeEventListener(\"keydown\", handleEscape);\r\n }, [visible, hideImmediately]);\r\n\r\n useEffect(() => {\r\n return () => {\r\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\r\n if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);\r\n };\r\n }, []);\r\n\r\n return (\r\n <>\r\n <div\r\n ref={triggerRef}\r\n className={classNames(\"zendir-tooltip-trigger\", className)}\r\n style={{\r\n display: \"inline-flex\",\r\n pointerEvents: \"auto\",\r\n maxWidth: \"100%\",\r\n width: \"inherit\",\r\n }}\r\n onMouseEnter={showTooltip}\r\n onMouseLeave={hideTooltip}\r\n onFocus={showTooltip}\r\n onBlur={hideTooltip}\r\n >\r\n {children}\r\n </div>\r\n\r\n {visible &&\r\n ReactDOM.createPortal(\r\n <div\r\n ref={tooltipRef}\r\n role=\"tooltip\"\r\n onMouseEnter={showTooltip}\r\n onMouseLeave={hideTooltip}\r\n style={{\r\n position: \"fixed\",\r\n top: positioned ? position.top : 0,\r\n left: positioned ? position.left : 0,\r\n width: \"max-content\",\r\n padding: `${tokens.spacing.sm} ${tokens.spacing.smd}`,\r\n backgroundColor: tokens.colors.background.elevated,\r\n color: tokens.colors.text.primary,\r\n fontSize: \"0.75rem\",\r\n fontWeight: 500,\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n borderRadius: tokens.borderRadius.md,\r\n border: `1px solid ${tokens.colors.accent.primary}20`,\r\n boxShadow: positioned\r\n ? `${tokens.shadows.lg}, 0 0 20px ${tokens.colors.accent.primary}10`\r\n : \"none\",\r\n zIndex: 2147483100,\r\n maxWidth: \"min(280px, calc(100vw - 24px))\",\r\n whiteSpace: \"normal\",\r\n pointerEvents: \"auto\",\r\n visibility: positioned ? \"visible\" : \"hidden\",\r\n opacity: positioned ? 1 : 0,\r\n transition: positioned ? undefined : \"none\",\r\n animation: positioned\r\n ? `zendir-tooltip ${tokens.animation.duration.fast}ms ${tokens.animation.easing.default} both`\r\n : \"none\",\r\n backdropFilter: \"blur(8px)\",\r\n }}\r\n >\r\n {content}\r\n <style>\r\n {`@keyframes zendir-tooltip { from { opacity: 0; transform: scale(0.95) translateY(${placement === \"bottom\" ? \"-4px\" : placement === \"top\" ? \"4px\" : \"0\"}); } to { opacity: 1; transform: scale(1) translateY(0); } }\r\n @media (prefers-reduced-motion: reduce) { @keyframes zendir-tooltip { from, to { opacity: 1; transform: none; } } }`}\r\n </style>\r\n </div>,\r\n document.body,\r\n )}\r\n </>\r\n );\r\n});\r\n\r\nexport default Tooltip;\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Tooltip.js","names":[],"sources":["../../../../src/react/core/overlays/Tooltip.tsx"],"sourcesContent":["/**\r\n * @zendir/ui - Tooltip Component\r\n *\r\n * Tooltip following Astro UX Design System.\r\n * Uses a portal to render in document.body, ensuring correct positioning\r\n * even when the trigger is inside containers with CSS transforms or overflow.\r\n *\r\n * @example\r\n * ```tsx\r\n * <Tooltip content=\"Click to enable telemetry\">\r\n * <Button>Enable</Button>\r\n * </Tooltip>\r\n * ```\r\n */\r\n\r\nimport React, {\r\n memo,\r\n useState,\r\n useRef,\r\n useEffect,\r\n useLayoutEffect,\r\n useCallback,\r\n} from \"react\";\r\nimport ReactDOM from \"react-dom\";\r\nimport { useTheme } from \"../../theme\";\r\nimport { classNames } from \"../../utils\";\r\n\r\nexport type TooltipPlacement = \"top\" | \"bottom\" | \"left\" | \"right\";\r\n\r\nexport interface TooltipProps {\r\n /** Tooltip content */\r\n content: React.ReactNode;\r\n /** Placement */\r\n placement?: TooltipPlacement;\r\n /** Delay before showing (ms) */\r\n delay?: number;\r\n /** Disabled state */\r\n disabled?: boolean;\r\n /** Children element to trigger tooltip */\r\n children: React.ReactElement;\r\n /**\r\n * Attach to the child rather than wrapping it.\r\n *\r\n * The wrapper is a `div`, which most of the time is invisible and fine.\r\n * Somewhere its parent dictates what its children may be it is neither: a\r\n * `div` between `tbody` and `tr` is not a table row, so the row it holds\r\n * stops taking part in the table's layout and its cells reflow as inline\r\n * content — the table comes apart. Cloning the child to carry the\r\n * handlers and the ref leaves the markup its parent requires intact.\r\n *\r\n * The child has to take a ref and spread the handlers, which a DOM\r\n * element does and an arbitrary component may not.\r\n */\r\n asChild?: boolean;\r\n /** Custom className */\r\n className?: string;\r\n}\r\n\r\nexport const Tooltip = memo(function Tooltip({\r\n content,\r\n placement = \"top\",\r\n delay = 200,\r\n disabled = false,\r\n children,\r\n className = \"\",\r\n asChild = false,\r\n}: TooltipProps): React.ReactElement {\r\n const { tokens } = useTheme();\r\n const [visible, setVisible] = useState(false);\r\n const [positioned, setPositioned] = useState(false);\r\n const [position, setPosition] = useState({ top: 0, left: 0 });\r\n // Not necessarily a div any more: with `asChild` the trigger is whatever\r\n // element the caller handed over, a table row included.\r\n const triggerRef = useRef<HTMLElement>(null);\r\n const tooltipRef = useRef<HTMLDivElement>(null);\r\n const timeoutRef = useRef<number>();\r\n const hideTimeoutRef = useRef<number>();\r\n\r\n const showTooltip = useCallback(() => {\r\n if (disabled) return;\r\n // Cancel any pending hide so moving between trigger ↔ tooltip keeps it open\r\n if (hideTimeoutRef.current) {\r\n clearTimeout(hideTimeoutRef.current);\r\n hideTimeoutRef.current = undefined;\r\n }\r\n timeoutRef.current = window.setTimeout(() => {\r\n setVisible(true);\r\n setPositioned(false);\r\n }, delay);\r\n }, [disabled, delay]);\r\n\r\n const hideTooltip = useCallback(() => {\r\n if (timeoutRef.current) {\r\n clearTimeout(timeoutRef.current);\r\n }\r\n // Small delay so user can move pointer from trigger to tooltip (WCAG 1.4.13)\r\n hideTimeoutRef.current = window.setTimeout(() => {\r\n setVisible(false);\r\n setPositioned(false);\r\n }, 100);\r\n }, []);\r\n\r\n const hideImmediately = useCallback(() => {\r\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\r\n if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);\r\n setVisible(false);\r\n setPositioned(false);\r\n }, []);\r\n\r\n const calculatePosition = useCallback(() => {\r\n if (!triggerRef.current || !tooltipRef.current) return;\r\n\r\n const triggerRect = triggerRef.current.getBoundingClientRect();\r\n const tooltipRect = tooltipRef.current.getBoundingClientRect();\r\n const gap = 8;\r\n const viewportWidth = window.innerWidth;\r\n const viewportHeight = window.innerHeight;\r\n\r\n let top = 0;\r\n let left = 0;\r\n\r\n switch (placement) {\r\n case \"top\":\r\n top = triggerRect.top - tooltipRect.height - gap;\r\n left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2;\r\n break;\r\n case \"bottom\":\r\n top = triggerRect.bottom + gap;\r\n left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2;\r\n break;\r\n case \"left\":\r\n top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2;\r\n left = triggerRect.left - tooltipRect.width - gap;\r\n break;\r\n case \"right\":\r\n top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2;\r\n left = triggerRect.right + gap;\r\n break;\r\n }\r\n\r\n // Keep tooltip within viewport bounds\r\n if (left < gap) left = gap;\r\n if (left + tooltipRect.width > viewportWidth - gap) {\r\n left = viewportWidth - tooltipRect.width - gap;\r\n }\r\n if (top < gap) top = gap;\r\n if (top + tooltipRect.height > viewportHeight - gap) {\r\n top = viewportHeight - tooltipRect.height - gap;\r\n }\r\n\r\n setPosition({ top, left });\r\n setPositioned(true);\r\n }, [placement]);\r\n\r\n // Use useLayoutEffect for initial positioning - runs synchronously\r\n // before browser paint, preventing flash at wrong position\r\n useLayoutEffect(() => {\r\n if (visible && triggerRef.current && tooltipRef.current) {\r\n calculatePosition();\r\n }\r\n }, [visible, calculatePosition]);\r\n\r\n // Use useEffect for scroll/resize listeners\r\n useEffect(() => {\r\n if (!visible) return;\r\n\r\n const handleScrollOrResize = () => {\r\n calculatePosition();\r\n };\r\n\r\n window.addEventListener(\"scroll\", handleScrollOrResize, true);\r\n window.addEventListener(\"resize\", handleScrollOrResize);\r\n\r\n return () => {\r\n window.removeEventListener(\"scroll\", handleScrollOrResize, true);\r\n window.removeEventListener(\"resize\", handleScrollOrResize);\r\n };\r\n }, [visible, calculatePosition]);\r\n\r\n // Escape key dismisses tooltip (WCAG 1.4.13 Content on Hover or Focus)\r\n useEffect(() => {\r\n if (!visible) return;\r\n const handleEscape = (e: KeyboardEvent) => {\r\n if (e.key === \"Escape\") {\r\n hideImmediately();\r\n }\r\n };\r\n document.addEventListener(\"keydown\", handleEscape);\r\n return () => document.removeEventListener(\"keydown\", handleEscape);\r\n }, [visible, hideImmediately]);\r\n\r\n useEffect(() => {\r\n return () => {\r\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\r\n if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current);\r\n };\r\n }, []);\r\n\r\n /**\r\n * Composed rather than replaced: a child given these already is a child\r\n * that does something with them, and taking that away to add a tooltip\r\n * would be a surprising thing for a tooltip to do.\r\n */\r\n const compose =\r\n (own: () => void, theirs?: (e: React.SyntheticEvent) => void) =>\r\n (e: React.SyntheticEvent) => {\r\n theirs?.(e);\r\n own();\r\n };\r\n\r\n const childProps = children.props as Record<string, never> & {\r\n onMouseEnter?: (e: React.SyntheticEvent) => void;\r\n onMouseLeave?: (e: React.SyntheticEvent) => void;\r\n onFocus?: (e: React.SyntheticEvent) => void;\r\n onBlur?: (e: React.SyntheticEvent) => void;\r\n };\r\n\r\n const trigger = asChild ? (\r\n React.cloneElement(children, {\r\n ref: triggerRef,\r\n onMouseEnter: compose(showTooltip, childProps.onMouseEnter),\r\n onMouseLeave: compose(hideTooltip, childProps.onMouseLeave),\r\n onFocus: compose(showTooltip, childProps.onFocus),\r\n onBlur: compose(hideTooltip, childProps.onBlur),\r\n } as React.Attributes)\r\n ) : (\r\n <div\r\n ref={triggerRef as React.RefObject<HTMLDivElement>}\r\n className={classNames(\"zendir-tooltip-trigger\", className)}\r\n style={{\r\n display: \"inline-flex\",\r\n pointerEvents: \"auto\",\r\n maxWidth: \"100%\",\r\n width: \"inherit\",\r\n }}\r\n onMouseEnter={showTooltip}\r\n onMouseLeave={hideTooltip}\r\n onFocus={showTooltip}\r\n onBlur={hideTooltip}\r\n >\r\n {children}\r\n </div>\r\n );\r\n\r\n return (\r\n <>\r\n {trigger}\r\n\r\n {visible &&\r\n ReactDOM.createPortal(\r\n <div\r\n ref={tooltipRef}\r\n role=\"tooltip\"\r\n onMouseEnter={showTooltip}\r\n onMouseLeave={hideTooltip}\r\n style={{\r\n position: \"fixed\",\r\n top: positioned ? position.top : 0,\r\n left: positioned ? position.left : 0,\r\n width: \"max-content\",\r\n padding: `${tokens.spacing.sm} ${tokens.spacing.smd}`,\r\n backgroundColor: tokens.colors.background.elevated,\r\n color: tokens.colors.text.primary,\r\n fontSize: \"0.75rem\",\r\n fontWeight: 500,\r\n fontFamily: tokens.typography.fontFamily.primary,\r\n borderRadius: tokens.borderRadius.md,\r\n border: `1px solid ${tokens.colors.accent.primary}20`,\r\n boxShadow: positioned\r\n ? `${tokens.shadows.lg}, 0 0 20px ${tokens.colors.accent.primary}10`\r\n : \"none\",\r\n zIndex: 2147483100,\r\n maxWidth: \"min(280px, calc(100vw - 24px))\",\r\n whiteSpace: \"normal\",\r\n pointerEvents: \"auto\",\r\n visibility: positioned ? \"visible\" : \"hidden\",\r\n opacity: positioned ? 1 : 0,\r\n transition: positioned ? undefined : \"none\",\r\n animation: positioned\r\n ? `zendir-tooltip ${tokens.animation.duration.fast}ms ${tokens.animation.easing.default} both`\r\n : \"none\",\r\n backdropFilter: \"blur(8px)\",\r\n }}\r\n >\r\n {content}\r\n <style>\r\n {`@keyframes zendir-tooltip { from { opacity: 0; transform: scale(0.95) translateY(${placement === \"bottom\" ? \"-4px\" : placement === \"top\" ? \"4px\" : \"0\"}); } to { opacity: 1; transform: scale(1) translateY(0); } }\r\n @media (prefers-reduced-motion: reduce) { @keyframes zendir-tooltip { from, to { opacity: 1; transform: none; } } }`}\r\n </style>\r\n </div>,\r\n document.body,\r\n )}\r\n </>\r\n );\r\n});\r\n\r\nexport default Tooltip;\r\n"],"mappings":"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"}
|