@zendir/ui 0.1.13 → 0.1.15

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.
@@ -0,0 +1,1412 @@
1
+ import { jsxs, jsx } from "react/jsx-runtime";
2
+ import { memo, forwardRef, useRef, useMemo, useCallback, useImperativeHandle, useState, useEffect } from "react";
3
+ import ReactEChartsCore from "echarts-for-react/lib/core";
4
+ import * as echarts from "echarts/core";
5
+ import { LineChart, BarChart, PieChart, ScatterChart, RadarChart, GaugeChart, HeatmapChart, CandlestickChart, BoxplotChart, TreemapChart, SunburstChart, FunnelChart, SankeyChart, CustomChart, GraphChart, ParallelChart } from "echarts/charts";
6
+ import { GridComponent, TooltipComponent, TitleComponent, LegendComponent, DataZoomComponent, ToolboxComponent, MarkLineComponent, MarkAreaComponent, MarkPointComponent, VisualMapComponent, BrushComponent, PolarComponent, RadarComponent, GeoComponent, ParallelComponent } from "echarts/components";
7
+ import { CanvasRenderer, SVGRenderer } from "echarts/renderers";
8
+ import { AstroIcon } from "../../core/AstroIcon.js";
9
+ import { HeaderIconWithStatus } from "../../core/HeaderIconWithStatus.js";
10
+ import { useCardBorderStyle } from "../../context/DisplaySettingsContext.js";
11
+ import { createAstroEChartsTheme, getSeriesColor, createAreaGradient } from "./theme.js";
12
+ import { useTheme } from "../../theme/ThemeProvider.js";
13
+ const STATUS_SHAPES = {
14
+ off: (size, color) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 12 12", width: size, height: size, children: /* @__PURE__ */ jsx("circle", { cx: "6", cy: "6", r: "5", fill: color }) }),
15
+ standby: (size, color) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 12 12", width: size, height: size, children: /* @__PURE__ */ jsx("circle", { cx: "6", cy: "6", r: "5", fill: color }) }),
16
+ normal: (size, color) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 12 12", width: size, height: size, children: /* @__PURE__ */ jsx("circle", { cx: "6", cy: "6", r: "5", fill: color }) }),
17
+ caution: (size, color) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 12 12", width: size, height: size, children: /* @__PURE__ */ jsx("rect", { x: "1", y: "1", width: "10", height: "10", fill: color }) }),
18
+ serious: (size, color) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 12 12", width: size, height: size, children: /* @__PURE__ */ jsx("polygon", { points: "6,1 11,6 6,11 1,6", fill: color }) }),
19
+ critical: (size, color) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 12 12", width: size, height: size, children: /* @__PURE__ */ jsx("polygon", { points: "6,11 1,2 11,2", fill: color }) })
20
+ };
21
+ const STATUS_LABELS = {
22
+ off: "Off",
23
+ standby: "Standby",
24
+ normal: "Normal",
25
+ caution: "Caution",
26
+ serious: "Serious",
27
+ critical: "Critical"
28
+ };
29
+ function slug(str) {
30
+ return str.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
31
+ }
32
+ function derivedExportBaseName(title, seriesNames, chartType) {
33
+ const parts = ["zendir"];
34
+ if (title) parts.push(slug(title));
35
+ else parts.push("chart");
36
+ const seriesSlug = seriesNames.slice(0, 4).map(slug).filter(Boolean).join("-");
37
+ if (seriesSlug) parts.push(seriesSlug);
38
+ parts.push(slug(chartType));
39
+ return parts.join("-") || "zendir-chart";
40
+ }
41
+ echarts.use([
42
+ LineChart,
43
+ BarChart,
44
+ PieChart,
45
+ ScatterChart,
46
+ RadarChart,
47
+ GaugeChart,
48
+ HeatmapChart,
49
+ CandlestickChart,
50
+ BoxplotChart,
51
+ TreemapChart,
52
+ SunburstChart,
53
+ FunnelChart,
54
+ SankeyChart,
55
+ CustomChart,
56
+ GraphChart,
57
+ ParallelChart,
58
+ GridComponent,
59
+ TooltipComponent,
60
+ TitleComponent,
61
+ LegendComponent,
62
+ DataZoomComponent,
63
+ ToolboxComponent,
64
+ MarkLineComponent,
65
+ MarkAreaComponent,
66
+ MarkPointComponent,
67
+ VisualMapComponent,
68
+ BrushComponent,
69
+ PolarComponent,
70
+ RadarComponent,
71
+ GeoComponent,
72
+ ParallelComponent,
73
+ CanvasRenderer,
74
+ SVGRenderer
75
+ ]);
76
+ function transformSeriesToECharts(series, chartType, theme) {
77
+ return series.map((s, index) => {
78
+ var _a, _b, _c, _d, _e;
79
+ const seriesType = s.type || chartType;
80
+ const color = s.color || getSeriesColor(index);
81
+ const baseSeries = {
82
+ id: s.id,
83
+ name: s.name,
84
+ type: mapChartType(seriesType),
85
+ data: transformData(s.data, seriesType),
86
+ yAxisIndex: s.yAxisIndex || 0,
87
+ showSymbol: seriesType === "scatter" || ((_a = s.symbol) == null ? void 0 : _a.type) !== "none",
88
+ smooth: s.smooth ?? (seriesType === "line" || seriesType === "area")
89
+ };
90
+ if (seriesType === "line" || seriesType === "area") {
91
+ baseSeries.lineStyle = {
92
+ width: ((_b = s.lineStyle) == null ? void 0 : _b.width) ?? 2,
93
+ type: ((_c = s.lineStyle) == null ? void 0 : _c.type) ?? "solid",
94
+ opacity: ((_d = s.lineStyle) == null ? void 0 : _d.opacity) ?? 1
95
+ };
96
+ baseSeries.itemStyle = { color };
97
+ if (seriesType === "area") {
98
+ baseSeries.areaStyle = s.areaStyle || {
99
+ opacity: 0.3,
100
+ color: createAreaGradient(color)
101
+ };
102
+ }
103
+ if (s.symbol) {
104
+ baseSeries.symbol = s.symbol.type || "circle";
105
+ baseSeries.symbolSize = s.symbol.size || 6;
106
+ }
107
+ }
108
+ if (seriesType === "bar") {
109
+ baseSeries.itemStyle = {
110
+ color,
111
+ borderRadius: [4, 4, 0, 0]
112
+ };
113
+ if (s.stack) {
114
+ baseSeries.stack = s.stack;
115
+ }
116
+ }
117
+ if (seriesType === "pie" || seriesType === "donut") {
118
+ baseSeries.radius = seriesType === "donut" ? ["40%", "70%"] : "70%";
119
+ baseSeries.label = {
120
+ show: true,
121
+ formatter: "{b}: {d}%",
122
+ color: theme.textStyle.color,
123
+ textBorderWidth: 0,
124
+ textBorderColor: "transparent"
125
+ };
126
+ baseSeries.emphasis = {
127
+ itemStyle: {
128
+ shadowBlur: 10,
129
+ shadowOffsetX: 0,
130
+ shadowColor: "rgba(0, 0, 0, 0.5)"
131
+ }
132
+ };
133
+ }
134
+ if (seriesType === "scatter") {
135
+ baseSeries.symbolSize = ((_e = s.symbol) == null ? void 0 : _e.size) || 10;
136
+ baseSeries.itemStyle = { color };
137
+ }
138
+ if (seriesType === "gauge") {
139
+ baseSeries.detail = {
140
+ formatter: "{value}%",
141
+ fontSize: 20,
142
+ color: theme.textStyle.color
143
+ };
144
+ baseSeries.axisLine = {
145
+ lineStyle: {
146
+ width: 10,
147
+ color: [
148
+ [0.3, theme.color[1]],
149
+ // Green
150
+ [0.7, theme.color[3]],
151
+ // Yellow
152
+ [1, theme.color[2]]
153
+ // Red
154
+ ]
155
+ }
156
+ };
157
+ }
158
+ return baseSeries;
159
+ });
160
+ }
161
+ function mapChartType(type) {
162
+ switch (type) {
163
+ case "area":
164
+ return "line";
165
+ case "donut":
166
+ return "pie";
167
+ default:
168
+ return type;
169
+ }
170
+ }
171
+ function transformData(data, type) {
172
+ if (!data || data.length === 0) return [];
173
+ if (typeof data[0] === "number") {
174
+ return data;
175
+ }
176
+ if (Array.isArray(data[0])) {
177
+ return data;
178
+ }
179
+ const firstItem = data[0];
180
+ if ("time" in firstItem) {
181
+ return data.map((d) => [d.time, d.value]);
182
+ }
183
+ if (type === "pie" || type === "donut") {
184
+ return data.map((d) => ({
185
+ name: String(d.x),
186
+ value: d.y,
187
+ // Apply color via itemStyle if specified
188
+ ...d.color ? { itemStyle: { color: d.color } } : {},
189
+ // Preserve any additional metadata
190
+ ...d.meta || {}
191
+ }));
192
+ }
193
+ if (type === "scatter") {
194
+ return data.map((d) => ({
195
+ value: [d.x, d.y],
196
+ ...d.color ? { itemStyle: { color: d.color } } : {},
197
+ ...d.label ? { name: d.label } : {}
198
+ }));
199
+ }
200
+ return data.map((d) => [d.x, d.y]);
201
+ }
202
+ function buildEChartsOptions(props, theme, hideTitle = false) {
203
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
204
+ const {
205
+ type,
206
+ series,
207
+ title,
208
+ subtitle,
209
+ xAxis,
210
+ yAxis,
211
+ tooltip,
212
+ legend,
213
+ zoom,
214
+ annotations,
215
+ loading,
216
+ compactAxes = true
217
+ // Default: hide axis names for more chart space
218
+ } = props;
219
+ const showEChartsTitle = title && !hideTitle;
220
+ const isPolar = type === "radar";
221
+ const isPie = type === "pie" || type === "donut";
222
+ const isGauge = type === "gauge";
223
+ const isSankey = type === "sankey";
224
+ const isTreemap = type === "treemap" || type === "sunburst";
225
+ const isGraph = ((_a = props.echartsOptions) == null ? void 0 : _a.series) && Array.isArray(props.echartsOptions.series) && props.echartsOptions.series.some((s) => (s == null ? void 0 : s.type) === "graph");
226
+ const is3D = !!(((_b = props.echartsOptions) == null ? void 0 : _b.grid3D) || ((_c = props.echartsOptions) == null ? void 0 : _c.xAxis3D) || ((_d = props.echartsOptions) == null ? void 0 : _d.globe) || ((_e = props.echartsOptions) == null ? void 0 : _e.series) && Array.isArray(props.echartsOptions.series) && props.echartsOptions.series.some((s) => {
227
+ const seriesType = (s == null ? void 0 : s.type) || "";
228
+ return seriesType.includes("3D") || seriesType.includes("3d");
229
+ }));
230
+ const needsGrid = !isPolar && !isPie && !isGauge && !isSankey && !isTreemap && !isGraph && !is3D && !props.hideAxes;
231
+ const hasTitle = hideTitle ? true : !!title;
232
+ const hasSubtitle = hideTitle ? false : !!subtitle;
233
+ const legendPosition = (legend == null ? void 0 : legend.position) ?? "bottom";
234
+ const legendAtTop = legendPosition === "top";
235
+ const legendAtBottom = legendPosition === "bottom";
236
+ const showLegend = (legend == null ? void 0 : legend.show) !== false;
237
+ const options = {
238
+ // Title - positioned to avoid legend overlap (hidden when using custom header)
239
+ title: showEChartsTitle ? {
240
+ text: title,
241
+ subtext: subtitle,
242
+ left: "center",
243
+ top: legendAtTop && showLegend ? 35 : 10,
244
+ textStyle: { ...theme.title.textStyle, fontWeight: theme.title.textStyle.fontWeight ?? "normal" },
245
+ subtextStyle: theme.title.subtextStyle
246
+ } : void 0,
247
+ // Tooltip with improved contrast
248
+ // When compactAxes is true, axis names are shown in the axisPointer label on hover
249
+ tooltip: (tooltip == null ? void 0 : tooltip.enabled) !== false ? {
250
+ trigger: (tooltip == null ? void 0 : tooltip.trigger) || (isPie ? "item" : "axis"),
251
+ axisPointer: {
252
+ type: (tooltip == null ? void 0 : tooltip.crosshair) === true || (tooltip == null ? void 0 : tooltip.crosshair) === "both" ? "cross" : "line",
253
+ lineStyle: theme.axisPointer.lineStyle,
254
+ crossStyle: theme.axisPointer.crossStyle,
255
+ // Show axis value labels on hover (includes axis name context)
256
+ label: compactAxes ? {
257
+ show: true,
258
+ backgroundColor: theme.tooltip.backgroundColor,
259
+ borderColor: theme.tooltip.borderColor,
260
+ color: theme.tooltip.textStyle.color,
261
+ fontSize: 11,
262
+ padding: [4, 8],
263
+ // Add axis name prefix to the label when in compact mode
264
+ formatter: (params) => {
265
+ var _a2;
266
+ const axisName = params.axisDimension === "x" ? xAxis == null ? void 0 : xAxis.name : Array.isArray(yAxis) ? (_a2 = yAxis[params.axisIndex]) == null ? void 0 : _a2.name : yAxis == null ? void 0 : yAxis.name;
267
+ const value = typeof params.value === "number" ? params.value.toLocaleString() : params.value;
268
+ return axisName ? `${axisName}: ${value}` : String(value);
269
+ }
270
+ } : void 0
271
+ },
272
+ backgroundColor: theme.tooltip.backgroundColor,
273
+ borderColor: theme.tooltip.borderColor,
274
+ borderWidth: theme.tooltip.borderWidth,
275
+ textStyle: {
276
+ ...theme.tooltip.textStyle,
277
+ fontSize: 14
278
+ // AstroUXDS minimum 14pt
279
+ },
280
+ extraCssText: theme.tooltip.extraCssText,
281
+ confine: (tooltip == null ? void 0 : tooltip.confine) ?? true,
282
+ formatter: tooltip == null ? void 0 : tooltip.formatter
283
+ } : void 0,
284
+ // Legend - ultra-compact styling for maximum chart space
285
+ legend: showLegend ? {
286
+ show: true,
287
+ type: "scroll",
288
+ orient: (legend == null ? void 0 : legend.orient) || "horizontal",
289
+ left: (legend == null ? void 0 : legend.position) === "left" ? "left" : (legend == null ? void 0 : legend.position) === "right" ? "right" : "center",
290
+ top: legendAtTop ? 4 : void 0,
291
+ bottom: legendAtBottom ? 2 : void 0,
292
+ padding: [2, 6],
293
+ itemGap: 8,
294
+ itemWidth: 12,
295
+ itemHeight: 6,
296
+ textStyle: {
297
+ ...theme.legend.textStyle,
298
+ fontSize: 10
299
+ // Ultra-compact for maximum chart space
300
+ },
301
+ inactiveColor: theme.legend.inactiveColor,
302
+ selectedMode: (legend == null ? void 0 : legend.interactive) !== false
303
+ } : { show: false },
304
+ // Grid - minimal spacing for maximum chart plotting area
305
+ // When compactAxes is true, axis names are hidden so we need minimal padding
306
+ // containLabel: true ensures axis tick labels don't get cut off
307
+ // Extra space only needed for slider zoom (inside zoom is invisible)
308
+ grid: needsGrid ? {
309
+ left: compactAxes ? 8 : 40,
310
+ right: Array.isArray(yAxis) && yAxis.length > 1 ? compactAxes ? 8 : 40 : 8,
311
+ top: hasTitle ? hasSubtitle ? 50 : 36 : legendAtTop && showLegend ? 26 : 4,
312
+ bottom: (legendAtBottom && showLegend ? 26 : 16) + ((zoom == null ? void 0 : zoom.type) === "slider" || (zoom == null ? void 0 : zoom.type) === "both" ? 26 : 0),
313
+ containLabel: true
314
+ } : void 0,
315
+ // X Axis - compact font sizes for more chart space
316
+ // In compact mode, hide axis name (shown in tooltip instead)
317
+ xAxis: needsGrid ? {
318
+ type: (xAxis == null ? void 0 : xAxis.type) || "time",
319
+ name: compactAxes ? void 0 : xAxis == null ? void 0 : xAxis.name,
320
+ nameLocation: (xAxis == null ? void 0 : xAxis.nameLocation) || "middle",
321
+ nameGap: 18,
322
+ nameTextStyle: {
323
+ ...theme.xAxis.nameTextStyle,
324
+ fontSize: 10,
325
+ // Ultra-compact for maximum chart space
326
+ fontWeight: 500
327
+ },
328
+ min: xAxis == null ? void 0 : xAxis.min,
329
+ max: xAxis == null ? void 0 : xAxis.max,
330
+ axisLine: {
331
+ show: (xAxis == null ? void 0 : xAxis.showLine) !== false,
332
+ lineStyle: theme.xAxis.axisLine.lineStyle
333
+ },
334
+ axisTick: {
335
+ lineStyle: theme.xAxis.axisTick.lineStyle
336
+ },
337
+ axisLabel: {
338
+ ...theme.xAxis.axisLabel,
339
+ fontSize: 10,
340
+ // Ultra-compact for maximum chart space
341
+ formatter: xAxis == null ? void 0 : xAxis.labelFormatter,
342
+ // Prevent label overlap - auto-hide overlapping labels
343
+ hideOverlap: true,
344
+ // Rotate labels slightly if needed for better fit
345
+ rotate: 0,
346
+ // Show fewer labels when space is tight
347
+ interval: "auto"
348
+ },
349
+ splitLine: {
350
+ show: (xAxis == null ? void 0 : xAxis.showGrid) !== false,
351
+ lineStyle: { ...theme.xAxis.splitLine.lineStyle, type: theme.xAxis.splitLine.lineStyle.type ?? "solid" }
352
+ },
353
+ inverse: xAxis == null ? void 0 : xAxis.inverse
354
+ } : void 0,
355
+ // Y Axis - ultra-compact font sizes for maximum chart space
356
+ // In compact mode, hide axis name (shown in tooltip instead)
357
+ yAxis: needsGrid ? (Array.isArray(yAxis) ? yAxis : [yAxis || {}]).map((axis, i) => ({
358
+ type: (axis == null ? void 0 : axis.type) || "value",
359
+ name: compactAxes ? void 0 : axis == null ? void 0 : axis.name,
360
+ nameLocation: (axis == null ? void 0 : axis.nameLocation) || "middle",
361
+ nameGap: 28,
362
+ nameTextStyle: {
363
+ ...theme.yAxis.nameTextStyle,
364
+ fontSize: 10,
365
+ // Ultra-compact for maximum chart space
366
+ fontWeight: 500
367
+ },
368
+ position: (axis == null ? void 0 : axis.position) || (i === 0 ? "left" : "right"),
369
+ min: axis == null ? void 0 : axis.min,
370
+ max: axis == null ? void 0 : axis.max,
371
+ splitNumber: axis == null ? void 0 : axis.splitNumber,
372
+ axisLine: {
373
+ show: (axis == null ? void 0 : axis.showLine) !== false,
374
+ lineStyle: theme.yAxis.axisLine.lineStyle
375
+ },
376
+ axisTick: {
377
+ lineStyle: theme.yAxis.axisTick.lineStyle
378
+ },
379
+ axisLabel: {
380
+ ...theme.yAxis.axisLabel,
381
+ fontSize: 10,
382
+ // Ultra-compact for maximum chart space
383
+ formatter: (axis == null ? void 0 : axis.labelFormatter) ? axis.labelFormatter : (axis == null ? void 0 : axis.unit) ? (v) => `${v}${axis.unit}` : void 0
384
+ },
385
+ splitLine: {
386
+ show: (axis == null ? void 0 : axis.showGrid) !== false,
387
+ lineStyle: theme.yAxis.splitLine.lineStyle
388
+ },
389
+ inverse: axis == null ? void 0 : axis.inverse
390
+ })) : void 0,
391
+ // Radar (for radar charts) - transparent background for glassmorphic look
392
+ radar: isPolar ? {
393
+ indicator: ((_g = (_f = series[0]) == null ? void 0 : _f.data) == null ? void 0 : _g.map((d) => {
394
+ const point = d;
395
+ return {
396
+ name: point.label || point.category || String(point.x),
397
+ max: 100
398
+ };
399
+ })) || [],
400
+ splitArea: {
401
+ show: true,
402
+ areaStyle: {
403
+ color: ["transparent", "transparent", "transparent", "transparent", "transparent"]
404
+ }
405
+ },
406
+ splitLine: {
407
+ lineStyle: {
408
+ color: theme.xAxis.splitLine.lineStyle.color,
409
+ opacity: 0.4
410
+ }
411
+ },
412
+ axisLine: {
413
+ lineStyle: {
414
+ color: theme.xAxis.axisLine.lineStyle.color,
415
+ opacity: 0.4
416
+ }
417
+ },
418
+ axisName: {
419
+ color: theme.xAxis.axisLabel.color,
420
+ fontSize: 12
421
+ }
422
+ } : void 0,
423
+ // Data Zoom - inside zoom enabled by default for mouse wheel/pinch
424
+ // Use zoom={{ enabled: false }} to disable, or zoom={{ type: 'slider' }} for slider
425
+ dataZoom: (zoom == null ? void 0 : zoom.enabled) === false ? void 0 : [
426
+ // Inside zoom (mouse wheel / pinch) - ENABLED BY DEFAULT
427
+ ...(zoom == null ? void 0 : zoom.type) === "inside" || (zoom == null ? void 0 : zoom.type) === "both" || !(zoom == null ? void 0 : zoom.type) ? [{
428
+ type: "inside",
429
+ xAxisIndex: (zoom == null ? void 0 : zoom.axis) === "x" || (zoom == null ? void 0 : zoom.axis) === "both" || !(zoom == null ? void 0 : zoom.axis) ? 0 : void 0,
430
+ yAxisIndex: (zoom == null ? void 0 : zoom.axis) === "y" || (zoom == null ? void 0 : zoom.axis) === "both" ? 0 : void 0,
431
+ start: (zoom == null ? void 0 : zoom.start) ?? 0,
432
+ end: (zoom == null ? void 0 : zoom.end) ?? 100,
433
+ minSpan: zoom == null ? void 0 : zoom.minSpan,
434
+ maxSpan: zoom == null ? void 0 : zoom.maxSpan,
435
+ // Smooth zoom animation
436
+ throttle: 50
437
+ }] : [],
438
+ // Slider zoom (only when explicitly requested)
439
+ ...(zoom == null ? void 0 : zoom.type) === "slider" || (zoom == null ? void 0 : zoom.type) === "both" ? [{
440
+ type: "slider",
441
+ show: true,
442
+ xAxisIndex: (zoom == null ? void 0 : zoom.axis) === "x" || (zoom == null ? void 0 : zoom.axis) === "both" ? 0 : void 0,
443
+ yAxisIndex: (zoom == null ? void 0 : zoom.axis) === "y" || (zoom == null ? void 0 : zoom.axis) === "both" ? 0 : void 0,
444
+ start: (zoom == null ? void 0 : zoom.start) ?? 0,
445
+ end: (zoom == null ? void 0 : zoom.end) ?? 100,
446
+ minSpan: zoom == null ? void 0 : zoom.minSpan,
447
+ maxSpan: zoom == null ? void 0 : zoom.maxSpan,
448
+ backgroundColor: theme.dataZoom.backgroundColor,
449
+ borderColor: theme.dataZoom.borderColor,
450
+ fillerColor: theme.dataZoom.fillerColor,
451
+ handleStyle: { color: theme.dataZoom.handleColor },
452
+ textStyle: theme.dataZoom.textStyle,
453
+ height: 25,
454
+ bottom: 10
455
+ }] : []
456
+ ],
457
+ // Toolbox (export) – always use custom dropdown for consistent UI
458
+ toolbox: ((_h = props.export) == null ? void 0 : _h.enabled) ? (() => {
459
+ var _a2;
460
+ return {
461
+ show: false,
462
+ // Hide ECharts toolbox, use custom export button instead
463
+ right: 20,
464
+ top: 10,
465
+ feature: {
466
+ dataView: ((_a2 = props.export.formats) == null ? void 0 : _a2.includes("json")) ? {
467
+ show: true,
468
+ readOnly: true
469
+ } : void 0
470
+ },
471
+ iconStyle: {
472
+ borderColor: theme.textStyle.color
473
+ }
474
+ };
475
+ })() : void 0,
476
+ // Series
477
+ series: transformSeriesToECharts(series, type, theme),
478
+ // Animation
479
+ animation: !loading,
480
+ animationDuration: 300,
481
+ animationEasing: "cubicOut"
482
+ };
483
+ if ((annotations == null ? void 0 : annotations.markLines) && options.series) {
484
+ const seriesArray = options.series;
485
+ if (seriesArray.length > 0) {
486
+ seriesArray[0].markLine = {
487
+ silent: true,
488
+ symbol: "none",
489
+ data: annotations.markLines.data.map((line) => {
490
+ var _a2, _b2;
491
+ return {
492
+ ...line,
493
+ lineStyle: {
494
+ color: line.color || theme.color[2],
495
+ type: ((_a2 = line.lineStyle) == null ? void 0 : _a2.type) || "dashed",
496
+ width: ((_b2 = line.lineStyle) == null ? void 0 : _b2.width) || 1
497
+ },
498
+ label: {
499
+ show: !!line.label,
500
+ formatter: line.label,
501
+ position: "middle",
502
+ // center of line so label doesn't overlap y-axis at bottom
503
+ distance: 8,
504
+ color: theme.textStyle.color,
505
+ fontWeight: 600,
506
+ fontSize: 11,
507
+ textBorderWidth: 0,
508
+ textBorderColor: "transparent",
509
+ backgroundColor: theme.tooltip.backgroundColor,
510
+ borderColor: line.color || theme.color[2],
511
+ borderWidth: 1,
512
+ borderRadius: 4,
513
+ padding: [2, 6]
514
+ }
515
+ };
516
+ })
517
+ };
518
+ }
519
+ }
520
+ if ((annotations == null ? void 0 : annotations.markAreas) && options.series) {
521
+ const seriesArray = options.series;
522
+ const axisLabelColor = ((_j = (_i = theme.xAxis) == null ? void 0 : _i.axisLabel) == null ? void 0 : _j.color) ?? ((_k = theme.textStyle) == null ? void 0 : _k.color) ?? "#94a3b8";
523
+ const fadedColor = axisLabelColor.startsWith("rgba") ? axisLabelColor : `${axisLabelColor}cc`;
524
+ const defaultLabelStyle = {
525
+ show: true,
526
+ color: fadedColor,
527
+ fontSize: 11,
528
+ fontWeight: "500",
529
+ textBorderWidth: 0,
530
+ textBorderColor: "transparent",
531
+ formatter: (params) => {
532
+ const name = ((params == null ? void 0 : params.name) ?? "") || (typeof (params == null ? void 0 : params.value) === "string" ? params.value : "");
533
+ if (!name) return "";
534
+ const icon = name.toLowerCase().includes("eclipse") ? "◐ " : "";
535
+ return icon ? `${icon}${name}` : name;
536
+ }
537
+ };
538
+ if (seriesArray.length > 0) {
539
+ seriesArray[0].markArea = {
540
+ silent: true,
541
+ data: annotations.markAreas.data.map((pair) => {
542
+ const [start, end] = pair;
543
+ return [
544
+ { ...start, label: { ...defaultLabelStyle, formatter: defaultLabelStyle.formatter } },
545
+ end
546
+ ];
547
+ }),
548
+ itemStyle: annotations.markAreas.itemStyle
549
+ };
550
+ }
551
+ }
552
+ if (props.echartsOptions) {
553
+ return mergeOptions(options, props.echartsOptions);
554
+ }
555
+ return options;
556
+ }
557
+ function mergeOptions(base, custom) {
558
+ let mergedSeries = base.series;
559
+ if (custom.series) {
560
+ const baseSeries = base.series || [];
561
+ const customSeries = custom.series;
562
+ const allBaseSeriesEmpty = baseSeries.length === 0 || baseSeries.every((s) => {
563
+ if (!s) return true;
564
+ if (!s.data) return true;
565
+ if (Array.isArray(s.data) && s.data.length === 0) return true;
566
+ return false;
567
+ });
568
+ const baseLen = baseSeries.length;
569
+ const customLen = customSeries.length;
570
+ if (baseLen === 1 && customLen === 1) {
571
+ const b = baseSeries[0];
572
+ const c = customSeries[0];
573
+ if ((b == null ? void 0 : b.type) === "gauge" && (c == null ? void 0 : c.type) === "gauge") {
574
+ mergedSeries = customSeries;
575
+ } else if (allBaseSeriesEmpty) {
576
+ mergedSeries = customSeries;
577
+ } else {
578
+ mergedSeries = [...baseSeries, ...customSeries];
579
+ }
580
+ } else if (allBaseSeriesEmpty) {
581
+ mergedSeries = customSeries;
582
+ } else {
583
+ mergedSeries = [...baseSeries, ...customSeries];
584
+ }
585
+ }
586
+ return {
587
+ ...base,
588
+ ...custom,
589
+ series: mergedSeries
590
+ };
591
+ }
592
+ const AstroChart = memo(forwardRef(
593
+ function AstroChart2(props, ref) {
594
+ var _a, _b, _c, _d, _e, _f, _g;
595
+ const {
596
+ width = "100%",
597
+ height = 400,
598
+ loading = false,
599
+ emptyMessage = "No data available",
600
+ className = "",
601
+ style,
602
+ ariaLabel,
603
+ onChartReady,
604
+ onClick,
605
+ onHover,
606
+ onZoomChange,
607
+ onBrushSelect,
608
+ realTime,
609
+ series = [],
610
+ infoTooltip,
611
+ // Header icon/status system
612
+ icon,
613
+ iconStatus,
614
+ statusMessage,
615
+ showStatusBadge = false
616
+ } = props;
617
+ const chartRef = useRef(null);
618
+ const { tokens, mode, theme: themeVariant } = useTheme();
619
+ const isTransparentTheme = themeVariant === "transparent" || themeVariant === "transparent-bold" || themeVariant === "transparent-minimal";
620
+ const isBoldVariant = themeVariant === "transparent-bold";
621
+ const isMinimalVariant = themeVariant === "transparent-minimal";
622
+ const cardBorderStyle = useCardBorderStyle(tokens, isTransparentTheme);
623
+ const glassAccentColor = tokens.colors.accent.primary;
624
+ const glassAccentMuted = `${glassAccentColor}66`;
625
+ const headingConfig = ((_b = (_a = tokens.layout) == null ? void 0 : _a.card) == null ? void 0 : _b.heading) ?? {
626
+ iconSize: 20,
627
+ iconSizeCompact: 16,
628
+ titleFontSize: "1.125rem",
629
+ titleFontSizeChart: "0.875rem",
630
+ titleFontWeight: 500,
631
+ gap: 8
632
+ };
633
+ const chartTitleFontSize = headingConfig.titleFontSizeChart ?? headingConfig.titleFontSize;
634
+ const theme = useMemo(
635
+ () => createAstroEChartsTheme(tokens, mode, themeVariant),
636
+ [tokens, mode, themeVariant]
637
+ );
638
+ const hideEChartsTitle = !!(icon || props.title);
639
+ const options = useMemo(
640
+ () => buildEChartsOptions(props, theme, hideEChartsTitle),
641
+ [props, theme, hideEChartsTitle]
642
+ );
643
+ const getChart = useCallback(() => {
644
+ var _a2;
645
+ return (_a2 = chartRef.current) == null ? void 0 : _a2.getEchartsInstance();
646
+ }, []);
647
+ const getExportFileName = useCallback((extension, includeTimestamp = true) => {
648
+ var _a2, _b2;
649
+ const context = {
650
+ title: props.title,
651
+ subtitle: props.subtitle,
652
+ seriesNames: series.map((s) => s.name),
653
+ chartType: props.type
654
+ };
655
+ let base;
656
+ if ((_a2 = props.export) == null ? void 0 : _a2.getExportFileName) {
657
+ base = props.export.getExportFileName(context);
658
+ } else if ((_b2 = props.export) == null ? void 0 : _b2.fileName) {
659
+ base = props.export.fileName;
660
+ } else {
661
+ base = derivedExportBaseName(props.title, context.seriesNames, props.type);
662
+ }
663
+ if (includeTimestamp) {
664
+ const d = /* @__PURE__ */ new Date();
665
+ const ts = d.toISOString().slice(0, 19).replace(/[-T:]/g, "").replace(/(\d{8})(\d{6})/, "$1-$2");
666
+ base = `${base}-${ts}`;
667
+ }
668
+ if (extension) base = `${base}.${extension.replace(/^\./, "")}`;
669
+ return base;
670
+ }, [props.title, props.subtitle, props.type, props.export, series]);
671
+ useImperativeHandle(ref, () => ({
672
+ getChart,
673
+ resize: () => {
674
+ var _a2;
675
+ return (_a2 = getChart()) == null ? void 0 : _a2.resize();
676
+ },
677
+ getExportFileName,
678
+ exportImage: (type = "png", pixelRatio = 2) => {
679
+ const chart = getChart();
680
+ if (!chart) return void 0;
681
+ const imgType = type === "jpg" ? "jpeg" : type;
682
+ return chart.getDataURL({
683
+ type: imgType,
684
+ pixelRatio,
685
+ backgroundColor: "transparent"
686
+ });
687
+ },
688
+ exportCSV: () => {
689
+ var _a2, _b2;
690
+ const rows = [];
691
+ const headers = ["Index", ...series.map((s) => s.name)];
692
+ rows.push(headers.join(","));
693
+ const maxLen = Math.max(...series.map((s) => {
694
+ var _a3;
695
+ return ((_a3 = s.data) == null ? void 0 : _a3.length) || 0;
696
+ }));
697
+ for (let i = 0; i < maxLen; i++) {
698
+ const row = [i.toString()];
699
+ for (const s of series) {
700
+ const data = s.data;
701
+ const value = data == null ? void 0 : data[i];
702
+ if (typeof value === "number") {
703
+ row.push(value.toString());
704
+ } else if (Array.isArray(value)) {
705
+ row.push(((_a2 = value[1]) == null ? void 0 : _a2.toString()) || "");
706
+ } else if (value && typeof value === "object") {
707
+ const point = value;
708
+ row.push(((_b2 = "value" in point ? point.value : point.y) == null ? void 0 : _b2.toString()) || "");
709
+ } else {
710
+ row.push("");
711
+ }
712
+ }
713
+ rows.push(row.join(","));
714
+ }
715
+ return rows.join("\n");
716
+ },
717
+ appendData: (seriesIndex, data) => {
718
+ var _a2;
719
+ const chart = getChart();
720
+ if (!chart) return;
721
+ const option = chart.getOption();
722
+ const seriesOpt = (_a2 = option.series) == null ? void 0 : _a2[seriesIndex];
723
+ if (!seriesOpt) return;
724
+ const currentData = [...seriesOpt.data || []];
725
+ const newPoint = "time" in data ? [data.time, data.value] : [data.x, data.y];
726
+ currentData.push(newPoint);
727
+ const maxPoints = (realTime == null ? void 0 : realTime.maxPoints) || 1e3;
728
+ while (currentData.length > maxPoints) {
729
+ currentData.shift();
730
+ }
731
+ chart.setOption({
732
+ series: [{
733
+ data: currentData
734
+ }]
735
+ }, {
736
+ replaceMerge: ["series"]
737
+ });
738
+ },
739
+ clearData: () => {
740
+ const chart = getChart();
741
+ if (!chart) return;
742
+ chart.setOption({
743
+ series: series.map(() => ({ data: [] }))
744
+ });
745
+ },
746
+ dispatchAction: (action) => {
747
+ var _a2;
748
+ (_a2 = getChart()) == null ? void 0 : _a2.dispatchAction(action);
749
+ }
750
+ }), [getChart, series, realTime]);
751
+ const handleEvents = useMemo(() => {
752
+ const events = {};
753
+ if (onClick) {
754
+ events.click = (params) => {
755
+ onClick(params);
756
+ };
757
+ }
758
+ if (onHover) {
759
+ events.mouseover = (params) => {
760
+ onHover(params);
761
+ };
762
+ }
763
+ if (onZoomChange) {
764
+ events.datazoom = (params) => {
765
+ var _a2, _b2, _c2, _d2;
766
+ const p = params;
767
+ onZoomChange({
768
+ start: ((_b2 = (_a2 = p.batch) == null ? void 0 : _a2[0]) == null ? void 0 : _b2.start) ?? p.start ?? 0,
769
+ end: ((_d2 = (_c2 = p.batch) == null ? void 0 : _c2[0]) == null ? void 0 : _d2.end) ?? p.end ?? 100
770
+ });
771
+ };
772
+ }
773
+ if (onBrushSelect) {
774
+ events.brushselected = (params) => {
775
+ onBrushSelect(params);
776
+ };
777
+ }
778
+ return events;
779
+ }, [onClick, onHover, onZoomChange, onBrushSelect]);
780
+ const handleChartReady = useCallback((chart) => {
781
+ onChartReady == null ? void 0 : onChartReady(chart);
782
+ }, [onChartReady]);
783
+ const exportFormats = ((_c = props.export) == null ? void 0 : _c.formats) ?? ["png"];
784
+ const useCustomExportDropdown = (_d = props.export) == null ? void 0 : _d.enabled;
785
+ const [exportMenuOpen, setExportMenuOpen] = useState(false);
786
+ const exportMenuRef = useRef(null);
787
+ const [infoTooltipOpen, setInfoTooltipOpen] = useState(false);
788
+ const infoTooltipRef = useRef(null);
789
+ useEffect(() => {
790
+ if (!infoTooltip || !infoTooltipOpen) return;
791
+ const handleClickOutside = (e) => {
792
+ if (infoTooltipRef.current && !infoTooltipRef.current.contains(e.target)) {
793
+ setInfoTooltipOpen(false);
794
+ }
795
+ };
796
+ document.addEventListener("mousedown", handleClickOutside);
797
+ return () => document.removeEventListener("mousedown", handleClickOutside);
798
+ }, [infoTooltip, infoTooltipOpen]);
799
+ useEffect(() => {
800
+ if (!useCustomExportDropdown || !exportMenuOpen) return;
801
+ const handleClickOutside = (e) => {
802
+ if (exportMenuRef.current && !exportMenuRef.current.contains(e.target)) {
803
+ setExportMenuOpen(false);
804
+ }
805
+ };
806
+ document.addEventListener("mousedown", handleClickOutside);
807
+ return () => document.removeEventListener("mousedown", handleClickOutside);
808
+ }, [useCustomExportDropdown, exportMenuOpen]);
809
+ const handleExportFormat = useCallback((format) => {
810
+ var _a2, _b2, _c2, _d2;
811
+ const chart = getChart();
812
+ if (format === "csv") {
813
+ const rows = [];
814
+ const headers = ["Index", ...series.map((s) => s.name)];
815
+ rows.push(headers.join(","));
816
+ const maxLen = Math.max(...series.map((s) => {
817
+ var _a3;
818
+ return ((_a3 = s.data) == null ? void 0 : _a3.length) || 0;
819
+ }));
820
+ for (let i = 0; i < maxLen; i++) {
821
+ const row = [i.toString()];
822
+ for (const s of series) {
823
+ const data = s.data;
824
+ const value = data == null ? void 0 : data[i];
825
+ if (typeof value === "number") row.push(value.toString());
826
+ else if (Array.isArray(value)) row.push(((_a2 = value[1]) == null ? void 0 : _a2.toString()) || "");
827
+ else if (value && typeof value === "object") {
828
+ const point = value;
829
+ row.push(((_b2 = "value" in point ? point.value : point.y) == null ? void 0 : _b2.toString()) || "");
830
+ } else row.push("");
831
+ }
832
+ rows.push(row.join(","));
833
+ }
834
+ const csv = rows.join("\n");
835
+ const filename = getExportFileName("csv", true);
836
+ const blob = new Blob([csv], { type: "text/csv;charset=utf-8" });
837
+ const url = URL.createObjectURL(blob);
838
+ const a = document.createElement("a");
839
+ a.href = url;
840
+ a.download = filename;
841
+ a.click();
842
+ URL.revokeObjectURL(url);
843
+ } else if (chart) {
844
+ const imgType = format === "jpeg" ? "jpeg" : format;
845
+ const dataUrl = chart.getDataURL({
846
+ type: imgType,
847
+ pixelRatio: ((_c2 = props.export) == null ? void 0 : _c2.pixelRatio) || 2,
848
+ backgroundColor: ((_d2 = props.export) == null ? void 0 : _d2.backgroundColor) || "transparent"
849
+ });
850
+ if (dataUrl) {
851
+ const ext = format === "jpeg" ? "jpg" : format;
852
+ const filename = getExportFileName(ext, true);
853
+ const a = document.createElement("a");
854
+ a.href = dataUrl;
855
+ a.download = filename;
856
+ a.click();
857
+ }
858
+ }
859
+ setExportMenuOpen(false);
860
+ }, [getChart, series, getExportFileName, (_e = props.export) == null ? void 0 : _e.pixelRatio, (_f = props.export) == null ? void 0 : _f.backgroundColor]);
861
+ useEffect(() => {
862
+ var _a2;
863
+ const chart = getChart();
864
+ if (!chart) return;
865
+ let mounted = true;
866
+ const resizeObserver = new ResizeObserver(() => {
867
+ if (!mounted) return;
868
+ try {
869
+ chart.resize();
870
+ } catch {
871
+ }
872
+ });
873
+ const container = (_a2 = chartRef.current) == null ? void 0 : _a2.ele;
874
+ if (container) {
875
+ resizeObserver.observe(container);
876
+ }
877
+ return () => {
878
+ mounted = false;
879
+ resizeObserver.disconnect();
880
+ };
881
+ }, [getChart]);
882
+ const needs3DCanvas = useMemo(() => {
883
+ const eo = props.echartsOptions;
884
+ if (!eo) return false;
885
+ if (eo.grid3D || eo.xAxis3D || eo.yAxis3D || eo.zAxis3D || eo.globe || eo.geo3D) return true;
886
+ if (Array.isArray(eo.series)) {
887
+ return eo.series.some((s) => {
888
+ const t = (s == null ? void 0 : s.type) || "";
889
+ return t.includes("3D") || t.includes("3d") || t === "scatter3D" || t === "bar3D" || t === "surface" || t === "lines3D";
890
+ });
891
+ }
892
+ return false;
893
+ }, [props.echartsOptions]);
894
+ const hasSeriesData = series.some((s) => s.data && s.data.length > 0);
895
+ const hasEchartsOptionsSeries = ((_g = props.echartsOptions) == null ? void 0 : _g.series) && (Array.isArray(props.echartsOptions.series) ? props.echartsOptions.series.length > 0 : props.echartsOptions.series !== void 0);
896
+ const hasData = hasSeriesData || hasEchartsOptionsSeries;
897
+ const getStatusColor = (status) => {
898
+ var _a2, _b2, _c2, _d2, _e2, _f2;
899
+ switch (status) {
900
+ case "critical":
901
+ return ((_a2 = tokens.colors.status) == null ? void 0 : _a2.critical) || "#ff3838";
902
+ case "serious":
903
+ return ((_b2 = tokens.colors.status) == null ? void 0 : _b2.serious) || "#ffb302";
904
+ case "caution":
905
+ return ((_c2 = tokens.colors.status) == null ? void 0 : _c2.caution) || "#fce83a";
906
+ case "normal":
907
+ return ((_d2 = tokens.colors.status) == null ? void 0 : _d2.normal) || "#56f000";
908
+ case "standby":
909
+ return ((_e2 = tokens.colors.status) == null ? void 0 : _e2.standby) || "#2dccff";
910
+ case "off":
911
+ return ((_f2 = tokens.colors.status) == null ? void 0 : _f2.off) || "#a4abb6";
912
+ default:
913
+ return tokens.colors.text.secondary;
914
+ }
915
+ };
916
+ const hasHeader = !!(icon || props.title);
917
+ const useGlassMode = isBoldVariant || isMinimalVariant;
918
+ const containerStyle = {
919
+ width: typeof width === "number" ? `${width}px` : width,
920
+ height: typeof height === "number" ? `${height}px` : height,
921
+ borderRadius: tokens.borderRadius.lg,
922
+ padding: tokens.spacing.sm,
923
+ position: "relative",
924
+ overflow: "hidden",
925
+ // Glass mode styling for bold/minimal (matching Container)
926
+ ...useGlassMode ? {
927
+ backgroundColor: "rgba(10, 15, 25, 0.35)",
928
+ backdropFilter: "blur(12px)",
929
+ WebkitBackdropFilter: "blur(12px)",
930
+ border: `1px solid ${glassAccentMuted}`,
931
+ borderTop: isBoldVariant ? `2px solid ${glassAccentColor}` : `1px solid ${glassAccentColor}`,
932
+ transition: "all 0.3s ease-out, box-shadow 0.3s ease-out"
933
+ } : isTransparentTheme ? {
934
+ // Regular transparent (glass) theme — match top cards (ISS): transparent + blur
935
+ backgroundColor: "transparent",
936
+ backdropFilter: "blur(12px)",
937
+ WebkitBackdropFilter: "blur(12px)",
938
+ ...cardBorderStyle
939
+ } : {
940
+ // Non-transparent themes — use same border as Container (muted) so chart doesn't stand out
941
+ backgroundColor: tokens.colors.background.surface,
942
+ border: `1px solid ${tokens.colors.border.muted}`
943
+ },
944
+ ...style
945
+ };
946
+ const glassGradient = useGlassMode ? `linear-gradient(135deg, ${glassAccentColor}10 0%, ${glassAccentColor}03 100%)` : void 0;
947
+ if (!hasData && !loading) {
948
+ return /* @__PURE__ */ jsxs(
949
+ "div",
950
+ {
951
+ className: `astro-chart astro-chart--empty ${className}`,
952
+ style: containerStyle,
953
+ role: "group",
954
+ "aria-roledescription": "chart",
955
+ "aria-label": ariaLabel || "Empty chart",
956
+ children: [
957
+ useGlassMode && glassGradient && /* @__PURE__ */ jsx(
958
+ "div",
959
+ {
960
+ style: {
961
+ position: "absolute",
962
+ inset: 0,
963
+ background: glassGradient,
964
+ pointerEvents: "none",
965
+ zIndex: 0
966
+ }
967
+ }
968
+ ),
969
+ /* @__PURE__ */ jsx("div", { style: {
970
+ display: "flex",
971
+ alignItems: "center",
972
+ justifyContent: "center",
973
+ height: "100%",
974
+ color: tokens.colors.text.secondary,
975
+ fontFamily: tokens.typography.fontFamily.primary,
976
+ fontSize: tokens.typography.fontSize.sm,
977
+ position: "relative",
978
+ zIndex: 1
979
+ }, children: emptyMessage })
980
+ ]
981
+ }
982
+ );
983
+ }
984
+ const exportImageFormats = exportFormats.filter((f) => ["png", "jpeg", "jpg", "svg"].includes(f));
985
+ const hasCsv = exportFormats.includes("csv");
986
+ return /* @__PURE__ */ jsxs(
987
+ "div",
988
+ {
989
+ className: `astro-chart ${className}`,
990
+ style: containerStyle,
991
+ role: "group",
992
+ "aria-roledescription": "chart",
993
+ "aria-label": ariaLabel || props.title || "Chart",
994
+ children: [
995
+ useGlassMode && glassGradient && /* @__PURE__ */ jsx(
996
+ "div",
997
+ {
998
+ style: {
999
+ position: "absolute",
1000
+ inset: 0,
1001
+ background: glassGradient,
1002
+ pointerEvents: "none",
1003
+ zIndex: 0
1004
+ }
1005
+ }
1006
+ ),
1007
+ hasHeader && isBoldVariant && /* @__PURE__ */ jsxs("div", { style: { position: "relative", zIndex: 1 }, children: [
1008
+ /* @__PURE__ */ jsx("div", { style: {
1009
+ display: "flex",
1010
+ alignItems: "center",
1011
+ justifyContent: "space-between",
1012
+ padding: `12px ${tokens.spacing.sm} 0`
1013
+ }, children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: headingConfig.gap }, children: [
1014
+ icon && iconStatus && /* @__PURE__ */ jsx(
1015
+ HeaderIconWithStatus,
1016
+ {
1017
+ icon,
1018
+ size: headingConfig.iconSize,
1019
+ status: iconStatus,
1020
+ statusColor: getStatusColor(iconStatus)
1021
+ }
1022
+ ),
1023
+ icon && !iconStatus && /* @__PURE__ */ jsx(
1024
+ AstroIcon,
1025
+ {
1026
+ name: icon,
1027
+ size: headingConfig.iconSize,
1028
+ color: glassAccentColor
1029
+ }
1030
+ ),
1031
+ props.title && /* @__PURE__ */ jsx("h3", { style: {
1032
+ margin: 0,
1033
+ fontSize: chartTitleFontSize,
1034
+ fontWeight: headingConfig.titleFontWeight,
1035
+ color: glassAccentColor,
1036
+ letterSpacing: "0.5px",
1037
+ textShadow: `0 0 20px ${glassAccentMuted}`
1038
+ }, children: props.title })
1039
+ ] }) }),
1040
+ /* @__PURE__ */ jsxs("div", { style: {
1041
+ display: "flex",
1042
+ alignItems: "center",
1043
+ gap: "8px",
1044
+ padding: `10px ${tokens.spacing.sm} 0`,
1045
+ marginBottom: tokens.spacing.xs
1046
+ }, children: [
1047
+ /* @__PURE__ */ jsx("div", { style: {
1048
+ flex: 1,
1049
+ height: "2px",
1050
+ background: `linear-gradient(90deg, ${glassAccentColor} 0%, ${glassAccentMuted} 50%, transparent 100%)`,
1051
+ boxShadow: `0 0 8px ${glassAccentMuted}`
1052
+ } }),
1053
+ showStatusBadge && iconStatus && /* @__PURE__ */ jsxs("span", { style: {
1054
+ display: "inline-flex",
1055
+ alignItems: "center",
1056
+ gap: "4px",
1057
+ padding: "2px 8px",
1058
+ fontSize: "0.625rem",
1059
+ // 10px in rem
1060
+ fontWeight: 500,
1061
+ // AstroUXDS medium (not 600)
1062
+ letterSpacing: "0.05em",
1063
+ color: getStatusColor(iconStatus),
1064
+ backgroundColor: `${getStatusColor(iconStatus)}20`,
1065
+ border: `1px solid ${getStatusColor(iconStatus)}40`,
1066
+ borderRadius: "3px",
1067
+ whiteSpace: "nowrap"
1068
+ }, children: [
1069
+ STATUS_SHAPES[iconStatus](6, getStatusColor(iconStatus)),
1070
+ STATUS_LABELS[iconStatus]
1071
+ ] })
1072
+ ] })
1073
+ ] }),
1074
+ hasHeader && isMinimalVariant && /* @__PURE__ */ jsxs("div", { style: {
1075
+ display: "flex",
1076
+ alignItems: "center",
1077
+ padding: `${tokens.spacing.xs} ${tokens.spacing.sm} 0`,
1078
+ marginBottom: tokens.spacing.xs,
1079
+ position: "relative",
1080
+ zIndex: 1
1081
+ }, children: [
1082
+ icon && iconStatus && /* @__PURE__ */ jsx(
1083
+ HeaderIconWithStatus,
1084
+ {
1085
+ icon,
1086
+ size: headingConfig.iconSizeCompact,
1087
+ status: iconStatus,
1088
+ statusColor: getStatusColor(iconStatus),
1089
+ style: { marginRight: tokens.spacing.sm }
1090
+ }
1091
+ ),
1092
+ icon && !iconStatus && /* @__PURE__ */ jsx(
1093
+ AstroIcon,
1094
+ {
1095
+ name: icon,
1096
+ size: headingConfig.iconSizeCompact,
1097
+ color: glassAccentColor,
1098
+ style: { marginRight: tokens.spacing.sm }
1099
+ }
1100
+ ),
1101
+ props.title && /* @__PURE__ */ jsx("h3", { style: {
1102
+ margin: 0,
1103
+ fontSize: chartTitleFontSize,
1104
+ fontWeight: headingConfig.titleFontWeight,
1105
+ color: glassAccentColor,
1106
+ textShadow: `0 0 16px ${glassAccentMuted}`,
1107
+ letterSpacing: "0.02em",
1108
+ whiteSpace: "nowrap",
1109
+ marginRight: "12px"
1110
+ }, children: props.title }),
1111
+ /* @__PURE__ */ jsxs("div", { style: {
1112
+ flex: 1,
1113
+ display: "flex",
1114
+ alignItems: "center",
1115
+ gap: headingConfig.gap,
1116
+ minWidth: 40
1117
+ }, children: [
1118
+ /* @__PURE__ */ jsx("div", { style: {
1119
+ flex: 1,
1120
+ height: "1px",
1121
+ background: `linear-gradient(90deg, ${glassAccentColor} 0%, ${glassAccentMuted} 60%, transparent 100%)`
1122
+ } }),
1123
+ showStatusBadge && iconStatus && /* @__PURE__ */ jsxs("span", { style: {
1124
+ display: "inline-flex",
1125
+ alignItems: "center",
1126
+ gap: "3px",
1127
+ padding: "1px 6px",
1128
+ fontSize: "0.5625rem",
1129
+ // 9px in rem (micro)
1130
+ fontWeight: 500,
1131
+ // AstroUXDS medium (not 600)
1132
+ letterSpacing: "0.05em",
1133
+ color: getStatusColor(iconStatus),
1134
+ backgroundColor: `${getStatusColor(iconStatus)}15`,
1135
+ border: `1px solid ${getStatusColor(iconStatus)}30`,
1136
+ borderRadius: "2px",
1137
+ whiteSpace: "nowrap"
1138
+ }, children: [
1139
+ STATUS_SHAPES[iconStatus](5, getStatusColor(iconStatus)),
1140
+ STATUS_LABELS[iconStatus]
1141
+ ] })
1142
+ ] })
1143
+ ] }),
1144
+ hasHeader && isTransparentTheme && !useGlassMode && /* @__PURE__ */ jsxs(
1145
+ "div",
1146
+ {
1147
+ title: statusMessage,
1148
+ style: {
1149
+ position: "absolute",
1150
+ top: 6,
1151
+ left: 8,
1152
+ display: "flex",
1153
+ alignItems: "center",
1154
+ gap: headingConfig.gap,
1155
+ zIndex: 10
1156
+ },
1157
+ children: [
1158
+ icon && iconStatus && /* @__PURE__ */ jsx(
1159
+ HeaderIconWithStatus,
1160
+ {
1161
+ icon,
1162
+ size: headingConfig.iconSizeCompact,
1163
+ status: iconStatus,
1164
+ statusColor: getStatusColor(iconStatus)
1165
+ }
1166
+ ),
1167
+ icon && !iconStatus && /* @__PURE__ */ jsx(
1168
+ AstroIcon,
1169
+ {
1170
+ name: icon,
1171
+ size: headingConfig.iconSizeCompact,
1172
+ color: tokens.colors.text.tertiary
1173
+ }
1174
+ ),
1175
+ props.title && /* @__PURE__ */ jsx("span", { style: {
1176
+ fontSize: chartTitleFontSize,
1177
+ fontWeight: headingConfig.titleFontWeight,
1178
+ color: tokens.colors.text.primary
1179
+ }, children: props.title })
1180
+ ]
1181
+ }
1182
+ ),
1183
+ hasHeader && !isTransparentTheme && /* @__PURE__ */ jsxs(
1184
+ "div",
1185
+ {
1186
+ title: statusMessage,
1187
+ style: {
1188
+ position: "absolute",
1189
+ top: 6,
1190
+ left: 8,
1191
+ display: "flex",
1192
+ alignItems: "center",
1193
+ gap: headingConfig.gap,
1194
+ zIndex: 10
1195
+ },
1196
+ children: [
1197
+ icon && iconStatus && /* @__PURE__ */ jsx(
1198
+ HeaderIconWithStatus,
1199
+ {
1200
+ icon,
1201
+ size: headingConfig.iconSizeCompact,
1202
+ status: iconStatus,
1203
+ statusColor: getStatusColor(iconStatus)
1204
+ }
1205
+ ),
1206
+ icon && !iconStatus && /* @__PURE__ */ jsx(
1207
+ AstroIcon,
1208
+ {
1209
+ name: icon,
1210
+ size: headingConfig.iconSizeCompact,
1211
+ color: tokens.colors.text.tertiary
1212
+ }
1213
+ ),
1214
+ props.title && /* @__PURE__ */ jsx("span", { style: {
1215
+ fontSize: chartTitleFontSize,
1216
+ fontWeight: headingConfig.titleFontWeight,
1217
+ color: tokens.colors.text.primary
1218
+ }, children: props.title })
1219
+ ]
1220
+ }
1221
+ ),
1222
+ /* @__PURE__ */ jsx(
1223
+ ReactEChartsCore,
1224
+ {
1225
+ ref: chartRef,
1226
+ echarts,
1227
+ option: options,
1228
+ opts: {
1229
+ renderer: props.renderer ?? (needs3DCanvas ? "canvas" : "svg"),
1230
+ ...(props.renderer === "canvas" || needs3DCanvas) && typeof window !== "undefined" && { devicePixelRatio: window.devicePixelRatio || 1 }
1231
+ },
1232
+ style: { width: "100%", height: "100%" },
1233
+ notMerge: true,
1234
+ lazyUpdate: true,
1235
+ showLoading: loading,
1236
+ loadingOption: {
1237
+ text: "Loading...",
1238
+ color: tokens.colors.accent.primary,
1239
+ textColor: tokens.colors.text.primary,
1240
+ maskColor: isTransparentTheme ? "rgba(10, 15, 25, 0.6)" : `${tokens.colors.background.surface}80`
1241
+ },
1242
+ onEvents: handleEvents,
1243
+ onChartReady: handleChartReady
1244
+ }
1245
+ ),
1246
+ /* @__PURE__ */ jsxs("div", { style: {
1247
+ position: "absolute",
1248
+ right: 8,
1249
+ top: 6,
1250
+ zIndex: 10,
1251
+ display: "flex",
1252
+ alignItems: "center",
1253
+ gap: 4
1254
+ }, children: [
1255
+ infoTooltip && /* @__PURE__ */ jsxs("div", { ref: infoTooltipRef, style: { position: "relative" }, children: [
1256
+ /* @__PURE__ */ jsx(
1257
+ "button",
1258
+ {
1259
+ type: "button",
1260
+ onClick: () => setInfoTooltipOpen((v) => !v),
1261
+ "aria-label": "Chart information",
1262
+ "aria-expanded": infoTooltipOpen,
1263
+ style: {
1264
+ display: "flex",
1265
+ alignItems: "center",
1266
+ justifyContent: "center",
1267
+ width: 20,
1268
+ height: 20,
1269
+ padding: 4,
1270
+ border: `1px solid ${tokens.colors.border.default}`,
1271
+ borderRadius: "50%",
1272
+ background: infoTooltipOpen ? tokens.colors.accent.primary : isTransparentTheme ? "rgba(10, 15, 25, 0.5)" : tokens.colors.background.surface,
1273
+ color: infoTooltipOpen ? tokens.colors.background.base : tokens.colors.text.secondary,
1274
+ cursor: "pointer",
1275
+ transition: "all 0.15s ease"
1276
+ },
1277
+ children: infoTooltip.icon || /* @__PURE__ */ jsx("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true", children: /* @__PURE__ */ jsx("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" }) })
1278
+ }
1279
+ ),
1280
+ infoTooltipOpen && /* @__PURE__ */ jsx(
1281
+ "div",
1282
+ {
1283
+ role: "tooltip",
1284
+ style: {
1285
+ position: "absolute",
1286
+ right: 0,
1287
+ top: "100%",
1288
+ marginTop: 8,
1289
+ minWidth: 200,
1290
+ maxWidth: infoTooltip.maxWidth || 300,
1291
+ padding: "12px 16px",
1292
+ background: isTransparentTheme ? "rgba(10, 15, 25, 0.85)" : tokens.colors.background.surface,
1293
+ backdropFilter: isTransparentTheme ? "blur(12px)" : void 0,
1294
+ border: `1px solid ${tokens.colors.border.default}`,
1295
+ borderRadius: tokens.borderRadius.md,
1296
+ boxShadow: "0 4px 16px rgba(0,0,0,0.25)",
1297
+ color: tokens.colors.text.primary,
1298
+ fontFamily: tokens.typography.fontFamily.primary,
1299
+ fontSize: tokens.typography.fontSize.sm,
1300
+ lineHeight: 1.5,
1301
+ zIndex: 20
1302
+ },
1303
+ children: infoTooltip.content
1304
+ }
1305
+ )
1306
+ ] }),
1307
+ useCustomExportDropdown && /* @__PURE__ */ jsxs("div", { ref: exportMenuRef, style: { position: "relative" }, children: [
1308
+ /* @__PURE__ */ jsx(
1309
+ "button",
1310
+ {
1311
+ type: "button",
1312
+ onClick: () => setExportMenuOpen((v) => !v),
1313
+ "aria-haspopup": "true",
1314
+ "aria-expanded": exportMenuOpen,
1315
+ "aria-label": "Save chart as",
1316
+ style: {
1317
+ display: "flex",
1318
+ alignItems: "center",
1319
+ justifyContent: "center",
1320
+ width: 20,
1321
+ height: 20,
1322
+ padding: 4,
1323
+ border: `1px solid ${tokens.colors.border.default}`,
1324
+ borderRadius: "50%",
1325
+ background: isTransparentTheme ? "rgba(10, 15, 25, 0.5)" : tokens.colors.background.surface,
1326
+ color: tokens.colors.text.secondary,
1327
+ cursor: "pointer",
1328
+ transition: "all 0.15s ease"
1329
+ },
1330
+ children: /* @__PURE__ */ jsxs("svg", { width: "10", height: "10", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
1331
+ /* @__PURE__ */ jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
1332
+ /* @__PURE__ */ jsx("polyline", { points: "7 10 12 15 17 10" }),
1333
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: "15", x2: "12", y2: "3" })
1334
+ ] })
1335
+ }
1336
+ ),
1337
+ exportMenuOpen && /* @__PURE__ */ jsxs(
1338
+ "div",
1339
+ {
1340
+ role: "menu",
1341
+ style: {
1342
+ position: "absolute",
1343
+ right: 0,
1344
+ top: "100%",
1345
+ marginTop: 4,
1346
+ minWidth: 120,
1347
+ padding: 4,
1348
+ background: tokens.colors.background.surface,
1349
+ border: `1px solid ${tokens.colors.border.default}`,
1350
+ borderRadius: tokens.borderRadius.md,
1351
+ boxShadow: "0 4px 12px rgba(0,0,0,0.2)"
1352
+ },
1353
+ children: [
1354
+ exportImageFormats.map((f) => /* @__PURE__ */ jsx(
1355
+ "button",
1356
+ {
1357
+ type: "button",
1358
+ role: "menuitem",
1359
+ onClick: () => handleExportFormat(f === "jpg" ? "jpeg" : f),
1360
+ style: {
1361
+ display: "block",
1362
+ width: "100%",
1363
+ padding: "8px 12px",
1364
+ border: "none",
1365
+ borderRadius: tokens.borderRadius.sm,
1366
+ background: "transparent",
1367
+ color: tokens.colors.text.primary,
1368
+ fontFamily: tokens.typography.fontFamily.primary,
1369
+ fontSize: tokens.typography.fontSize.sm,
1370
+ textAlign: "left",
1371
+ cursor: "pointer"
1372
+ },
1373
+ children: f.toUpperCase()
1374
+ },
1375
+ f
1376
+ )),
1377
+ hasCsv && /* @__PURE__ */ jsx(
1378
+ "button",
1379
+ {
1380
+ type: "button",
1381
+ role: "menuitem",
1382
+ onClick: () => handleExportFormat("csv"),
1383
+ style: {
1384
+ display: "block",
1385
+ width: "100%",
1386
+ padding: "8px 12px",
1387
+ border: "none",
1388
+ borderRadius: tokens.borderRadius.sm,
1389
+ background: "transparent",
1390
+ color: tokens.colors.text.primary,
1391
+ fontFamily: tokens.typography.fontFamily.primary,
1392
+ fontSize: tokens.typography.fontSize.sm,
1393
+ textAlign: "left",
1394
+ cursor: "pointer"
1395
+ },
1396
+ children: "CSV"
1397
+ }
1398
+ )
1399
+ ]
1400
+ }
1401
+ )
1402
+ ] })
1403
+ ] })
1404
+ ]
1405
+ }
1406
+ );
1407
+ }
1408
+ ));
1409
+ export {
1410
+ AstroChart
1411
+ };
1412
+ //# sourceMappingURL=AstroChart.js.map