@powerportalspro/react-charts 5.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,461 @@
1
+ 'use strict';
2
+
3
+ var core = require('@powerportalspro/core');
4
+ var react = require('react');
5
+ var chart_js = require('chart.js');
6
+ var chartjsChartFunnel = require('chartjs-chart-funnel');
7
+ var jsxRuntime = require('react/jsx-runtime');
8
+
9
+ // src/types.ts
10
+ var ChartType = {
11
+ /** Bar chart (`'bar'`). */
12
+ Bar: "bar",
13
+ /** Line chart (`'line'`). */
14
+ Line: "line",
15
+ /** Pie chart (`'pie'`). */
16
+ Pie: "pie",
17
+ /** Doughnut chart (`'doughnut'`). */
18
+ Doughnut: "doughnut",
19
+ /** Polar area chart (`'polarArea'`). */
20
+ PolarArea: "polarArea",
21
+ /** Radar chart (`'radar'`). */
22
+ Radar: "radar",
23
+ /** Bubble chart (`'bubble'`). */
24
+ Bubble: "bubble",
25
+ /** Scatter chart (`'scatter'`). */
26
+ Scatter: "scatter",
27
+ /** Funnel chart (via the `chartjs-chart-funnel` plugin, type `'funnel'`). */
28
+ Funnel: "funnel"
29
+ };
30
+ var ChartLegendPosition = {
31
+ Top: "top",
32
+ Bottom: "bottom",
33
+ Left: "left",
34
+ Right: "right",
35
+ /** Legend is not rendered. */
36
+ Hidden: "hidden"
37
+ };
38
+ var ChartIndexAxis = {
39
+ X: "x",
40
+ Y: "y"
41
+ };
42
+ var ChartOrientation = {
43
+ Vertical: "vertical",
44
+ Horizontal: "horizontal"
45
+ };
46
+ function toIndexAxis(orientation) {
47
+ return orientation === ChartOrientation.Horizontal ? ChartIndexAxis.Y : ChartIndexAxis.X;
48
+ }
49
+ var registered = false;
50
+ function ensureRegistered() {
51
+ if (registered) return;
52
+ chart_js.Chart.register(...chart_js.registerables, chartjsChartFunnel.FunnelController, chartjsChartFunnel.TrapezoidElement);
53
+ registered = true;
54
+ }
55
+ var RADIAL_TYPES = /* @__PURE__ */ new Set(["pie", "doughnut", "polararea", "radar", "funnel"]);
56
+ function trimmed(value) {
57
+ if (value === null || value === void 0) return void 0;
58
+ const t = value.trim();
59
+ return t.length > 0 ? t : void 0;
60
+ }
61
+ function formatValue(raw, prefix, suffix) {
62
+ const num = typeof raw === "number" ? raw : Number(raw);
63
+ const formatted = Number.isFinite(num) ? num.toLocaleString() : String(raw);
64
+ return `${prefix ?? ""}${formatted}${suffix ?? ""}`;
65
+ }
66
+ function buildChartOptions(type, theme, onClick) {
67
+ const text = trimmed(theme?.textColor);
68
+ const grid = trimmed(theme?.gridColor);
69
+ const yPrefix = trimmed(theme?.yAxisPrefix);
70
+ const ySuffix = trimmed(theme?.yAxisSuffix);
71
+ const xPrefix = trimmed(theme?.xAxisPrefix);
72
+ const xSuffix = trimmed(theme?.xAxisSuffix);
73
+ const hasYFormat = yPrefix !== void 0 || ySuffix !== void 0;
74
+ const hasXFormat = xPrefix !== void 0 || xSuffix !== void 0;
75
+ const animationEnabled = theme?.enableAnimation !== false;
76
+ const titleText = trimmed(theme?.title);
77
+ const legendPos = trimmed(theme?.legendPosition);
78
+ const stacked = theme?.stacked === true;
79
+ const indexAxis = trimmed(theme?.indexAxis) ?? (type.toLowerCase() === "funnel" ? "y" : void 0);
80
+ const options = {
81
+ responsive: true,
82
+ maintainAspectRatio: false,
83
+ ...indexAxis ? { indexAxis } : {}
84
+ };
85
+ options.plugins = options.plugins ?? {};
86
+ if (legendPos === "hidden") {
87
+ options.plugins.legend = { display: false };
88
+ } else {
89
+ options.plugins.legend = {
90
+ ...legendPos ? { position: legendPos } : {},
91
+ ...text ? { labels: { color: text } } : {}
92
+ };
93
+ }
94
+ if (titleText) {
95
+ options.plugins.title = {
96
+ display: true,
97
+ text: titleText,
98
+ ...text ? { color: text } : {}
99
+ };
100
+ } else if (text) {
101
+ options.plugins.title = { color: text };
102
+ }
103
+ if (hasYFormat) {
104
+ options.plugins.tooltip = {
105
+ callbacks: {
106
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
107
+ label: (ctx) => {
108
+ const label = ctx.dataset.label ? `${ctx.dataset.label}: ` : "";
109
+ return `${label}${formatValue(ctx.parsed.y ?? ctx.parsed ?? 0, yPrefix, ySuffix)}`;
110
+ }
111
+ }
112
+ };
113
+ }
114
+ if (text) {
115
+ options.color = text;
116
+ }
117
+ if (!animationEnabled) {
118
+ options.animation = false;
119
+ }
120
+ const isRadial = RADIAL_TYPES.has(type.toLowerCase());
121
+ if (!isRadial) {
122
+ const x = {};
123
+ const y = {};
124
+ if (text) {
125
+ x.ticks = { color: text };
126
+ y.ticks = { color: text };
127
+ }
128
+ if (grid) {
129
+ x.grid = { color: grid };
130
+ x.border = { color: grid };
131
+ y.grid = { color: grid };
132
+ y.border = { color: grid };
133
+ }
134
+ if (hasYFormat) {
135
+ y.ticks = y.ticks ?? {};
136
+ y.ticks.callback = function(value) {
137
+ return formatValue(value, yPrefix, ySuffix);
138
+ };
139
+ }
140
+ if (hasXFormat) {
141
+ x.ticks = x.ticks ?? {};
142
+ x.ticks.callback = function(_value, index) {
143
+ const labels = this.chart?.data?.labels;
144
+ const raw = labels?.[index] ?? _value;
145
+ return formatValue(raw, xPrefix, xSuffix);
146
+ };
147
+ }
148
+ if (stacked) {
149
+ x.stacked = true;
150
+ y.stacked = true;
151
+ }
152
+ options.scales = { x, y };
153
+ }
154
+ if (onClick) {
155
+ options.onClick = (_event, elements, chart) => {
156
+ if (!elements || elements.length === 0) return;
157
+ const first = elements[0];
158
+ const datasetIndex = first.datasetIndex;
159
+ const dataIndex = first.index;
160
+ const dataset = chart.data.datasets[datasetIndex];
161
+ const labels = chart.data.labels;
162
+ const rawValue = dataset?.data?.[dataIndex];
163
+ const value = typeof rawValue === "number" ? rawValue : rawValue && typeof rawValue === "object" && "y" in rawValue ? Number(rawValue.y) : Number(rawValue ?? 0);
164
+ onClick({
165
+ datasetIndex,
166
+ dataIndex,
167
+ label: labels?.[dataIndex] != null ? String(labels[dataIndex]) : "",
168
+ value: Number.isFinite(value) ? value : 0,
169
+ datasetLabel: typeof dataset?.label === "string" ? dataset.label : ""
170
+ });
171
+ };
172
+ }
173
+ return options;
174
+ }
175
+ function projectDatasets(_type, datasets) {
176
+ return datasets.map((d) => {
177
+ const points = d.data ?? [];
178
+ const flatData = points.map((p) => p.value);
179
+ const hasBg = points.some((p) => p.backgroundColor !== void 0);
180
+ const hasBorder = points.some((p) => p.borderColor !== void 0);
181
+ const bgColors = hasBg ? points.map((p) => p.backgroundColor ?? d.backgroundColor) : void 0;
182
+ const borderColors = hasBorder ? points.map((p) => p.borderColor ?? d.borderColor) : void 0;
183
+ return {
184
+ ...d,
185
+ data: flatData,
186
+ backgroundColor: bgColors ?? d.backgroundColor,
187
+ borderColor: borderColors ?? d.borderColor
188
+ };
189
+ });
190
+ }
191
+ function updateChartDatasets(chart, type, datasets) {
192
+ chart.data.datasets = projectDatasets(type, datasets);
193
+ chart.update("none");
194
+ }
195
+ function createChart(canvas, type, labels, datasets, theme, onClick) {
196
+ ensureRegistered();
197
+ const lingering = chart_js.Chart.getChart(canvas);
198
+ if (lingering) {
199
+ lingering.destroy();
200
+ }
201
+ const config = {
202
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
203
+ type,
204
+ data: {
205
+ labels,
206
+ datasets: projectDatasets(type, datasets)
207
+ },
208
+ options: buildChartOptions(type, theme, onClick)
209
+ };
210
+ return new chart_js.Chart(canvas, config);
211
+ }
212
+ function destroyChart(chart) {
213
+ if (chart) {
214
+ chart.destroy();
215
+ }
216
+ }
217
+ function exportChartAsImage(chart) {
218
+ if (!chart) return null;
219
+ return chart.toBase64Image("image/png", 1);
220
+ }
221
+ function getContainerStyle(width, height) {
222
+ return {
223
+ position: "relative",
224
+ minWidth: 0,
225
+ maxWidth: "100%",
226
+ width: width ?? "100%",
227
+ ...height !== void 0 ? { height } : {}
228
+ };
229
+ }
230
+ function ChartImpl(props, ref) {
231
+ const {
232
+ type = ChartType.Bar,
233
+ labels,
234
+ datasets,
235
+ width,
236
+ height = "400px",
237
+ theme,
238
+ onElementClick
239
+ } = props;
240
+ const canvasRef = react.useRef(null);
241
+ const chartRef = react.useRef(null);
242
+ const latestPropsRef = react.useRef({ datasets, onElementClick });
243
+ latestPropsRef.current = { datasets, onElementClick };
244
+ const handleClick = react.useCallback((payload) => {
245
+ const { datasets: currentDatasets, onElementClick: currentHandler } = latestPropsRef.current;
246
+ if (!currentHandler) return;
247
+ const dataset = payload.datasetIndex >= 0 && payload.datasetIndex < currentDatasets.length ? currentDatasets[payload.datasetIndex] : void 0;
248
+ const points = dataset?.data ?? [];
249
+ const dataPoint = payload.dataIndex >= 0 && payload.dataIndex < points.length ? points[payload.dataIndex] : void 0;
250
+ currentHandler({
251
+ datasetIndex: payload.datasetIndex,
252
+ dataIndex: payload.dataIndex,
253
+ label: payload.label,
254
+ value: payload.value,
255
+ datasetLabel: payload.datasetLabel,
256
+ ...dataPoint !== void 0 ? { dataPoint } : {}
257
+ });
258
+ }, []);
259
+ const lastDrawn = react.useRef(null);
260
+ react.useEffect(() => {
261
+ const canvas = canvasRef.current;
262
+ if (!canvas) return;
263
+ const inputsChanged = !lastDrawn.current || lastDrawn.current.type !== type || lastDrawn.current.labels !== labels || lastDrawn.current.datasets !== datasets || lastDrawn.current.theme !== theme;
264
+ if (!inputsChanged && chartRef.current) {
265
+ return;
266
+ }
267
+ const chart = chartRef.current;
268
+ if (chart && lastDrawn.current && lastDrawn.current.type === type && lastDrawn.current.theme === theme && lastDrawn.current.labels === labels && isStyleOnlyDatasetChange(lastDrawn.current.datasets, datasets)) {
269
+ updateChartDatasets(chart, type, datasets);
270
+ lastDrawn.current = { type, labels, datasets, theme };
271
+ return;
272
+ }
273
+ destroyChart(chartRef.current);
274
+ chartRef.current = createChart(canvas, type, labels, datasets, theme, handleClick);
275
+ lastDrawn.current = { type, labels, datasets, theme };
276
+ }, [type, labels, datasets, theme, handleClick]);
277
+ react.useEffect(() => {
278
+ return () => {
279
+ destroyChart(chartRef.current);
280
+ chartRef.current = null;
281
+ };
282
+ }, []);
283
+ react.useImperativeHandle(
284
+ ref,
285
+ () => ({
286
+ exportAsImage: () => exportChartAsImage(chartRef.current)
287
+ }),
288
+ []
289
+ );
290
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ppp-chart", style: getContainerStyle(width, height), children: /* @__PURE__ */ jsxRuntime.jsx(
291
+ "canvas",
292
+ {
293
+ ref: canvasRef,
294
+ style: { display: "block", maxWidth: "100%", maxHeight: "100%" }
295
+ }
296
+ ) });
297
+ }
298
+ var Chart2 = react.forwardRef(ChartImpl);
299
+ function isStyleOnlyDatasetChange(prev, next) {
300
+ if (prev.length !== next.length) return false;
301
+ for (let i = 0; i < prev.length; i++) {
302
+ const p = prev[i];
303
+ const n = next[i];
304
+ if (p.label !== n.label) return false;
305
+ const pData = p.data ?? [];
306
+ const nData = n.data ?? [];
307
+ if (pData.length !== nData.length) return false;
308
+ for (let j = 0; j < pData.length; j++) {
309
+ const pv = pData[j].value;
310
+ const nv = nData[j].value;
311
+ if (pv !== nv) return false;
312
+ }
313
+ }
314
+ return true;
315
+ }
316
+
317
+ // src/dataverse-chart-data-source.ts
318
+ var DataverseChartDataSource = class {
319
+ fetchXml;
320
+ labelColumn = "";
321
+ valueColumn = "";
322
+ seriesColumn;
323
+ singleSeriesLabel = "";
324
+ constructor(options = {}) {
325
+ Object.assign(this, options);
326
+ }
327
+ /**
328
+ * Builds a {@link ChartDataRequest} from this instance's properties
329
+ * and POSTs it via `client.loadChartAsync`. Subclasses inject the
330
+ * input mode via {@link configureRequest}.
331
+ */
332
+ async loadAsync(client, signal) {
333
+ const request = {
334
+ labelColumn: this.labelColumn,
335
+ valueColumn: this.valueColumn
336
+ };
337
+ if (this.seriesColumn !== void 0) request.seriesColumn = this.seriesColumn;
338
+ if (this.singleSeriesLabel) request.singleSeriesLabel = this.singleSeriesLabel;
339
+ this.configureRequest(request);
340
+ const response = await client.loadChartAsync(request, signal);
341
+ return response.data ?? { labels: [], datasets: [] };
342
+ }
343
+ /**
344
+ * Sets the input mode (FetchXml / ViewId / Aggregate) on the request.
345
+ * Base implementation sets `fetchXml`; subclasses override to use
346
+ * ViewId or Aggregate config instead.
347
+ */
348
+ configureRequest(request) {
349
+ if (this.fetchXml !== void 0) request.fetchXml = this.fetchXml;
350
+ }
351
+ };
352
+
353
+ // src/view-dataverse-chart-data-source.ts
354
+ var ViewDataverseChartDataSource = class extends DataverseChartDataSource {
355
+ viewId;
356
+ constructor(options = {}) {
357
+ super(options);
358
+ if (options.viewId !== void 0) {
359
+ this.viewId = options.viewId;
360
+ }
361
+ }
362
+ configureRequest(request) {
363
+ if (!this.viewId) {
364
+ throw new Error('"viewId" must be set before calling loadAsync.');
365
+ }
366
+ request.viewId = this.viewId;
367
+ }
368
+ };
369
+ var AggregateDataverseChartDataSource = class extends DataverseChartDataSource {
370
+ tableName;
371
+ groupByDateGrouping = core.ChartDateGrouping.None;
372
+ groupByLinkedEntity;
373
+ seriesDateGrouping = core.ChartDateGrouping.None;
374
+ seriesLinkedEntity;
375
+ aggregate = core.AggregateType.Sum;
376
+ viewId;
377
+ filterXml;
378
+ top;
379
+ constructor(options) {
380
+ super(options);
381
+ this.tableName = options.tableName;
382
+ this.labelColumn = options.groupByColumn;
383
+ this.valueColumn = options.aggregateColumn;
384
+ if (options.groupByDateGrouping !== void 0) this.groupByDateGrouping = options.groupByDateGrouping;
385
+ if (options.groupByLinkedEntity !== void 0) this.groupByLinkedEntity = options.groupByLinkedEntity;
386
+ if (options.seriesDateGrouping !== void 0) this.seriesDateGrouping = options.seriesDateGrouping;
387
+ if (options.seriesLinkedEntity !== void 0) this.seriesLinkedEntity = options.seriesLinkedEntity;
388
+ if (options.aggregate !== void 0) this.aggregate = options.aggregate;
389
+ if (options.viewId !== void 0) this.viewId = options.viewId;
390
+ if (options.filterXml !== void 0) this.filterXml = options.filterXml;
391
+ if (options.top !== void 0) this.top = options.top;
392
+ }
393
+ /** Caller-friendly alias for `labelColumn` (kept in sync via getter/setter). */
394
+ get groupByColumn() {
395
+ return this.labelColumn;
396
+ }
397
+ set groupByColumn(value) {
398
+ this.labelColumn = value;
399
+ }
400
+ /** Caller-friendly alias for `valueColumn`. */
401
+ get aggregateColumn() {
402
+ return this.valueColumn;
403
+ }
404
+ set aggregateColumn(value) {
405
+ this.valueColumn = value;
406
+ }
407
+ configureRequest(request) {
408
+ const groupBy = {
409
+ column: this.groupByColumn,
410
+ dateGrouping: this.groupByDateGrouping
411
+ };
412
+ if (this.groupByLinkedEntity) groupBy.linkedEntity = this.groupByLinkedEntity;
413
+ let series;
414
+ if (this.seriesColumn !== void 0 || this.seriesDateGrouping !== core.ChartDateGrouping.None || this.seriesLinkedEntity !== void 0) {
415
+ series = {
416
+ column: this.seriesColumn ?? "",
417
+ dateGrouping: this.seriesDateGrouping
418
+ };
419
+ if (this.seriesLinkedEntity) series.linkedEntity = this.seriesLinkedEntity;
420
+ }
421
+ const config = {
422
+ tableName: this.tableName,
423
+ groupBy,
424
+ aggregateColumn: this.aggregateColumn,
425
+ aggregate: this.aggregate
426
+ };
427
+ if (series !== void 0) config.series = series;
428
+ if (this.viewId !== void 0) config.viewFilterId = this.viewId;
429
+ if (this.filterXml !== void 0) config.filterXml = this.filterXml;
430
+ if (this.top !== void 0) config.top = this.top;
431
+ request.aggregate = config;
432
+ }
433
+ };
434
+
435
+ // src/index.ts
436
+ var VERSION = "0.1.0";
437
+
438
+ Object.defineProperty(exports, "AggregateType", {
439
+ enumerable: true,
440
+ get: function () { return core.AggregateType; }
441
+ });
442
+ Object.defineProperty(exports, "ChartDateGrouping", {
443
+ enumerable: true,
444
+ get: function () { return core.ChartDateGrouping; }
445
+ });
446
+ Object.defineProperty(exports, "JoinOperator", {
447
+ enumerable: true,
448
+ get: function () { return core.JoinOperator; }
449
+ });
450
+ exports.AggregateDataverseChartDataSource = AggregateDataverseChartDataSource;
451
+ exports.Chart = Chart2;
452
+ exports.ChartIndexAxis = ChartIndexAxis;
453
+ exports.ChartLegendPosition = ChartLegendPosition;
454
+ exports.ChartOrientation = ChartOrientation;
455
+ exports.ChartType = ChartType;
456
+ exports.DataverseChartDataSource = DataverseChartDataSource;
457
+ exports.VERSION = VERSION;
458
+ exports.ViewDataverseChartDataSource = ViewDataverseChartDataSource;
459
+ exports.toIndexAxis = toIndexAxis;
460
+ //# sourceMappingURL=index.cjs.map
461
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts","../src/internal/chart-driver.ts","../src/chart.tsx","../src/dataverse-chart-data-source.ts","../src/view-dataverse-chart-data-source.ts","../src/aggregate-dataverse-chart-data-source.ts","../src/index.ts"],"names":["Chart","registerables","FunnelController","TrapezoidElement","useRef","useCallback","useEffect","useImperativeHandle","jsx","forwardRef","ChartDateGrouping","AggregateType"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\r\n * Display-only type / enum definitions for the React Charts wrapper.\r\n * Wire-shape types (ChartData, ChartDataset, DataPoint, DataverseDataPoint,\r\n * ChartLinkedEntity, ChartDateGrouping, AggregateType, JoinOperator) are\r\n * generated from OpenAPI and re-exported from `@powerportalspro/core` —\r\n * this file holds only the Chart.js-presentation enums that don't cross\r\n * the wire.\r\n */\r\n\r\n// Re-export the generated wire shapes through this module so consumers\r\n// can `import { ChartData, ... } from '@powerportalspro/react-charts'`\r\n// without spelunking into core. Keeps the chart-package surface\r\n// self-contained for documentation / discoverability.\r\n//\r\n// `ChartDataset` is intentionally NOT re-exported from core directly — we\r\n// override it below with a generic wrapper so consumers can carry a\r\n// custom DataPoint subtype through the dataset / click-handler chain\r\n// without `as unknown as DataPoint` casts. (Mirrors Blazor's\r\n// `class MyDataPoint : DataPoint { ... }` pattern, which works because\r\n// C# polymorphism + Chart.js's pass-through behavior gives you the\r\n// extra fields back in the click handler for free.)\r\nexport {\r\n AggregateType,\r\n ChartDateGrouping,\r\n JoinOperator,\r\n type AggregateChartConfig,\r\n type BaseDataPoint,\r\n type ChartData,\r\n type ChartLinkedEntity,\r\n type DataPoint,\r\n type DataverseDataPoint,\r\n type ChartDataRequest,\r\n type ChartDataResponse,\r\n} from '@powerportalspro/core';\r\n\r\nimport type {\r\n ChartDataset as CoreChartDataset,\r\n DataPoint,\r\n} from '@powerportalspro/core';\r\n\r\n/**\r\n * Dataset of chart data points. Generic over `TPoint` so consumers can\r\n * carry a custom DataPoint subtype through to the click handler.\r\n *\r\n * Defaults to the built-in {@link DataPoint} union — when the consumer\r\n * doesn't specialize the generic, calls look exactly like before\r\n * (no `<…>` required). When they DO want richer click context, they\r\n * extend {@link BaseDataPoint} and parameterize:\r\n *\r\n * ```ts\r\n * interface SalesDataPoint extends BaseDataPoint {\r\n * region: string;\r\n * orderCount: number;\r\n * }\r\n *\r\n * const datasets: ChartDataset<SalesDataPoint>[] = [{\r\n * label: 'Sales',\r\n * data: [{ value: 65, region: 'North', orderCount: 12 }],\r\n * }];\r\n *\r\n * <FluentUIChart<SalesDataPoint>\r\n * datasets={datasets}\r\n * onElementClick={(a) => a.dataPoint?.region}\r\n * />\r\n * ```\r\n *\r\n * Mirrors Blazor's `List<DataPoint>` polymorphism — the underlying\r\n * Chart.js layer accepts arbitrary object fields on each data point and\r\n * passes them straight through to the click handler.\r\n */\r\nexport type ChartDataset<TPoint = DataPoint> = Omit<CoreChartDataset, 'data'> & {\r\n data?: TPoint[];\r\n};\r\n\r\n// ---------------------------------------------------------------------------\r\n// ChartType — projects directly to Chart.js's `type` string. Display-only:\r\n// the server-side `IChartService` doesn't know or care which chart\r\n// type the consumer renders against the returned data.\r\n// ---------------------------------------------------------------------------\r\n\r\n/** Chart types supported by the underlying Chart.js library. */\r\nexport const ChartType = {\r\n /** Bar chart (`'bar'`). */\r\n Bar: 'bar',\r\n /** Line chart (`'line'`). */\r\n Line: 'line',\r\n /** Pie chart (`'pie'`). */\r\n Pie: 'pie',\r\n /** Doughnut chart (`'doughnut'`). */\r\n Doughnut: 'doughnut',\r\n /** Polar area chart (`'polarArea'`). */\r\n PolarArea: 'polarArea',\r\n /** Radar chart (`'radar'`). */\r\n Radar: 'radar',\r\n /** Bubble chart (`'bubble'`). */\r\n Bubble: 'bubble',\r\n /** Scatter chart (`'scatter'`). */\r\n Scatter: 'scatter',\r\n /** Funnel chart (via the `chartjs-chart-funnel` plugin, type `'funnel'`). */\r\n Funnel: 'funnel',\r\n} as const;\r\nexport type ChartType = (typeof ChartType)[keyof typeof ChartType];\r\n\r\n// ---------------------------------------------------------------------------\r\n// ChartLegendPosition — projects to Chart.js's `plugins.legend.position`.\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Placement of the chart legend, or `Hidden` to suppress it entirely. Maps\r\n * directly to Chart.js's lowercase legend position string.\r\n */\r\nexport const ChartLegendPosition = {\r\n Top: 'top',\r\n Bottom: 'bottom',\r\n Left: 'left',\r\n Right: 'right',\r\n /** Legend is not rendered. */\r\n Hidden: 'hidden',\r\n} as const;\r\nexport type ChartLegendPosition = (typeof ChartLegendPosition)[keyof typeof ChartLegendPosition];\r\n\r\n// ---------------------------------------------------------------------------\r\n// ChartIndexAxis — Chart.js's `indexAxis` value, projected through orientation.\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Chart.js `indexAxis` value — `'x'` for vertical bar / line charts and\r\n * `'y'` for horizontal bars / funnels. Most consumers should set\r\n * {@link ChartOrientation} on a component instead of touching this directly.\r\n */\r\nexport const ChartIndexAxis = {\r\n X: 'x',\r\n Y: 'y',\r\n} as const;\r\nexport type ChartIndexAxis = (typeof ChartIndexAxis)[keyof typeof ChartIndexAxis];\r\n\r\n// ---------------------------------------------------------------------------\r\n// ChartOrientation — caller-friendly vertical/horizontal label.\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Caller-facing chart orientation. Maps to the underlying Chart.js\r\n * `indexAxis` via {@link toIndexAxis}: `Vertical` → `'x'`, `Horizontal` →\r\n * `'y'`. Funnel charts default to `Horizontal` for a top-down funnel.\r\n */\r\nexport const ChartOrientation = {\r\n Vertical: 'vertical',\r\n Horizontal: 'horizontal',\r\n} as const;\r\nexport type ChartOrientation = (typeof ChartOrientation)[keyof typeof ChartOrientation];\r\n\r\n/** Maps a {@link ChartOrientation} to the Chart.js {@link ChartIndexAxis}. */\r\nexport function toIndexAxis(orientation: ChartOrientation): ChartIndexAxis {\r\n return orientation === ChartOrientation.Horizontal ? ChartIndexAxis.Y : ChartIndexAxis.X;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// ChartTheme — display-only theming forwarded to Chart.js.\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Theme and formatting overrides forwarded to Chart.js. Fields left\r\n * `undefined` are not applied — the underlying Chart.js defaults stand.\r\n */\r\nexport interface ChartTheme {\r\n /** Color for legend labels, axis ticks, and the title. */\r\n textColor?: string;\r\n /** Color for axis gridlines and borders. */\r\n gridColor?: string;\r\n /** Prefix prepended to every Y-axis tick label and tooltip value (e.g. `\"$\"`). */\r\n yAxisPrefix?: string;\r\n /** Suffix appended to every Y-axis tick label and tooltip value (e.g. `\"%\"`). */\r\n yAxisSuffix?: string;\r\n /** Prefix prepended to every X-axis tick label (e.g. `\"Q\"`). */\r\n xAxisPrefix?: string;\r\n /** Suffix appended to every X-axis tick label. */\r\n xAxisSuffix?: string;\r\n /** Chart title text displayed above the chart. */\r\n title?: string;\r\n /**\r\n * Stack datasets on top of each other instead of side-by-side. Applies to\r\n * bar and line charts.\r\n */\r\n stacked?: boolean;\r\n /** Legend placement or `Hidden`. When omitted, Chart.js's default is used. */\r\n legendPosition?: ChartLegendPosition;\r\n /** Whether chart animations are enabled. Defaults to `true`. */\r\n enableAnimation?: boolean;\r\n /**\r\n * Category-axis direction — usually set indirectly via the caller-facing\r\n * {@link ChartOrientation}. Funnel charts default to `'y'`.\r\n */\r\n indexAxis?: ChartIndexAxis;\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// ChartClickEventArgs — payload delivered to `onElementClick`.\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Payload describing a single click on a rendered chart element. Generic\r\n * over `TPoint` so consumers using a custom DataPoint subtype see their\r\n * extra fields on `dataPoint` without a cast.\r\n */\r\nexport interface ChartClickEventArgs<TPoint = DataPoint> {\r\n /** Zero-based dataset index that contains the clicked element. */\r\n datasetIndex: number;\r\n /** Zero-based point index within its dataset. */\r\n dataIndex: number;\r\n /** Category label of the clicked element (e.g. `\"January\"`) or empty. */\r\n label: string;\r\n /** Numeric value of the clicked data point. */\r\n value: number;\r\n /** Label of the dataset the clicked element belongs to. */\r\n datasetLabel: string;\r\n /**\r\n * The original data-point object backing the clicked element. Typed as\r\n * the dataset's `TPoint` so a `ChartDataset<MyPoint>` flows through to\r\n * `MyPoint` here. With the default {@link DataPoint} union, discriminate\r\n * via the `$type` field or `'record' in dataPoint` to recover\r\n * DataverseDataPoint context. `undefined` only when the indices from\r\n * Chart.js no longer line up with the current datasets (rare race\r\n * during a data swap).\r\n */\r\n dataPoint?: TPoint;\r\n}\r\n","/**\r\n * Chart.js driver — pure-function port of `Chart.razor.ts`. Builds the\r\n * Chart.js options object from a {@link ChartTheme}, creates a Chart.js\r\n * instance against a canvas, and exports/destroys it on demand. The\r\n * `<Chart>` React component owns the Chart instance via a ref, so there's\r\n * no global registry the way the Blazor module needs one — each component\r\n * is self-contained.\r\n *\r\n * Kept as a separate module so consumers (custom chart wrappers, the\r\n * eventual FluentUIChart) can reuse `buildChartOptions` / `RADIAL_TYPES`\r\n * without re-deriving them.\r\n */\r\n\r\nimport { Chart, registerables, type ChartConfiguration } from 'chart.js';\r\nimport { FunnelController, TrapezoidElement } from 'chartjs-chart-funnel';\r\nimport {\r\n ChartType,\r\n type ChartDataset,\r\n type ChartTheme,\r\n type DataPoint,\r\n} from '../types.js';\r\n\r\n// One-time registration. Chart.js requires explicit controller/element\r\n// registration since v3, and the funnel plugin ships its own controller +\r\n// element. Calling Chart.register twice is a no-op so we can do it at\r\n// module load.\r\nlet registered = false;\r\nfunction ensureRegistered(): void {\r\n if (registered) return;\r\n Chart.register(...registerables, FunnelController, TrapezoidElement);\r\n registered = true;\r\n}\r\n\r\n/**\r\n * Radial chart types — share the property that per-point colors apply to\r\n * individual segments. Cartesian types take one color per dataset instead.\r\n * Normalized to lowercase so caller-supplied values (e.g. `'PolarArea'`)\r\n * still match after the `.toLowerCase()` lookup in the driver.\r\n */\r\nexport const RADIAL_TYPES = new Set<string>(['pie', 'doughnut', 'polararea', 'radar', 'funnel']);\r\n\r\nfunction trimmed(value: string | null | undefined): string | undefined {\r\n if (value === null || value === undefined) return undefined;\r\n const t = value.trim();\r\n return t.length > 0 ? t : undefined;\r\n}\r\n\r\nfunction formatValue(raw: number | string, prefix?: string, suffix?: string): string {\r\n const num = typeof raw === 'number' ? raw : Number(raw);\r\n const formatted = Number.isFinite(num) ? num.toLocaleString() : String(raw);\r\n return `${prefix ?? ''}${formatted}${suffix ?? ''}`;\r\n}\r\n\r\n/** Callback signature invoked when the user clicks on a rendered element. */\r\nexport interface ChartClickPayload {\r\n datasetIndex: number;\r\n dataIndex: number;\r\n label: string;\r\n value: number;\r\n datasetLabel: string;\r\n}\r\n\r\nexport type ChartClickHandler = (payload: ChartClickPayload) => void;\r\n\r\n/**\r\n * Builds the Chart.js options object from a {@link ChartTheme}. Pure — no\r\n * side effects, can be called any number of times. Matches the\r\n * `buildOptions` function in the Blazor `Chart.razor.ts` line-for-line so\r\n * behavior is identical across the two layers.\r\n */\r\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\r\nexport function buildChartOptions(\r\n type: string,\r\n theme?: ChartTheme | null,\r\n onClick?: ChartClickHandler | null,\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n): any {\r\n const text = trimmed(theme?.textColor);\r\n const grid = trimmed(theme?.gridColor);\r\n const yPrefix = trimmed(theme?.yAxisPrefix);\r\n const ySuffix = trimmed(theme?.yAxisSuffix);\r\n const xPrefix = trimmed(theme?.xAxisPrefix);\r\n const xSuffix = trimmed(theme?.xAxisSuffix);\r\n const hasYFormat = yPrefix !== undefined || ySuffix !== undefined;\r\n const hasXFormat = xPrefix !== undefined || xSuffix !== undefined;\r\n const animationEnabled = theme?.enableAnimation !== false;\r\n\r\n const titleText = trimmed(theme?.title);\r\n const legendPos = trimmed(theme?.legendPosition);\r\n const stacked = theme?.stacked === true;\r\n\r\n // Funnel charts default to `'y'` for the conventional top-down funnel\r\n // look; everything else falls through to Chart.js's per-type default\r\n // unless the caller set `indexAxis` explicitly.\r\n const indexAxis =\r\n trimmed(theme?.indexAxis) ?? (type.toLowerCase() === 'funnel' ? 'y' : undefined);\r\n\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const options: any = {\r\n responsive: true,\r\n maintainAspectRatio: false,\r\n ...(indexAxis ? { indexAxis } : {}),\r\n };\r\n\r\n options.plugins = options.plugins ?? {};\r\n\r\n // Legend\r\n if (legendPos === 'hidden') {\r\n options.plugins.legend = { display: false };\r\n } else {\r\n options.plugins.legend = {\r\n ...(legendPos ? { position: legendPos } : {}),\r\n ...(text ? { labels: { color: text } } : {}),\r\n };\r\n }\r\n\r\n // Title\r\n if (titleText) {\r\n options.plugins.title = {\r\n display: true,\r\n text: titleText,\r\n ...(text ? { color: text } : {}),\r\n };\r\n } else if (text) {\r\n options.plugins.title = { color: text };\r\n }\r\n\r\n // Tooltip value formatting — applies the Y-axis prefix/suffix so e.g.\r\n // currency charts get `\"$1,234\"` in the hover label, not bare numbers.\r\n if (hasYFormat) {\r\n options.plugins.tooltip = {\r\n callbacks: {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n label: (ctx: any) => {\r\n const label = ctx.dataset.label ? `${ctx.dataset.label}: ` : '';\r\n return `${label}${formatValue(ctx.parsed.y ?? ctx.parsed ?? 0, yPrefix, ySuffix)}`;\r\n },\r\n },\r\n };\r\n }\r\n\r\n if (text) {\r\n options.color = text;\r\n }\r\n\r\n if (!animationEnabled) {\r\n options.animation = false;\r\n }\r\n\r\n // Scales — cartesian charts only. Radial (pie/doughnut/polarArea/radar/\r\n // funnel) reject a `scales` block.\r\n const isRadial = RADIAL_TYPES.has(type.toLowerCase());\r\n if (!isRadial) {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const x: any = {};\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const y: any = {};\r\n\r\n if (text) {\r\n x.ticks = { color: text };\r\n y.ticks = { color: text };\r\n }\r\n\r\n if (grid) {\r\n x.grid = { color: grid };\r\n x.border = { color: grid };\r\n y.grid = { color: grid };\r\n y.border = { color: grid };\r\n }\r\n\r\n if (hasYFormat) {\r\n y.ticks = y.ticks ?? {};\r\n y.ticks.callback = function (this: unknown, value: number) {\r\n return formatValue(value, yPrefix, ySuffix);\r\n };\r\n }\r\n\r\n if (hasXFormat) {\r\n x.ticks = x.ticks ?? {};\r\n x.ticks.callback = function (this: unknown, _value: number, index: number) {\r\n // Categorical X axis: render the label string for this index,\r\n // re-formatted with the prefix/suffix. `value` is the tick offset\r\n // (not the label), so we re-read the label from chart.data.\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n const labels = (this as any).chart?.data?.labels;\r\n const raw = labels?.[index] ?? _value;\r\n return formatValue(raw, xPrefix, xSuffix);\r\n };\r\n }\r\n\r\n if (stacked) {\r\n x.stacked = true;\r\n y.stacked = true;\r\n }\r\n\r\n options.scales = { x, y };\r\n }\r\n\r\n if (onClick) {\r\n // Chart.js fires onClick on every click inside the canvas; `elements`\r\n // is empty when the click landed on the chart background.\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n options.onClick = (_event: unknown, elements: any[], chart: any) => {\r\n if (!elements || elements.length === 0) return;\r\n\r\n const first = elements[0];\r\n const datasetIndex = first.datasetIndex as number;\r\n const dataIndex = first.index as number;\r\n const dataset = chart.data.datasets[datasetIndex];\r\n const labels = chart.data.labels as Array<string | number> | undefined;\r\n\r\n const rawValue = dataset?.data?.[dataIndex];\r\n const value =\r\n typeof rawValue === 'number'\r\n ? rawValue\r\n : rawValue && typeof rawValue === 'object' && 'y' in rawValue\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n ? Number((rawValue as any).y)\r\n : Number(rawValue ?? 0);\r\n\r\n onClick({\r\n datasetIndex,\r\n dataIndex,\r\n label: labels?.[dataIndex] != null ? String(labels[dataIndex]) : '',\r\n value: Number.isFinite(value) ? value : 0,\r\n datasetLabel: typeof dataset?.label === 'string' ? dataset.label : '',\r\n });\r\n };\r\n }\r\n\r\n return options;\r\n}\r\n\r\n/**\r\n * Projects an array of {@link ChartDataset}s into the shape Chart.js\r\n * expects (`data` becomes `number[]`, per-point colors are split out\r\n * into `backgroundColor`/`borderColor` arrays). Per-point colors are\r\n * supported across cartesian and radial chart types alike — Chart.js\r\n * accepts an array on `dataset.backgroundColor` for bar / line / etc.\r\n * the same way it does for pie / doughnut. Required for the chart\r\n * cross-filter highlight UX, which fades non-matching bars by\r\n * overriding their per-point `backgroundColor`.\r\n */\r\nfunction projectDatasets(\r\n _type: ChartType,\r\n datasets: ChartDataset[],\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n): any[] {\r\n return datasets.map((d) => {\r\n // The generated schema marks `data` as optional (mirrors C#'s\r\n // List<DataPoint> Data { get; set; } = new() — never null in\r\n // practice, but the OpenAPI emitter typed it nullable). Default to\r\n // empty so projection doesn't crash on a missing array.\r\n const points: DataPoint[] = d.data ?? [];\r\n const flatData = points.map((p) => p.value);\r\n\r\n // Per-point colors: build a parallel array sized to `points`,\r\n // filling each slot with the per-point override OR the dataset's\r\n // fallback. Chart.js bar/line/pie/etc. all read the array shape.\r\n // Only swap in the array when at least one point sets an explicit\r\n // override (no override = pass through the dataset's\r\n // backgroundColor / leave undefined for the palette to fill in).\r\n const hasBg = points.some((p) => p.backgroundColor !== undefined);\r\n const hasBorder = points.some((p) => p.borderColor !== undefined);\r\n const bgColors = hasBg\r\n ? points.map((p) => p.backgroundColor ?? d.backgroundColor)\r\n : undefined;\r\n const borderColors = hasBorder\r\n ? points.map((p) => p.borderColor ?? d.borderColor)\r\n : undefined;\r\n\r\n return {\r\n ...d,\r\n data: flatData,\r\n backgroundColor: bgColors ?? d.backgroundColor,\r\n borderColor: borderColors ?? d.borderColor,\r\n };\r\n });\r\n}\r\n\r\n/**\r\n * In-place update of an existing Chart.js instance with new\r\n * {@link ChartDataset}s, skipping animation. Used by\r\n * {@link Chart} when it detects that only per-point styling\r\n * (typically backgroundColor for a cross-filter highlight) changed —\r\n * structural changes (chart type, label set, dataset count, data\r\n * values) still go through destroy + recreate.\r\n *\r\n * Skipping animation via `update('none')` is intentional: highlight\r\n * toggles are visual decoration that should snap into place; bar /\r\n * pie animations on every click would feel laggy.\r\n */\r\nexport function updateChartDatasets(\r\n chart: Chart,\r\n type: ChartType,\r\n datasets: ChartDataset[],\r\n): void {\r\n chart.data.datasets = projectDatasets(type, datasets);\r\n chart.update('none');\r\n}\r\n\r\n/**\r\n * Creates a new Chart.js instance against `canvas`. Destroys any prior\r\n * instance still attached to the same canvas first — protects against hot\r\n * reloads where our component-level cleanup didn't run.\r\n */\r\nexport function createChart(\r\n canvas: HTMLCanvasElement,\r\n type: ChartType,\r\n labels: string[],\r\n datasets: ChartDataset[],\r\n theme?: ChartTheme | null,\r\n onClick?: ChartClickHandler | null,\r\n): Chart {\r\n ensureRegistered();\r\n\r\n // Defensive: a previous instance attached to this canvas (lost ref,\r\n // double-mount during dev StrictMode, etc.) must be torn down before a\r\n // new Chart can claim it.\r\n const lingering = Chart.getChart(canvas);\r\n if (lingering) {\r\n lingering.destroy();\r\n }\r\n\r\n const config: ChartConfiguration = {\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n type: type as any,\r\n data: {\r\n labels,\r\n datasets: projectDatasets(type, datasets),\r\n },\r\n options: buildChartOptions(type, theme, onClick),\r\n };\r\n\r\n return new Chart(canvas, config);\r\n}\r\n\r\n/** Tears down a Chart.js instance. Safe to call repeatedly. */\r\nexport function destroyChart(chart: Chart | null | undefined): void {\r\n if (chart) {\r\n chart.destroy();\r\n }\r\n}\r\n\r\n/**\r\n * Exports the chart as a base64-encoded PNG data URI\r\n * (`\"data:image/png;base64,…\"`). Returns `null` when the chart hasn't been\r\n * created yet.\r\n */\r\nexport function exportChartAsImage(chart: Chart | null | undefined): string | null {\r\n if (!chart) return null;\r\n return chart.toBase64Image('image/png', 1);\r\n}\r\n","/**\r\n * Base `<Chart>` React component — direct Chart.js wrapper, mirrors the\r\n * Blazor `Chart.razor` API surface (Type, Labels, Datasets, Width, Height,\r\n * Theme, OnElementClick + `exportAsImage`).\r\n *\r\n * Owns the Chart.js instance via a canvas ref; React's render is just the\r\n * outer container + `<canvas>`. Chart lifecycle is driven by an effect that\r\n * reads the props snapshot. The component pre-empts re-renders triggered\r\n * by unrelated parent updates by reference-checking labels/datasets/theme\r\n * before destroying the underlying Chart.js instance — the same\r\n * \"destroy + recreate is expensive\" optimization the Blazor component\r\n * uses (each re-create restarts the entry animation).\r\n */\r\n\r\nimport type { Chart as ChartJsInstance } from 'chart.js';\r\nimport {\r\n forwardRef,\r\n useCallback,\r\n useEffect,\r\n useImperativeHandle,\r\n useRef,\r\n type CSSProperties,\r\n type ForwardedRef,\r\n type ReactElement,\r\n type RefAttributes,\r\n} from 'react';\r\nimport {\r\n createChart,\r\n destroyChart,\r\n exportChartAsImage,\r\n updateChartDatasets,\r\n type ChartClickPayload,\r\n} from './internal/chart-driver.js';\r\nimport {\r\n ChartType,\r\n type ChartClickEventArgs,\r\n type ChartDataset,\r\n type ChartTheme,\r\n type DataPoint,\r\n} from './types.js';\r\n\r\n/** Imperative handle exposed to callers via `ref` on `<Chart>`. */\r\nexport interface ChartHandle {\r\n /**\r\n * Exports the chart as a PNG data URI\r\n * (`\"data:image/png;base64,…\"`). Returns `null` when the chart hasn't\r\n * been rendered yet (typically the first paint).\r\n */\r\n exportAsImage(): string | null;\r\n}\r\n\r\n/**\r\n * Props accepted by `<Chart>`. Generic over `TPoint` — the type carried\r\n * through the dataset's `data` array and back out on `onElementClick`.\r\n * Defaults to {@link DataPoint}, so callers that don't extend the built-in\r\n * DataPoint shape never need to mention the generic.\r\n */\r\nexport interface ChartProps<TPoint = DataPoint> {\r\n /** Chart type. Defaults to `ChartType.Bar`. */\r\n type?: ChartType;\r\n /** Category labels rendered along the primary axis. */\r\n labels: string[];\r\n /** Datasets to plot. */\r\n datasets: ChartDataset<TPoint>[];\r\n /** Optional CSS width applied to the outer container. */\r\n width?: string;\r\n /** Optional CSS height applied to the outer container. Defaults to `'400px'`. */\r\n height?: string;\r\n /** Theme overrides forwarded to Chart.js. */\r\n theme?: ChartTheme;\r\n /** Fires when the user clicks on a rendered chart element. */\r\n onElementClick?: (args: ChartClickEventArgs<TPoint>) => void;\r\n}\r\n\r\n/**\r\n * Computes the inline style applied to the chart's outer container.\r\n *\r\n * `min-width: 0` lets the wrapper shrink inside flex/grid parents (which\r\n * default to `min-width: auto`, i.e. content-based). Without it, once\r\n * Chart.js sizes the canvas to e.g. 800px, the flex item stays pinned at\r\n * 800px and the chart overflows on shrink. `width: 100%` (applied when the\r\n * caller didn't pick one) decouples the wrapper's width from the canvas's\r\n * intrinsic size so Chart.js's `ResizeObserver` actually fires.\r\n */\r\nfunction getContainerStyle(width?: string, height?: string): CSSProperties {\r\n return {\r\n position: 'relative',\r\n minWidth: 0,\r\n maxWidth: '100%',\r\n width: width ?? '100%',\r\n ...(height !== undefined ? { height } : {}),\r\n };\r\n}\r\n\r\n// forwardRef erases generics on its inferred type — React's official\r\n// workaround is to cast the result back to a generic function signature.\r\n// The runtime value is unchanged (still the forwardRef component) but the\r\n// public type lets callers write `<Chart<MyPoint> ... />`.\r\nfunction ChartImpl<TPoint>(\r\n props: ChartProps<TPoint>,\r\n ref: ForwardedRef<ChartHandle>,\r\n): ReactElement | null {\r\n const {\r\n type = ChartType.Bar,\r\n labels,\r\n datasets,\r\n width,\r\n height = '400px',\r\n theme,\r\n onElementClick,\r\n } = props;\r\n\r\n const canvasRef = useRef<HTMLCanvasElement | null>(null);\r\n const chartRef = useRef<ChartJsInstance | null>(null);\r\n\r\n // Stable click handler box so we don't recreate the Chart.js instance on\r\n // every render just because the parent passed an inline arrow. The\r\n // handler reads from `latestPropsRef` so it always sees the current\r\n // datasets (matters for the IDataPoint enrichment below).\r\n const latestPropsRef = useRef({ datasets, onElementClick });\r\n latestPropsRef.current = { datasets, onElementClick };\r\n\r\n const handleClick = useCallback((payload: ChartClickPayload) => {\r\n const { datasets: currentDatasets, onElementClick: currentHandler } = latestPropsRef.current;\r\n if (!currentHandler) return;\r\n\r\n // Look up the original DataPoint instance from the dataset array, the\r\n // same way Blazor's `OnChartElementClickedAsync` does. Lets consumers\r\n // discriminate on `DataverseDataPoint.record` (or any custom subtype\r\n // they attached) without us serializing the entire object across a JS\r\n // boundary.\r\n const dataset =\r\n payload.datasetIndex >= 0 && payload.datasetIndex < currentDatasets.length\r\n ? currentDatasets[payload.datasetIndex]\r\n : undefined;\r\n const points = dataset?.data ?? [];\r\n const dataPoint =\r\n payload.dataIndex >= 0 && payload.dataIndex < points.length\r\n ? points[payload.dataIndex]\r\n : undefined;\r\n\r\n currentHandler({\r\n datasetIndex: payload.datasetIndex,\r\n dataIndex: payload.dataIndex,\r\n label: payload.label,\r\n value: payload.value,\r\n datasetLabel: payload.datasetLabel,\r\n ...(dataPoint !== undefined ? { dataPoint } : {}),\r\n });\r\n }, []);\r\n\r\n // Track the props snapshot we last drew with so unrelated re-renders\r\n // (e.g. parent state changes that don't touch chart inputs) don't tear\r\n // down the Chart.js instance — destroy+recreate restarts animations.\r\n const lastDrawn = useRef<{\r\n type: ChartType;\r\n labels: string[];\r\n datasets: ChartDataset<TPoint>[];\r\n theme: ChartTheme | undefined;\r\n } | null>(null);\r\n\r\n useEffect(() => {\r\n const canvas = canvasRef.current;\r\n if (!canvas) return;\r\n\r\n const inputsChanged =\r\n !lastDrawn.current ||\r\n lastDrawn.current.type !== type ||\r\n lastDrawn.current.labels !== labels ||\r\n lastDrawn.current.datasets !== datasets ||\r\n lastDrawn.current.theme !== theme;\r\n\r\n if (!inputsChanged && chartRef.current) {\r\n return;\r\n }\r\n\r\n // Cross-filter highlight optimization: when ONLY per-point styling\r\n // changed (same chart type, same labels reference, same dataset\r\n // shape + same numeric values — only colors/borders differ), update\r\n // in place via Chart.js's `chart.update('none')` instead of\r\n // destroying + recreating. The recreate path restarts the entry\r\n // animation, which is fine for a fresh fetch but distracting when\r\n // the user just toggled a chart slice's highlight on a sibling\r\n // grid. Shape check guards against false positives (a new data\r\n // value or a different bucket count needs a full recreate).\r\n const chart = chartRef.current;\r\n if (\r\n chart\r\n && lastDrawn.current\r\n && lastDrawn.current.type === type\r\n && lastDrawn.current.theme === theme\r\n && lastDrawn.current.labels === labels\r\n && isStyleOnlyDatasetChange(lastDrawn.current.datasets, datasets)\r\n ) {\r\n // Cast at the driver boundary: TPoint is structural sugar for the\r\n // CALLER; the Chart.js layer just sees arbitrary data-point\r\n // objects and treats unknown fields as inert metadata.\r\n updateChartDatasets(chart, type, datasets as ChartDataset[]);\r\n lastDrawn.current = { type, labels, datasets, theme };\r\n return;\r\n }\r\n\r\n // Destroy any prior instance before creating the new one. The driver\r\n // also defensively destroys whatever Chart.js still tracks for this\r\n // canvas, but tearing down our owned ref keeps state tidy.\r\n destroyChart(chartRef.current);\r\n chartRef.current = createChart(canvas, type, labels, datasets as ChartDataset[], theme, handleClick);\r\n\r\n lastDrawn.current = { type, labels, datasets, theme };\r\n }, [type, labels, datasets, theme, handleClick]);\r\n\r\n // Final teardown on unmount. Separate effect (no deps) so it fires once\r\n // for the component's lifetime — the lifecycle effect above runs on\r\n // input changes and would otherwise re-run this cleanup unnecessarily.\r\n useEffect(() => {\r\n return () => {\r\n destroyChart(chartRef.current);\r\n chartRef.current = null;\r\n };\r\n }, []);\r\n\r\n useImperativeHandle(\r\n ref,\r\n () => ({\r\n exportAsImage: () => exportChartAsImage(chartRef.current),\r\n }),\r\n [],\r\n );\r\n\r\n return (\r\n <div className=\"ppp-chart\" style={getContainerStyle(width, height)}>\r\n <canvas\r\n ref={canvasRef}\r\n style={{ display: 'block', maxWidth: '100%', maxHeight: '100%' }}\r\n />\r\n </div>\r\n );\r\n}\r\n\r\nexport const Chart = forwardRef(ChartImpl) as <TPoint = DataPoint>(\r\n props: ChartProps<TPoint> & RefAttributes<ChartHandle>,\r\n) => ReactElement | null;\r\n\r\n/**\r\n * True when `next` differs from `prev` ONLY in per-point styling\r\n * (backgroundColor / borderColor on data points, dataset-level\r\n * styling). The structural axes — dataset count, dataset label, per-\r\n * point numeric value, point count per dataset — must all match.\r\n *\r\n * Used to detect chart cross-filter highlight toggles, which should\r\n * apply via `chart.update('none')` instead of triggering a full\r\n * destroy + recreate (the latter restarts the entry animation, which\r\n * feels laggy on a slice click). False positives are safe (a full\r\n * recreate still works); false negatives unnecessarily animate.\r\n */\r\nfunction isStyleOnlyDatasetChange<TPoint>(\r\n prev: ChartDataset<TPoint>[],\r\n next: ChartDataset<TPoint>[],\r\n): boolean {\r\n if (prev.length !== next.length) return false;\r\n for (let i = 0; i < prev.length; i++) {\r\n const p = prev[i]!;\r\n const n = next[i]!;\r\n if (p.label !== n.label) return false;\r\n const pData = p.data ?? [];\r\n const nData = n.data ?? [];\r\n if (pData.length !== nData.length) return false;\r\n for (let j = 0; j < pData.length; j++) {\r\n // TPoint is opaque; the only field we structurally rely on for\r\n // \"is this the same data?\" is `.value`, which lives on\r\n // DataPointBase. Cast through `unknown` to read it without\r\n // requiring TPoint to expose it.\r\n const pv = (pData[j] as unknown as { value?: unknown }).value;\r\n const nv = (nData[j] as unknown as { value?: unknown }).value;\r\n if (pv !== nv) return false;\r\n }\r\n }\r\n return true;\r\n}\r\n","/**\r\n * Base chart data source. Builds a {@link ChartDataRequest} from the\r\n * configured properties and dispatches it through the server-side\r\n * `IChartService` via {@link PowerPortalsProClient.loadChartAsync}.\r\n * The server owns FetchXML construction and record shaping — this class\r\n * is a thin client over the typed request shape.\r\n *\r\n * Mirrors `PowerPortalsPro.Web.Blazor.Charts.Components.DataverseChartDataSource`\r\n * on the Blazor side; both feed the same endpoint.\r\n */\r\n\r\nimport type {\r\n ChartData,\r\n ChartDataRequest,\r\n PowerPortalsProClient,\r\n} from '@powerportalspro/core';\r\n\r\n/**\r\n * Configuration accepted by {@link DataverseChartDataSource}. Subclasses\r\n * carry their own subset and forward what they need to the base constructor.\r\n */\r\nexport interface DataverseChartDataSourceOptions {\r\n /**\r\n * FetchXML to send to the server. Subclasses leave this blank and\r\n * override `configureRequest` to set a different input mode.\r\n */\r\n fetchXml?: string;\r\n /** Result column name to read chart labels from (X axis). */\r\n labelColumn?: string;\r\n /** Result column name to read numeric values from. */\r\n valueColumn?: string;\r\n /**\r\n * Optional result column name used to split rows into multiple\r\n * datasets. When set, rows sharing the same series value form one\r\n * dataset.\r\n */\r\n seriesColumn?: string;\r\n /**\r\n * Optional label applied to the single emitted dataset when\r\n * {@link seriesColumn} isn't set. Multi-series queries use the\r\n * formatted series value as the dataset label and ignore this.\r\n */\r\n singleSeriesLabel?: string;\r\n}\r\n\r\nexport class DataverseChartDataSource {\r\n fetchXml?: string;\r\n labelColumn = '';\r\n valueColumn = '';\r\n seriesColumn?: string;\r\n singleSeriesLabel = '';\r\n\r\n constructor(options: DataverseChartDataSourceOptions = {}) {\r\n Object.assign(this, options);\r\n }\r\n\r\n /**\r\n * Builds a {@link ChartDataRequest} from this instance's properties\r\n * and POSTs it via `client.loadChartAsync`. Subclasses inject the\r\n * input mode via {@link configureRequest}.\r\n */\r\n async loadAsync(\r\n client: PowerPortalsProClient,\r\n signal?: AbortSignal,\r\n ): Promise<ChartData> {\r\n const request: ChartDataRequest = {\r\n labelColumn: this.labelColumn,\r\n valueColumn: this.valueColumn,\r\n };\r\n if (this.seriesColumn !== undefined) request.seriesColumn = this.seriesColumn;\r\n if (this.singleSeriesLabel) request.singleSeriesLabel = this.singleSeriesLabel;\r\n this.configureRequest(request);\r\n\r\n const response = await client.loadChartAsync(request, signal);\r\n return response.data ?? { labels: [], datasets: [] };\r\n }\r\n\r\n /**\r\n * Sets the input mode (FetchXml / ViewId / Aggregate) on the request.\r\n * Base implementation sets `fetchXml`; subclasses override to use\r\n * ViewId or Aggregate config instead.\r\n */\r\n protected configureRequest(request: ChartDataRequest): void {\r\n if (this.fetchXml !== undefined) request.fetchXml = this.fetchXml;\r\n }\r\n}\r\n","/**\r\n * Loads chart data from a saved Dataverse view. Caller still picks which\r\n * of the view's columns map to labels / values via `labelColumn` /\r\n * `valueColumn`; this class just sets the request's `viewId` input mode.\r\n *\r\n * Mirrors `PowerPortalsPro.Web.Blazor.Charts.Components.ViewDataverseChartDataSource`.\r\n */\r\n\r\nimport type { ChartDataRequest } from '@powerportalspro/core';\r\nimport {\r\n DataverseChartDataSource,\r\n type DataverseChartDataSourceOptions,\r\n} from './dataverse-chart-data-source.js';\r\n\r\nexport interface ViewDataverseChartDataSourceOptions extends DataverseChartDataSourceOptions {\r\n /** Dataverse saved-view id. Server fetches its FetchXML on every load. */\r\n viewId?: string;\r\n}\r\n\r\nexport class ViewDataverseChartDataSource extends DataverseChartDataSource {\r\n viewId?: string;\r\n\r\n constructor(options: ViewDataverseChartDataSourceOptions = {}) {\r\n super(options);\r\n if (options.viewId !== undefined) {\r\n this.viewId = options.viewId;\r\n }\r\n }\r\n\r\n protected override configureRequest(request: ChartDataRequest): void {\r\n if (!this.viewId) {\r\n throw new Error('\"viewId\" must be set before calling loadAsync.');\r\n }\r\n request.viewId = this.viewId;\r\n }\r\n}\r\n","/**\r\n * Aggregate chart data source. Builds an {@link AggregateChartConfig}\r\n * from typed properties and sends it through the server-side endpoint.\r\n * The server constructs the FetchXML (including link-entity trees,\r\n * combined date groupings, and view-filter merging) and shapes the\r\n * response.\r\n *\r\n * Mirrors `PowerPortalsPro.Web.Blazor.Charts.Components.AggregateDataverseChartDataSource`.\r\n */\r\n\r\nimport {\r\n AggregateType,\r\n ChartDateGrouping,\r\n type AggregateChartConfig,\r\n type ChartDataRequest,\r\n type ChartGrouping,\r\n type ChartLinkedEntity,\r\n} from '@powerportalspro/core';\r\nimport {\r\n DataverseChartDataSource,\r\n type DataverseChartDataSourceOptions,\r\n} from './dataverse-chart-data-source.js';\r\n\r\n/** Caller-supplied configuration. Mirrors the Blazor property surface. */\r\nexport interface AggregateDataverseChartDataSourceOptions\r\n extends Omit<DataverseChartDataSourceOptions, 'fetchXml' | 'labelColumn' | 'valueColumn'> {\r\n /** Dataverse table to query (e.g. `\"opportunity\"`). */\r\n tableName: string;\r\n /** Column to group by — becomes the chart's X-axis labels. */\r\n groupByColumn: string;\r\n /** Date bucketing for `groupByColumn` when it's a datetime column. */\r\n groupByDateGrouping?: ChartDateGrouping;\r\n /** Linked entity chain when `groupByColumn` lives on a related table. */\r\n groupByLinkedEntity?: ChartLinkedEntity;\r\n /**\r\n * Secondary date grouping applied to the same column as `groupByColumn`\r\n * (year-over-year charts). When set, the server emits a second\r\n * `groupby` on the same column and uses its result as the series\r\n * discriminator.\r\n */\r\n seriesDateGrouping?: ChartDateGrouping;\r\n /** Linked entity chain for `seriesColumn` when it lives on a related table. */\r\n seriesLinkedEntity?: ChartLinkedEntity;\r\n /** Column to aggregate (sum / count / etc.). */\r\n aggregateColumn: string;\r\n /** Aggregate function. Defaults to `AggregateType.Sum`. */\r\n aggregate?: AggregateType;\r\n /** Optional saved-view id whose filters are merged into the generated query. */\r\n viewId?: string;\r\n /** Optional raw `<filter>...</filter>` fragment injected into the query. */\r\n filterXml?: string;\r\n /** Optional row cap. */\r\n top?: number;\r\n}\r\n\r\nexport class AggregateDataverseChartDataSource extends DataverseChartDataSource {\r\n tableName: string;\r\n groupByDateGrouping: ChartDateGrouping = ChartDateGrouping.None;\r\n groupByLinkedEntity?: ChartLinkedEntity;\r\n seriesDateGrouping: ChartDateGrouping = ChartDateGrouping.None;\r\n seriesLinkedEntity?: ChartLinkedEntity;\r\n aggregate: AggregateType = AggregateType.Sum;\r\n viewId?: string;\r\n filterXml?: string;\r\n top?: number;\r\n\r\n constructor(options: AggregateDataverseChartDataSourceOptions) {\r\n super(options);\r\n this.tableName = options.tableName;\r\n this.labelColumn = options.groupByColumn;\r\n this.valueColumn = options.aggregateColumn;\r\n if (options.groupByDateGrouping !== undefined) this.groupByDateGrouping = options.groupByDateGrouping;\r\n if (options.groupByLinkedEntity !== undefined) this.groupByLinkedEntity = options.groupByLinkedEntity;\r\n if (options.seriesDateGrouping !== undefined) this.seriesDateGrouping = options.seriesDateGrouping;\r\n if (options.seriesLinkedEntity !== undefined) this.seriesLinkedEntity = options.seriesLinkedEntity;\r\n if (options.aggregate !== undefined) this.aggregate = options.aggregate;\r\n if (options.viewId !== undefined) this.viewId = options.viewId;\r\n if (options.filterXml !== undefined) this.filterXml = options.filterXml;\r\n if (options.top !== undefined) this.top = options.top;\r\n }\r\n\r\n /** Caller-friendly alias for `labelColumn` (kept in sync via getter/setter). */\r\n get groupByColumn(): string {\r\n return this.labelColumn;\r\n }\r\n set groupByColumn(value: string) {\r\n this.labelColumn = value;\r\n }\r\n\r\n /** Caller-friendly alias for `valueColumn`. */\r\n get aggregateColumn(): string {\r\n return this.valueColumn;\r\n }\r\n set aggregateColumn(value: string) {\r\n this.valueColumn = value;\r\n }\r\n\r\n protected override configureRequest(request: ChartDataRequest): void {\r\n // Compose the nested wire shape from the class's flat property\r\n // surface (kept flat for authoring ergonomics). The `series` slot\r\n // is only populated when at least one series-related option is\r\n // actually set — leaving it `null` preserves single-series\r\n // semantics on the server.\r\n const groupBy: ChartGrouping = {\r\n column: this.groupByColumn,\r\n dateGrouping: this.groupByDateGrouping,\r\n };\r\n if (this.groupByLinkedEntity) groupBy.linkedEntity = this.groupByLinkedEntity;\r\n\r\n let series: ChartGrouping | undefined;\r\n if (\r\n this.seriesColumn !== undefined ||\r\n this.seriesDateGrouping !== ChartDateGrouping.None ||\r\n this.seriesLinkedEntity !== undefined\r\n ) {\r\n series = {\r\n column: this.seriesColumn ?? '',\r\n dateGrouping: this.seriesDateGrouping,\r\n };\r\n if (this.seriesLinkedEntity) series.linkedEntity = this.seriesLinkedEntity;\r\n }\r\n\r\n const config: AggregateChartConfig = {\r\n tableName: this.tableName,\r\n groupBy,\r\n aggregateColumn: this.aggregateColumn,\r\n aggregate: this.aggregate,\r\n };\r\n if (series !== undefined) config.series = series;\r\n if (this.viewId !== undefined) config.viewFilterId = this.viewId;\r\n if (this.filterXml !== undefined) config.filterXml = this.filterXml;\r\n if (this.top !== undefined) config.top = this.top;\r\n\r\n request.aggregate = config;\r\n }\r\n}\r\n","export const VERSION = '0.1.0';\r\n\r\n// Types + enums.\r\n//\r\n// Wire-shape types (ChartData, ChartDataset, DataPoint,\r\n// DataverseDataPoint, ChartLinkedEntity, ChartDateGrouping,\r\n// AggregateType, JoinOperator, ChartDataRequest, ChartDataResponse,\r\n// AggregateChartConfig) are generated from the OpenAPI document on the\r\n// server side and re-exported here so consumers get a single chart-\r\n// package import surface.\r\n//\r\n// Display-only enums (ChartType, ChartLegendPosition, ChartIndexAxis,\r\n// ChartOrientation) and ChartTheme / ChartClickEventArgs live in\r\n// `types.ts` — these never cross the wire.\r\nexport {\r\n AggregateType,\r\n ChartDateGrouping,\r\n ChartIndexAxis,\r\n ChartLegendPosition,\r\n ChartOrientation,\r\n ChartType,\r\n JoinOperator,\r\n toIndexAxis,\r\n type AggregateChartConfig,\r\n type BaseDataPoint,\r\n type ChartClickEventArgs,\r\n type ChartData,\r\n type ChartDataset,\r\n type ChartLinkedEntity,\r\n type ChartTheme,\r\n type DataPoint,\r\n type DataverseDataPoint,\r\n type ChartDataRequest,\r\n type ChartDataResponse,\r\n} from './types.js';\r\n\r\n// Base `<Chart>` component — direct Chart.js wrapper, no Fluent UI\r\n// dependency. Themed wrapper / DataverseChart convenience component live\r\n// in `@powerportalspro/react-fluent`.\r\nexport { Chart, type ChartHandle, type ChartProps } from './chart.js';\r\n\r\n// Data sources — pure logic classes that turn a configuration into a\r\n// FetchXML query, run it via `PowerPortalsProClient`, and return a\r\n// `ChartData`. Consumers wire one to `<DataverseChart source={…} />`.\r\nexport {\r\n DataverseChartDataSource,\r\n type DataverseChartDataSourceOptions,\r\n} from './dataverse-chart-data-source.js';\r\nexport {\r\n ViewDataverseChartDataSource,\r\n type ViewDataverseChartDataSourceOptions,\r\n} from './view-dataverse-chart-data-source.js';\r\nexport {\r\n AggregateDataverseChartDataSource,\r\n type AggregateDataverseChartDataSourceOptions,\r\n} from './aggregate-dataverse-chart-data-source.js';\r\n"]}