@praxisui/charts 3.0.0-beta.0 → 3.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,7 +5,7 @@ import * as i1$1 from '@praxisui/core';
5
5
  import { buildApiUrl, API_URL, PraxisI18nService, ComponentMetadataRegistry, createDefaultTableConfig, DynamicWidgetPageComponent, DynamicGridPageComponent } from '@praxisui/core';
6
6
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
7
7
  import { use, init } from 'echarts/core';
8
- import { BarChart, LineChart, PieChart } from 'echarts/charts';
8
+ import { BarChart, LineChart, PieChart, ScatterChart } from 'echarts/charts';
9
9
  import { AriaComponent, DatasetComponent, GridComponent, LegendComponent, TitleComponent, TooltipComponent, TransformComponent } from 'echarts/components';
10
10
  import { CanvasRenderer } from 'echarts/renderers';
11
11
  import * as i1 from '@angular/common/http';
@@ -19,6 +19,9 @@ class PraxisChartDataTransformerService {
19
19
  if (config.type === 'pie' || config.type === 'donut') {
20
20
  return this.transformPie(config, rows);
21
21
  }
22
+ if (config.type === 'scatter') {
23
+ return this.transformScatter(config, rows);
24
+ }
22
25
  return this.transformCartesian(config, rows);
23
26
  }
24
27
  transformCartesian(config, rows) {
@@ -86,10 +89,11 @@ class PraxisChartDataTransformerService {
86
89
  return {
87
90
  id: seriesConfig.id,
88
91
  name: seriesConfig.name ?? seriesConfig.metric?.label ?? seriesConfig.id,
89
- type: config.type === 'bar' || config.type === 'stacked-bar' ? 'bar' : 'line',
90
- stack: config.type === 'stacked-bar' ? seriesConfig.stackId ?? 'stack-1' : undefined,
91
- smooth: seriesConfig.smooth ?? config.type === 'area',
92
- area: config.type === 'area',
92
+ type: this.resolveCartesianSeriesType(config, seriesConfig),
93
+ axis: seriesConfig.axis ?? 'primary',
94
+ stack: config.type === 'stacked-bar' || config.type === 'stacked-area' ? seriesConfig.stackId ?? 'stack-1' : undefined,
95
+ smooth: seriesConfig.smooth ?? this.isAreaLikeSeries(config, seriesConfig),
96
+ area: this.isAreaLikeSeries(config, seriesConfig),
93
97
  color: seriesConfig.color,
94
98
  labelsVisible: seriesConfig.labels?.visible ?? false,
95
99
  points: categories.map((category) => {
@@ -100,6 +104,66 @@ class PraxisChartDataTransformerService {
100
104
  }),
101
105
  };
102
106
  }
107
+ resolveCartesianSeriesType(config, seriesConfig) {
108
+ if (config.type === 'combo') {
109
+ return seriesConfig.type === 'bar' ? 'bar' : 'line';
110
+ }
111
+ return config.type === 'bar' || config.type === 'stacked-bar' || config.type === 'horizontal-bar'
112
+ ? 'bar'
113
+ : 'line';
114
+ }
115
+ isAreaLikeSeries(config, seriesConfig) {
116
+ if (config.type === 'combo') {
117
+ return seriesConfig.type === 'area';
118
+ }
119
+ return config.type === 'area' || config.type === 'stacked-area';
120
+ }
121
+ transformScatter(config, rows) {
122
+ const xField = config.axes?.x?.field;
123
+ const seriesConfig = config.series[0];
124
+ const yField = config.axes?.y?.field ?? seriesConfig?.metric?.field;
125
+ if (!xField || !seriesConfig || !yField || !rows.length) {
126
+ return {
127
+ mode: 'scatter',
128
+ categories: [],
129
+ series: [],
130
+ slices: [],
131
+ hasData: false,
132
+ };
133
+ }
134
+ const points = rows
135
+ .map((row) => {
136
+ const xValue = this.extractScatterXValue(row[xField], config.axes?.x?.type);
137
+ const yValue = this.extractMetricValue(row, {
138
+ ...seriesConfig,
139
+ metric: {
140
+ ...seriesConfig.metric,
141
+ field: yField,
142
+ },
143
+ });
144
+ if (xValue === null || !Number.isFinite(yValue)) {
145
+ return null;
146
+ }
147
+ return [xValue, yValue];
148
+ })
149
+ .filter((point) => point !== null);
150
+ return {
151
+ mode: 'scatter',
152
+ categories: [],
153
+ series: [
154
+ {
155
+ id: seriesConfig.id,
156
+ name: seriesConfig.name ?? seriesConfig.metric?.label ?? seriesConfig.id,
157
+ type: 'scatter',
158
+ color: seriesConfig.color,
159
+ labelsVisible: seriesConfig.labels?.visible ?? false,
160
+ points,
161
+ },
162
+ ],
163
+ slices: [],
164
+ hasData: points.length > 0,
165
+ };
166
+ }
103
167
  aggregate(rows, seriesConfig) {
104
168
  const aggregation = seriesConfig.metric?.aggregation ?? 'sum';
105
169
  if (aggregation === 'count') {
@@ -145,6 +209,25 @@ class PraxisChartDataTransformerService {
145
209
  }
146
210
  return String(value);
147
211
  }
212
+ extractScatterXValue(value, axisType) {
213
+ if (value instanceof Date) {
214
+ return axisType === 'time' ? value.toISOString() : value.getTime();
215
+ }
216
+ if (typeof value === 'number' && Number.isFinite(value)) {
217
+ return value;
218
+ }
219
+ if (typeof value === 'string') {
220
+ const trimmed = value.trim();
221
+ if (!trimmed)
222
+ return null;
223
+ const parsed = Number(trimmed);
224
+ if (axisType === 'value' && Number.isFinite(parsed)) {
225
+ return parsed;
226
+ }
227
+ return trimmed;
228
+ }
229
+ return null;
230
+ }
148
231
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartDataTransformerService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
149
232
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartDataTransformerService, providedIn: 'root' });
150
233
  }
@@ -191,6 +274,47 @@ class PraxisChartOptionBuilderService {
191
274
  series: [pieSeries],
192
275
  };
193
276
  }
277
+ if (transformed.mode === 'scatter') {
278
+ const scatterSeries = transformed.series.map((series) => ({
279
+ id: series.id,
280
+ name: series.name,
281
+ type: 'scatter',
282
+ label: { show: series.labelsVisible },
283
+ itemStyle: series.color ? { color: series.color } : undefined,
284
+ data: series.points,
285
+ }));
286
+ return {
287
+ backgroundColor: config.theme?.backgroundColor,
288
+ color: palette,
289
+ title: {
290
+ text: this.resolveText(config.title),
291
+ subtext: this.resolveText(config.subtitle),
292
+ left: 'left',
293
+ },
294
+ tooltip: tooltipEnabled ? { trigger: 'item' } : undefined,
295
+ legend: legendVisible ? { show: true, top: 0 } : { show: false },
296
+ grid: this.buildGrid(),
297
+ xAxis: {
298
+ type: config.axes?.x?.type ?? 'value',
299
+ name: config.axes?.x?.label,
300
+ axisLabel: {
301
+ show: config.axes?.x?.labels?.visible ?? true,
302
+ rotate: config.axes?.x?.labels?.rotate ?? 0,
303
+ },
304
+ },
305
+ yAxis: {
306
+ type: config.axes?.y?.type ?? 'value',
307
+ name: config.axes?.y?.label,
308
+ min: config.axes?.y?.min,
309
+ max: config.axes?.y?.max,
310
+ axisLabel: {
311
+ show: config.axes?.y?.labels?.visible ?? true,
312
+ },
313
+ },
314
+ series: scatterSeries,
315
+ };
316
+ }
317
+ const horizontal = config.orientation === 'horizontal' || config.type === 'horizontal-bar';
194
318
  return {
195
319
  backgroundColor: config.theme?.backgroundColor,
196
320
  color: palette,
@@ -205,35 +329,42 @@ class PraxisChartOptionBuilderService {
205
329
  legend: legendVisible
206
330
  ? { show: true, top: 0 }
207
331
  : { show: false },
208
- grid: {
209
- top: 56,
210
- right: 24,
211
- bottom: 32,
212
- left: 48,
213
- containLabel: true,
214
- },
215
- xAxis: {
216
- type: config.axes?.x?.type ?? 'category',
217
- name: config.axes?.x?.label,
218
- data: transformed.categories,
219
- axisLabel: {
220
- show: config.axes?.x?.labels?.visible ?? true,
221
- rotate: config.axes?.x?.labels?.rotate ?? 0,
222
- },
223
- },
224
- yAxis: {
225
- type: config.axes?.y?.type ?? 'value',
226
- name: config.axes?.y?.label,
227
- min: config.axes?.y?.min,
228
- max: config.axes?.y?.max,
229
- axisLabel: {
230
- show: config.axes?.y?.labels?.visible ?? true,
332
+ grid: this.buildGrid(),
333
+ xAxis: horizontal
334
+ ? {
335
+ type: config.axes?.y?.type ?? 'value',
336
+ name: config.axes?.y?.label,
337
+ min: config.axes?.y?.min,
338
+ max: config.axes?.y?.max,
339
+ axisLabel: {
340
+ show: config.axes?.y?.labels?.visible ?? true,
341
+ },
342
+ }
343
+ : {
344
+ type: config.axes?.x?.type ?? 'category',
345
+ name: config.axes?.x?.label,
346
+ data: transformed.categories,
347
+ axisLabel: {
348
+ show: config.axes?.x?.labels?.visible ?? true,
349
+ rotate: config.axes?.x?.labels?.rotate ?? 0,
350
+ },
231
351
  },
232
- },
352
+ yAxis: horizontal
353
+ ? {
354
+ type: config.axes?.x?.type ?? 'category',
355
+ name: config.axes?.x?.label,
356
+ data: transformed.categories,
357
+ axisLabel: {
358
+ show: config.axes?.x?.labels?.visible ?? true,
359
+ rotate: config.axes?.x?.labels?.rotate ?? 0,
360
+ },
361
+ }
362
+ : this.buildCartesianYAxis(config),
233
363
  series: transformed.series.map((series) => ({
234
364
  id: series.id,
235
365
  name: series.name,
236
366
  type: series.type,
367
+ yAxisIndex: series.axis === 'secondary' ? 1 : 0,
237
368
  stack: series.stack,
238
369
  smooth: series.smooth,
239
370
  areaStyle: series.area ? {} : undefined,
@@ -254,6 +385,45 @@ class PraxisChartOptionBuilderService {
254
385
  }
255
386
  return undefined;
256
387
  }
388
+ buildGrid() {
389
+ return {
390
+ top: 56,
391
+ right: 24,
392
+ bottom: 32,
393
+ left: 48,
394
+ // ECharts deprecou containLabel; esta combinacao preserva o mesmo comportamento
395
+ // sem depender da feature legacy de grid.
396
+ outerBoundsMode: 'same',
397
+ outerBoundsContain: 'axisLabel',
398
+ };
399
+ }
400
+ buildCartesianYAxis(config) {
401
+ const primaryAxis = {
402
+ type: config.axes?.y?.type ?? 'value',
403
+ name: config.axes?.y?.label,
404
+ min: config.axes?.y?.min,
405
+ max: config.axes?.y?.max,
406
+ axisLabel: {
407
+ show: config.axes?.y?.labels?.visible ?? true,
408
+ },
409
+ };
410
+ if (!config.axes?.ySecondary) {
411
+ return primaryAxis;
412
+ }
413
+ return [
414
+ primaryAxis,
415
+ {
416
+ type: config.axes.ySecondary.type ?? 'value',
417
+ name: config.axes.ySecondary.label,
418
+ min: config.axes.ySecondary.min,
419
+ max: config.axes.ySecondary.max,
420
+ position: config.axes.ySecondary.position ?? 'right',
421
+ axisLabel: {
422
+ show: config.axes.ySecondary.labels?.visible ?? true,
423
+ },
424
+ },
425
+ ];
426
+ }
257
427
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartOptionBuilderService, deps: [{ token: PraxisChartDataTransformerService }], target: i0.ɵɵFactoryTarget.Injectable });
258
428
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisChartOptionBuilderService, providedIn: 'root' });
259
429
  }
@@ -271,6 +441,7 @@ use([
271
441
  LegendComponent,
272
442
  LineChart,
273
443
  PieChart,
444
+ ScatterChart,
274
445
  TitleComponent,
275
446
  TooltipComponent,
276
447
  TransformComponent,
@@ -327,23 +498,22 @@ class PraxisChartStatsApiService {
327
498
  return throwError(() => new Error('PraxisChartStatsApiService requires query.statsPath and query.statsRequest for praxis.stats execution.'));
328
499
  }
329
500
  const categoryField = this.resolveCategoryField(config, statsRequest);
330
- const metricField = this.resolveMetricField(config);
331
501
  const url = this.buildStatsUrl(query.statsPath);
332
502
  return this.http
333
503
  .post(url, statsRequest)
334
- .pipe(map((response) => this.toChartRows(response?.data, statsRequest, categoryField, metricField)), catchError((error) => this.handleHttpError(error)));
504
+ .pipe(map((response) => this.toChartRows(response?.data, statsRequest, categoryField, config)), catchError((error) => this.handleHttpError(error)));
335
505
  }
336
- toChartRows(response, request, categoryField, metricField) {
506
+ toChartRows(response, request, categoryField, config) {
337
507
  if (!response) {
338
508
  return [];
339
509
  }
510
+ const metricBindings = this.resolveMetricBindings(config, request, response);
340
511
  if ('points' in response) {
341
512
  return response.points.map((point) => {
342
513
  const category = point.label ?? point.start ?? point.end ?? '';
343
- const metricValue = this.resolveMetricValue(point.value, point.count);
344
514
  return {
345
515
  [categoryField]: category,
346
- [metricField]: metricValue,
516
+ ...this.projectMetricValues(metricBindings, point.values, point.value, point.count),
347
517
  key: point.start ?? point.label ?? point.end ?? category,
348
518
  label: point.label ?? category,
349
519
  value: point.value ?? null,
@@ -357,10 +527,9 @@ class PraxisChartStatsApiService {
357
527
  if ('buckets' in response) {
358
528
  return response.buckets.map((bucket) => {
359
529
  const category = this.resolveBucketCategory(bucket);
360
- const metricValue = this.resolveMetricValue(bucket.value, bucket.count);
361
530
  return {
362
531
  [categoryField]: category,
363
- [metricField]: metricValue,
532
+ ...this.projectMetricValues(metricBindings, bucket.values, bucket.value, bucket.count),
364
533
  key: bucket.key ?? null,
365
534
  label: bucket.label ?? category,
366
535
  value: bucket.value ?? null,
@@ -402,8 +571,46 @@ class PraxisChartStatsApiService {
402
571
  || request.field
403
572
  || 'category');
404
573
  }
405
- resolveMetricField(config) {
406
- return config.series[0]?.metric?.field || 'value';
574
+ resolveMetricBindings(config, request, response) {
575
+ const queryMetrics = config.dataSource?.kind === 'remote'
576
+ ? (config.dataSource.query?.metrics ?? [])
577
+ : [];
578
+ if (queryMetrics.length) {
579
+ return queryMetrics.map((metric, index) => ({
580
+ field: metric.field || `value${index + 1}`,
581
+ alias: metric.alias || metric.field || `value${index + 1}`,
582
+ }));
583
+ }
584
+ const responseMetrics = 'metrics' in response ? response.metrics : undefined;
585
+ if (responseMetrics?.length) {
586
+ return responseMetrics.map((metric, index) => ({
587
+ field: metric.alias || metric.field || `value${index + 1}`,
588
+ alias: metric.alias || metric.field || `value${index + 1}`,
589
+ }));
590
+ }
591
+ const requestMetrics = 'metrics' in request ? request.metrics : undefined;
592
+ if (requestMetrics?.length) {
593
+ return requestMetrics.map((metric, index) => ({
594
+ field: metric.alias || metric.field || `value${index + 1}`,
595
+ alias: metric.alias || metric.field || `value${index + 1}`,
596
+ }));
597
+ }
598
+ const metric = ('metric' in response && response.metric) || ('metric' in request ? request.metric : undefined);
599
+ return [
600
+ {
601
+ field: metric?.alias || metric?.field || config.series[0]?.metric?.field || 'value',
602
+ alias: metric?.alias || metric?.field || config.series[0]?.metric?.field || 'value',
603
+ },
604
+ ];
605
+ }
606
+ projectMetricValues(bindings, values, primaryValue, count) {
607
+ return bindings.reduce((acc, binding, index) => {
608
+ const rawValue = values?.[binding.alias];
609
+ acc[binding.field] = index === 0
610
+ ? this.resolveMetricValue(rawValue ?? primaryValue, count)
611
+ : this.resolveMetricValue(rawValue, null);
612
+ return acc;
613
+ }, {});
407
614
  }
408
615
  buildStatsUrl(statsPath) {
409
616
  const base = this.buildDefaultApiBase();
@@ -906,7 +1113,7 @@ const PRAXIS_CHART_COMPONENT_METADATA = {
906
1113
  {
907
1114
  name: 'queryRequest',
908
1115
  type: 'PraxisChartQueryRequestEvent',
909
- description: 'Emitted before a remote praxis.stats datasource is resolved, allowing host-side observability or overrides.',
1116
+ description: 'Emitted before a remote praxis.stats datasource is resolved, allowing host-side observability of the outgoing request.',
910
1117
  },
911
1118
  {
912
1119
  name: 'loadStateChange',
@@ -1339,6 +1546,7 @@ class PraxisChartCanonicalContractMapperService {
1339
1546
  return {
1340
1547
  id: contract.chartId,
1341
1548
  type: contract.kind,
1549
+ orientation: this.resolveOrientation(contract),
1342
1550
  title: this.mapTextValue(contract.title),
1343
1551
  subtitle: this.mapTextValue(contract.subtitle),
1344
1552
  height: contract.height,
@@ -1365,16 +1573,13 @@ class PraxisChartCanonicalContractMapperService {
1365
1573
  };
1366
1574
  }
1367
1575
  assertSupportedContract(contract) {
1368
- if (contract.source.kind === 'derived') {
1369
- throw new Error('x-ui.chart source.kind="derived" is not yet implemented in @praxisui/charts.');
1370
- }
1371
- if (contract.source.kind !== 'praxis.stats') {
1576
+ if (contract.source.kind !== 'praxis.stats' && contract.source.kind !== 'derived') {
1372
1577
  throw new Error(`x-ui.chart source.kind="${contract.source.kind}" is not supported in @praxisui/charts.`);
1373
1578
  }
1374
- if (!contract.source.resource?.trim()) {
1579
+ if (contract.source.kind === 'praxis.stats' && !contract.source.resource?.trim()) {
1375
1580
  throw new Error('x-ui.chart source.resource is required for source.kind="praxis.stats".');
1376
1581
  }
1377
- if (!contract.source.operation) {
1582
+ if (contract.source.kind === 'praxis.stats' && !contract.source.operation) {
1378
1583
  throw new Error('x-ui.chart source.operation is required for source.kind="praxis.stats".');
1379
1584
  }
1380
1585
  if (contract.theme?.palette && typeof contract.theme.palette === 'string') {
@@ -1402,8 +1607,35 @@ class PraxisChartCanonicalContractMapperService {
1402
1607
  if (contract.metrics.length > 1 && (contract.kind === 'pie' || contract.kind === 'donut')) {
1403
1608
  throw new Error('x-ui.chart pie/donut charts with multiple metrics are not yet implemented in @praxisui/charts.');
1404
1609
  }
1405
- if (contract.source.kind === 'praxis.stats' && contract.metrics.length > 1) {
1406
- throw new Error('x-ui.chart praxis.stats currently supports a single metric per chart in @praxisui/charts.');
1610
+ if (contract.kind === 'combo') {
1611
+ if (contract.metrics.length < 2) {
1612
+ throw new Error('x-ui.chart combo charts require at least two metrics.');
1613
+ }
1614
+ if (contract.source.kind === 'praxis.stats'
1615
+ && contract.source.operation !== 'group-by'
1616
+ && contract.source.operation !== 'timeseries') {
1617
+ throw new Error('x-ui.chart combo charts over praxis.stats currently support only group-by or timeseries operations in @praxisui/charts.');
1618
+ }
1619
+ }
1620
+ if (contract.kind !== 'combo' && contract.metrics.some((metric) => metric.axis === 'secondary')) {
1621
+ throw new Error('x-ui.chart axis="secondary" is supported only for combo charts in @praxisui/charts.');
1622
+ }
1623
+ if (contract.source.kind === 'praxis.stats'
1624
+ && contract.source.operation === 'distribution'
1625
+ && contract.metrics.length > 1) {
1626
+ throw new Error('x-ui.chart praxis.stats distribution currently supports only a single metric in @praxisui/charts.');
1627
+ }
1628
+ if (contract.kind === 'horizontal-bar' && contract.orientation && contract.orientation !== 'horizontal') {
1629
+ throw new Error('x-ui.chart kind="horizontal-bar" requires orientation="horizontal" when orientation is provided.');
1630
+ }
1631
+ if (contract.kind === 'scatter') {
1632
+ const firstDimension = contract.dimensions?.[0];
1633
+ if (!firstDimension?.field) {
1634
+ throw new Error('x-ui.chart scatter charts require dimensions[0].field for the x axis.');
1635
+ }
1636
+ if (!contract.metrics?.[0]?.field) {
1637
+ throw new Error('x-ui.chart scatter charts require metrics[0].field for the y axis.');
1638
+ }
1407
1639
  }
1408
1640
  if (contract.events?.selectionChange || contract.events?.crossFilter) {
1409
1641
  throw new Error('x-ui.chart selectionChange/crossFilter declarative runtime actions are not yet implemented in @praxisui/charts.');
@@ -1412,6 +1644,8 @@ class PraxisChartCanonicalContractMapperService {
1412
1644
  buildAxes(contract) {
1413
1645
  const firstDimension = contract.dimensions?.[0];
1414
1646
  const firstMetric = contract.metrics?.[0];
1647
+ const secondaryMetric = contract.metrics?.find((metric) => metric.axis === 'secondary');
1648
+ const metricCount = contract.metrics?.length ?? 0;
1415
1649
  if (contract.kind === 'pie' || contract.kind === 'donut') {
1416
1650
  return {
1417
1651
  x: {
@@ -1420,6 +1654,20 @@ class PraxisChartCanonicalContractMapperService {
1420
1654
  },
1421
1655
  };
1422
1656
  }
1657
+ if (contract.kind === 'scatter') {
1658
+ return {
1659
+ x: {
1660
+ field: firstDimension?.field,
1661
+ label: this.toLabel(firstDimension?.label),
1662
+ type: firstDimension?.role === 'time' ? 'time' : 'value',
1663
+ },
1664
+ y: {
1665
+ field: firstMetric?.field,
1666
+ label: this.toLabel(firstMetric?.label),
1667
+ type: 'value',
1668
+ },
1669
+ };
1670
+ }
1423
1671
  return {
1424
1672
  x: {
1425
1673
  field: firstDimension?.field,
@@ -1427,9 +1675,16 @@ class PraxisChartCanonicalContractMapperService {
1427
1675
  type: firstDimension?.role === 'time' ? 'time' : 'category',
1428
1676
  },
1429
1677
  y: {
1430
- label: this.toLabel(firstMetric?.label),
1678
+ label: metricCount > 1 ? undefined : this.toLabel(firstMetric?.label),
1431
1679
  type: 'value',
1432
1680
  },
1681
+ ySecondary: contract.kind === 'combo' && secondaryMetric
1682
+ ? {
1683
+ label: this.toLabel(secondaryMetric.label),
1684
+ type: 'value',
1685
+ position: 'right',
1686
+ }
1687
+ : undefined,
1433
1688
  };
1434
1689
  }
1435
1690
  buildSeries(contract) {
@@ -1438,19 +1693,24 @@ class PraxisChartCanonicalContractMapperService {
1438
1693
  return (contract.metrics ?? []).map((metric, index) => ({
1439
1694
  id: `${metric.field}-${index + 1}`,
1440
1695
  name: this.toLabel(metric.label) ?? metric.field,
1441
- type: contract.kind === 'stacked-bar' ? 'bar' : contract.kind,
1696
+ type: this.resolveSeriesType(contract.kind, metric.seriesKind, index),
1697
+ axis: metric.axis ?? 'primary',
1442
1698
  categoryField: contract.kind === 'pie' || contract.kind === 'donut' ? firstDimension?.field : undefined,
1443
1699
  metric: {
1444
1700
  field: metric.field,
1445
1701
  aggregation: this.mapAggregation(metric.aggregation),
1446
1702
  label: this.toLabel(metric.label),
1447
1703
  },
1448
- stackId: contract.kind === 'stacked-bar' ? 'stack-1' : undefined,
1704
+ color: metric.color,
1705
+ stackId: contract.kind === 'stacked-bar' || contract.kind === 'stacked-area' ? 'stack-1' : undefined,
1449
1706
  labels: labelsVisible ? { visible: true } : undefined,
1450
- smooth: contract.kind === 'line' || contract.kind === 'area' ? true : undefined,
1707
+ smooth: this.shouldSmoothSeries(contract.kind, metric.seriesKind),
1451
1708
  }));
1452
1709
  }
1453
1710
  buildDataSource(contract) {
1711
+ if (contract.source.kind === 'derived') {
1712
+ return undefined;
1713
+ }
1454
1714
  return {
1455
1715
  kind: 'remote',
1456
1716
  resourcePath: contract.source.resource,
@@ -1459,6 +1719,9 @@ class PraxisChartCanonicalContractMapperService {
1459
1719
  };
1460
1720
  }
1461
1721
  buildQuery(contract) {
1722
+ if (contract.source.kind !== 'praxis.stats') {
1723
+ return undefined;
1724
+ }
1462
1725
  const filtersFromContract = this.toQueryFilterMap(contract.filters);
1463
1726
  const combinedFilters = {
1464
1727
  ...filtersFromContract,
@@ -1498,10 +1761,28 @@ class PraxisChartCanonicalContractMapperService {
1498
1761
  legend: { visible: this.resolveToggle(contract.legend, true) },
1499
1762
  tooltip: {
1500
1763
  enabled: this.resolveToggle(contract.tooltip, true),
1501
- trigger: contract.kind === 'pie' || contract.kind === 'donut' ? 'item' : 'axis',
1764
+ trigger: contract.kind === 'pie' || contract.kind === 'donut' || contract.kind === 'scatter' ? 'item' : 'axis',
1502
1765
  },
1503
1766
  };
1504
1767
  }
1768
+ resolveOrientation(contract) {
1769
+ if (contract.kind === 'horizontal-bar') {
1770
+ return 'horizontal';
1771
+ }
1772
+ return contract.orientation;
1773
+ }
1774
+ resolveSeriesType(chartKind, seriesKind, index) {
1775
+ if (chartKind === 'combo') {
1776
+ return seriesKind ?? (index === 0 ? 'bar' : 'line');
1777
+ }
1778
+ return chartKind === 'stacked-bar' ? 'bar' : chartKind;
1779
+ }
1780
+ shouldSmoothSeries(chartKind, seriesKind) {
1781
+ if (chartKind === 'combo') {
1782
+ return seriesKind === 'line' || seriesKind === 'area' ? true : undefined;
1783
+ }
1784
+ return chartKind === 'line' || chartKind === 'area' || chartKind === 'stacked-area' ? true : undefined;
1785
+ }
1505
1786
  mapTextValue(value) {
1506
1787
  if (!value)
1507
1788
  return undefined;
@@ -1559,15 +1840,18 @@ class PraxisChartCanonicalContractMapperService {
1559
1840
  }
1560
1841
  buildStatsRequest(contract, filters) {
1561
1842
  const field = this.resolveStatsField(contract);
1562
- const metric = this.buildStatsMetricRequest(contract);
1843
+ const metrics = this.buildStatsMetricRequests(contract);
1844
+ const metric = metrics[0];
1563
1845
  const limit = contract.limit ?? contract.source.options?.limit;
1564
1846
  const orderBy = this.mapStatsOrderByToBackend(contract.source.options?.orderBy);
1847
+ const requestMetrics = metrics.length > 1 ? metrics : undefined;
1565
1848
  switch (contract.source.operation) {
1566
1849
  case 'group-by':
1567
1850
  return {
1568
1851
  filter: filters,
1569
1852
  field,
1570
1853
  metric,
1854
+ metrics: requestMetrics,
1571
1855
  limit,
1572
1856
  orderBy,
1573
1857
  };
@@ -1577,6 +1861,7 @@ class PraxisChartCanonicalContractMapperService {
1577
1861
  field,
1578
1862
  granularity: this.mapStatsGranularityToBackend(contract.source.options?.granularity),
1579
1863
  metric,
1864
+ metrics: requestMetrics,
1580
1865
  fillGaps: contract.source.options?.fillGaps,
1581
1866
  };
1582
1867
  case 'distribution':
@@ -1610,8 +1895,22 @@ class PraxisChartCanonicalContractMapperService {
1610
1895
  return {
1611
1896
  operation,
1612
1897
  field: operation === 'COUNT' ? undefined : metric.field,
1898
+ alias: metric.field,
1613
1899
  };
1614
1900
  }
1901
+ buildStatsMetricRequests(contract) {
1902
+ if (!contract.metrics?.length) {
1903
+ throw new Error('x-ui.chart requires at least one metric to derive the canonical praxis.stats request.');
1904
+ }
1905
+ return contract.metrics.map((metric) => {
1906
+ const operation = this.mapStatsMetricOperation(metric.aggregation);
1907
+ return {
1908
+ operation,
1909
+ field: operation === 'COUNT' ? undefined : metric.field,
1910
+ alias: metric.field,
1911
+ };
1912
+ });
1913
+ }
1615
1914
  mapStatsOrderBy(value) {
1616
1915
  if (!value)
1617
1916
  return undefined;
@@ -2007,7 +2306,7 @@ const PRAXIS_CHART_BACKEND_MOCK_TIMESERIES = {
2007
2306
  widget: {
2008
2307
  kind: 'x-ui.chart',
2009
2308
  key: 'incident-timeseries-chart',
2010
- layout: { col: 7, row: 1, colSpan: 5, rowSpan: 4 },
2309
+ layout: { col: 7, row: 1, colSpan: 6, rowSpan: 4 },
2011
2310
  shell: {
2012
2311
  title: 'Incidentes por mes',
2013
2312
  subtitle: 'Time series real consumindo praxis.stats publicado',
@@ -2063,7 +2362,7 @@ const PRAXIS_CHART_BACKEND_MOCK_DONUT = {
2063
2362
  widget: {
2064
2363
  kind: 'x-ui.chart',
2065
2364
  key: 'ticket-status-chart',
2066
- layout: { col: 7, row: 1, colSpan: 5, rowSpan: 4 },
2365
+ layout: { col: 7, row: 5, colSpan: 6, rowSpan: 4 },
2067
2366
  shell: {
2068
2367
  title: 'Severidade de incidentes',
2069
2368
  subtitle: 'Distribution real consumindo praxis.stats publicado',
@@ -2107,6 +2406,313 @@ const PRAXIS_CHART_BACKEND_MOCK_DONUT = {
2107
2406
  },
2108
2407
  },
2109
2408
  };
2409
+ const PRAXIS_CHART_BACKEND_MOCK_HORIZONTAL_BAR = {
2410
+ schemaMeta: {
2411
+ schemaId: 'api/human-resources/vw-analytics-folha-pagamento|post|response|tenant:demo|locale:pt-BR',
2412
+ schemaHash: 'published-hash-payroll-department-ranking-v1',
2413
+ resourcePath: 'api/human-resources/vw-analytics-folha-pagamento',
2414
+ operation: 'post',
2415
+ schemaType: 'response',
2416
+ source: 'backend',
2417
+ },
2418
+ widget: {
2419
+ kind: 'x-ui.chart',
2420
+ key: 'payroll-department-horizontal-bar',
2421
+ layout: { col: 1, row: 5, colSpan: 6, rowSpan: 4 },
2422
+ shell: {
2423
+ title: 'Massa salarial por departamento',
2424
+ subtitle: 'Ranking horizontal em payroll analytics',
2425
+ },
2426
+ chart: {
2427
+ version: '0.1.0',
2428
+ kind: 'horizontal-bar',
2429
+ preset: 'ranking',
2430
+ chartId: 'payroll-department-horizontal-bar',
2431
+ title: { key: 'charts.payroll.department.title', fallback: 'Massa salarial por departamento' },
2432
+ subtitle: { key: 'charts.payroll.department.subtitle', fallback: 'Ranking horizontal consumindo praxis.stats' },
2433
+ orientation: 'horizontal',
2434
+ height: 340,
2435
+ source: {
2436
+ kind: 'praxis.stats',
2437
+ resource: 'api/human-resources/vw-analytics-folha-pagamento',
2438
+ operation: 'group-by',
2439
+ options: {
2440
+ orderBy: 'value-desc',
2441
+ limit: 8,
2442
+ },
2443
+ },
2444
+ dimensions: [
2445
+ {
2446
+ field: 'departamento',
2447
+ label: 'Departamento',
2448
+ role: 'category',
2449
+ },
2450
+ ],
2451
+ metrics: [
2452
+ {
2453
+ field: 'salarioLiquido',
2454
+ label: 'Massa liquida',
2455
+ aggregation: 'sum',
2456
+ },
2457
+ ],
2458
+ legend: { enabled: false },
2459
+ tooltip: { enabled: true },
2460
+ theme: {
2461
+ palette: ['#0f766e'],
2462
+ },
2463
+ events: {
2464
+ pointClick: {
2465
+ action: 'emit',
2466
+ },
2467
+ },
2468
+ },
2469
+ },
2470
+ };
2471
+ const PRAXIS_CHART_BACKEND_MOCK_STACKED_AREA = {
2472
+ schemaMeta: {
2473
+ schemaId: 'api/human-resources/vw-analytics-folha-pagamento|post|response|tenant:demo|locale:pt-BR',
2474
+ schemaHash: 'published-hash-payroll-net-trend-stacked-v1',
2475
+ resourcePath: 'api/human-resources/vw-analytics-folha-pagamento',
2476
+ operation: 'post',
2477
+ schemaType: 'response',
2478
+ source: 'backend',
2479
+ },
2480
+ widget: {
2481
+ kind: 'x-ui.chart',
2482
+ key: 'payroll-net-stacked-area',
2483
+ layout: { col: 1, row: 9, colSpan: 12, rowSpan: 4 },
2484
+ shell: {
2485
+ title: 'Composicao temporal da folha',
2486
+ subtitle: 'Stacked area inicial sobre timeseries published',
2487
+ },
2488
+ chart: {
2489
+ version: '0.1.0',
2490
+ kind: 'stacked-area',
2491
+ preset: 'composition',
2492
+ chartId: 'payroll-net-stacked-area',
2493
+ title: { key: 'charts.payroll.stackedArea.title', fallback: 'Composicao temporal da folha' },
2494
+ subtitle: { key: 'charts.payroll.stackedArea.subtitle', fallback: 'Stacked area sobre payroll analytics' },
2495
+ height: 340,
2496
+ source: {
2497
+ kind: 'praxis.stats',
2498
+ resource: 'api/human-resources/vw-analytics-folha-pagamento',
2499
+ operation: 'timeseries',
2500
+ options: {
2501
+ granularity: 'month',
2502
+ fillGaps: false,
2503
+ },
2504
+ },
2505
+ dimensions: [
2506
+ {
2507
+ field: 'competencia',
2508
+ label: 'Competencia',
2509
+ role: 'time',
2510
+ },
2511
+ ],
2512
+ metrics: [
2513
+ {
2514
+ field: 'salarioLiquido',
2515
+ label: 'Massa liquida',
2516
+ aggregation: 'sum',
2517
+ },
2518
+ ],
2519
+ legend: { enabled: true },
2520
+ tooltip: { enabled: true },
2521
+ theme: {
2522
+ palette: ['#1263b4'],
2523
+ },
2524
+ },
2525
+ },
2526
+ };
2527
+ const PRAXIS_CHART_BACKEND_MOCK_MULTI_METRIC_BAR = {
2528
+ schemaMeta: {
2529
+ schemaId: 'api/human-resources/vw-analytics-folha-pagamento|post|response|tenant:demo|locale:pt-BR',
2530
+ schemaHash: 'published-hash-payroll-multi-metric-bar-v1',
2531
+ resourcePath: 'api/human-resources/vw-analytics-folha-pagamento',
2532
+ operation: 'post',
2533
+ schemaType: 'response',
2534
+ source: 'backend',
2535
+ },
2536
+ widget: {
2537
+ kind: 'x-ui.chart',
2538
+ key: 'payroll-multi-metric-bar',
2539
+ layout: { col: 1, row: 13, colSpan: 12, rowSpan: 4 },
2540
+ shell: {
2541
+ title: 'Comparativo por departamento',
2542
+ subtitle: 'Bar chart multi-metrico sobre praxis.stats group-by',
2543
+ },
2544
+ chart: {
2545
+ version: '0.1.0',
2546
+ kind: 'bar',
2547
+ preset: 'comparison',
2548
+ chartId: 'payroll-multi-metric-bar',
2549
+ title: { key: 'charts.payroll.multiMetric.title', fallback: 'Comparativo por departamento' },
2550
+ subtitle: {
2551
+ key: 'charts.payroll.multiMetric.subtitle',
2552
+ fallback: 'Massa liquida e desconto medio via group-by sem eixo secundario',
2553
+ },
2554
+ height: 340,
2555
+ source: {
2556
+ kind: 'praxis.stats',
2557
+ resource: 'api/human-resources/vw-analytics-folha-pagamento',
2558
+ operation: 'group-by',
2559
+ options: {
2560
+ orderBy: 'value-desc',
2561
+ limit: 8,
2562
+ },
2563
+ },
2564
+ dimensions: [
2565
+ {
2566
+ field: 'departamento',
2567
+ label: 'Departamento',
2568
+ role: 'category',
2569
+ },
2570
+ ],
2571
+ metrics: [
2572
+ {
2573
+ field: 'salarioLiquido',
2574
+ label: 'Massa liquida',
2575
+ aggregation: 'sum',
2576
+ color: '#1263b4',
2577
+ },
2578
+ {
2579
+ field: 'pctDesconto',
2580
+ label: 'Desconto medio',
2581
+ aggregation: 'avg',
2582
+ color: '#0f766e',
2583
+ },
2584
+ ],
2585
+ legend: { enabled: true },
2586
+ tooltip: { enabled: true },
2587
+ theme: {
2588
+ palette: ['#1263b4', '#0f766e'],
2589
+ },
2590
+ },
2591
+ },
2592
+ };
2593
+ const PRAXIS_CHART_BACKEND_MOCK_SCATTER = {
2594
+ schemaMeta: {
2595
+ schemaId: 'api/human-resources/vw-analytics-folha-pagamento|post|response|tenant:demo|locale:pt-BR',
2596
+ schemaHash: 'published-hash-payroll-scatter-v1',
2597
+ resourcePath: 'api/human-resources/vw-analytics-folha-pagamento',
2598
+ operation: 'post',
2599
+ schemaType: 'response',
2600
+ source: 'backend',
2601
+ },
2602
+ widget: {
2603
+ kind: 'x-ui.chart',
2604
+ key: 'payroll-net-vs-discount-scatter',
2605
+ layout: { col: 1, row: 13, colSpan: 12, rowSpan: 4 },
2606
+ shell: {
2607
+ title: 'Liquido x desconto medio',
2608
+ subtitle: 'Dispersao inicial sobre payroll analytics',
2609
+ },
2610
+ chart: {
2611
+ version: '0.1.0',
2612
+ kind: 'scatter',
2613
+ preset: 'comparison',
2614
+ chartId: 'payroll-net-vs-discount-scatter',
2615
+ title: { key: 'charts.payroll.scatter.title', fallback: 'Liquido x desconto medio' },
2616
+ subtitle: { key: 'charts.payroll.scatter.subtitle', fallback: 'Scatter sobre recortes do payroll' },
2617
+ height: 340,
2618
+ source: {
2619
+ kind: 'praxis.stats',
2620
+ resource: 'api/human-resources/vw-analytics-folha-pagamento',
2621
+ operation: 'group-by',
2622
+ options: {
2623
+ orderBy: 'key-asc',
2624
+ limit: 25,
2625
+ },
2626
+ },
2627
+ dimensions: [
2628
+ {
2629
+ field: 'salarioLiquido',
2630
+ label: 'Salario liquido',
2631
+ role: 'value',
2632
+ },
2633
+ ],
2634
+ metrics: [
2635
+ {
2636
+ field: 'pctDesconto',
2637
+ label: 'Percentual medio de desconto',
2638
+ aggregation: 'avg',
2639
+ },
2640
+ ],
2641
+ legend: { enabled: false },
2642
+ tooltip: { enabled: true },
2643
+ theme: {
2644
+ palette: ['#c92a2a'],
2645
+ },
2646
+ },
2647
+ },
2648
+ };
2649
+ const PRAXIS_CHART_BACKEND_MOCK_COMBO = {
2650
+ schemaMeta: {
2651
+ schemaId: 'api/human-resources/vw-analytics-folha-pagamento|post|response|tenant:demo|locale:pt-BR',
2652
+ schemaHash: 'published-hash-payroll-combo-v2',
2653
+ resourcePath: 'api/human-resources/vw-analytics-folha-pagamento',
2654
+ operation: 'post',
2655
+ schemaType: 'response',
2656
+ source: 'backend',
2657
+ },
2658
+ widget: {
2659
+ kind: 'x-ui.chart',
2660
+ key: 'payroll-combo-variance',
2661
+ layout: { col: 1, row: 17, colSpan: 12, rowSpan: 4 },
2662
+ shell: {
2663
+ title: 'Folha liquida e desconto medio',
2664
+ subtitle: 'Combo remoto sobre praxis.stats multi-metrica',
2665
+ },
2666
+ chart: {
2667
+ version: '0.1.0',
2668
+ kind: 'combo',
2669
+ preset: 'comparison',
2670
+ chartId: 'payroll-combo-variance',
2671
+ title: { key: 'charts.payroll.combo.title', fallback: 'Folha liquida e desconto medio' },
2672
+ subtitle: { key: 'charts.payroll.combo.subtitle', fallback: 'Barras para liquido e linha para desconto medio via praxis.stats' },
2673
+ height: 340,
2674
+ source: {
2675
+ kind: 'praxis.stats',
2676
+ resource: 'api/human-resources/vw-analytics-folha-pagamento',
2677
+ operation: 'timeseries',
2678
+ options: {
2679
+ granularity: 'month',
2680
+ fillGaps: true,
2681
+ },
2682
+ },
2683
+ dimensions: [
2684
+ {
2685
+ field: 'competencia',
2686
+ label: 'Competencia',
2687
+ role: 'time',
2688
+ },
2689
+ ],
2690
+ metrics: [
2691
+ {
2692
+ field: 'salarioLiquido',
2693
+ label: 'Massa liquida',
2694
+ aggregation: 'sum',
2695
+ seriesKind: 'bar',
2696
+ axis: 'primary',
2697
+ color: '#1263b4',
2698
+ },
2699
+ {
2700
+ field: 'pctDesconto',
2701
+ label: 'Desconto medio',
2702
+ aggregation: 'avg',
2703
+ seriesKind: 'line',
2704
+ axis: 'secondary',
2705
+ color: '#c92a2a',
2706
+ },
2707
+ ],
2708
+ legend: { enabled: true },
2709
+ tooltip: { enabled: true },
2710
+ theme: {
2711
+ palette: ['#1263b4', '#c92a2a'],
2712
+ },
2713
+ },
2714
+ },
2715
+ };
2110
2716
 
2111
2717
  function buildPraxisChartMockWidgetPage(adapter) {
2112
2718
  return {
@@ -2114,6 +2720,11 @@ function buildPraxisChartMockWidgetPage(adapter) {
2114
2720
  withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_BAR)),
2115
2721
  withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_TIMESERIES)),
2116
2722
  withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_DONUT)),
2723
+ withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_HORIZONTAL_BAR)),
2724
+ withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_STACKED_AREA)),
2725
+ withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_MULTI_METRIC_BAR)),
2726
+ withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_SCATTER)),
2727
+ withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_COMBO)),
2117
2728
  ],
2118
2729
  layout: {
2119
2730
  orientation: 'columns',
@@ -2128,6 +2739,11 @@ function buildPraxisChartMockGridPage(adapter) {
2128
2739
  withShowcaseViewToggle(adapter.toGridWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_BAR)),
2129
2740
  withShowcaseViewToggle(adapter.toGridWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_TIMESERIES)),
2130
2741
  withShowcaseViewToggle(adapter.toGridWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_DONUT)),
2742
+ withShowcaseViewToggle(adapter.toGridWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_HORIZONTAL_BAR)),
2743
+ withShowcaseViewToggle(adapter.toGridWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_STACKED_AREA)),
2744
+ withShowcaseViewToggle(adapter.toGridWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_MULTI_METRIC_BAR)),
2745
+ withShowcaseViewToggle(adapter.toGridWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_SCATTER)),
2746
+ withShowcaseViewToggle(adapter.toGridWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_COMBO)),
2131
2747
  ],
2132
2748
  options: {
2133
2749
  cols: 12,
@@ -2141,6 +2757,7 @@ function buildPraxisChartInteractiveWidgetPage(adapter) {
2141
2757
  widgets: [
2142
2758
  withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_BAR)),
2143
2759
  withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_TIMESERIES)),
2760
+ withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_HORIZONTAL_BAR)),
2144
2761
  {
2145
2762
  key: 'chart-drilldown-panel',
2146
2763
  definition: {
@@ -2170,6 +2787,10 @@ function buildPraxisChartInteractiveWidgetPage(adapter) {
2170
2787
  },
2171
2788
  },
2172
2789
  withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_DONUT)),
2790
+ withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_STACKED_AREA)),
2791
+ withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_MULTI_METRIC_BAR)),
2792
+ withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_SCATTER)),
2793
+ withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_COMBO)),
2173
2794
  ],
2174
2795
  connections: [
2175
2796
  {
@@ -2208,6 +2829,41 @@ function buildPraxisChartInteractiveGridPage(adapter) {
2208
2829
  layout: { col: 7, row: 5, colSpan: 6, rowSpan: 4 },
2209
2830
  },
2210
2831
  })),
2832
+ withShowcaseViewToggle(adapter.toGridWidgetInstance({
2833
+ ...PRAXIS_CHART_BACKEND_MOCK_HORIZONTAL_BAR,
2834
+ widget: {
2835
+ ...PRAXIS_CHART_BACKEND_MOCK_HORIZONTAL_BAR.widget,
2836
+ layout: { col: 1, row: 9, colSpan: 6, rowSpan: 4 },
2837
+ },
2838
+ })),
2839
+ withShowcaseViewToggle(adapter.toGridWidgetInstance({
2840
+ ...PRAXIS_CHART_BACKEND_MOCK_STACKED_AREA,
2841
+ widget: {
2842
+ ...PRAXIS_CHART_BACKEND_MOCK_STACKED_AREA.widget,
2843
+ layout: { col: 1, row: 13, colSpan: 12, rowSpan: 4 },
2844
+ },
2845
+ })),
2846
+ withShowcaseViewToggle(adapter.toGridWidgetInstance({
2847
+ ...PRAXIS_CHART_BACKEND_MOCK_MULTI_METRIC_BAR,
2848
+ widget: {
2849
+ ...PRAXIS_CHART_BACKEND_MOCK_MULTI_METRIC_BAR.widget,
2850
+ layout: { col: 1, row: 17, colSpan: 12, rowSpan: 4 },
2851
+ },
2852
+ })),
2853
+ withShowcaseViewToggle(adapter.toGridWidgetInstance({
2854
+ ...PRAXIS_CHART_BACKEND_MOCK_SCATTER,
2855
+ widget: {
2856
+ ...PRAXIS_CHART_BACKEND_MOCK_SCATTER.widget,
2857
+ layout: { col: 1, row: 21, colSpan: 12, rowSpan: 4 },
2858
+ },
2859
+ })),
2860
+ withShowcaseViewToggle(adapter.toGridWidgetInstance({
2861
+ ...PRAXIS_CHART_BACKEND_MOCK_COMBO,
2862
+ widget: {
2863
+ ...PRAXIS_CHART_BACKEND_MOCK_COMBO.widget,
2864
+ layout: { col: 1, row: 25, colSpan: 12, rowSpan: 4 },
2865
+ },
2866
+ })),
2211
2867
  ],
2212
2868
  connections: [
2213
2869
  {
@@ -2429,6 +3085,16 @@ class PraxisChartCompositionShowcaseComponent {
2429
3085
  return 'Envelope timeseries';
2430
3086
  case 'distribution':
2431
3087
  return 'Envelope distribution';
3088
+ case 'horizontal-bar':
3089
+ return 'Envelope horizontal-bar';
3090
+ case 'stacked-area':
3091
+ return 'Envelope stacked-area';
3092
+ case 'multi-metric-bar':
3093
+ return 'Envelope multi-metric bar';
3094
+ case 'scatter':
3095
+ return 'Envelope scatter';
3096
+ case 'combo':
3097
+ return 'Envelope combo';
2432
3098
  case 'group-by':
2433
3099
  default:
2434
3100
  return 'Envelope group-by';
@@ -2441,6 +3107,16 @@ class PraxisChartCompositionShowcaseComponent {
2441
3107
  return PRAXIS_CHART_BACKEND_MOCK_TIMESERIES;
2442
3108
  case 'distribution':
2443
3109
  return PRAXIS_CHART_BACKEND_MOCK_DONUT;
3110
+ case 'horizontal-bar':
3111
+ return PRAXIS_CHART_BACKEND_MOCK_HORIZONTAL_BAR;
3112
+ case 'stacked-area':
3113
+ return PRAXIS_CHART_BACKEND_MOCK_STACKED_AREA;
3114
+ case 'multi-metric-bar':
3115
+ return PRAXIS_CHART_BACKEND_MOCK_MULTI_METRIC_BAR;
3116
+ case 'scatter':
3117
+ return PRAXIS_CHART_BACKEND_MOCK_SCATTER;
3118
+ case 'combo':
3119
+ return PRAXIS_CHART_BACKEND_MOCK_COMBO;
2444
3120
  case 'group-by':
2445
3121
  default:
2446
3122
  return PRAXIS_CHART_BACKEND_MOCK_BAR;
@@ -2485,6 +3161,21 @@ class PraxisChartCompositionShowcaseComponent {
2485
3161
  <button type="button" [class.active]="payloadMode() === 'distribution'" (click)="payloadMode.set('distribution')">
2486
3162
  Distribution
2487
3163
  </button>
3164
+ <button type="button" [class.active]="payloadMode() === 'horizontal-bar'" (click)="payloadMode.set('horizontal-bar')">
3165
+ Horizontal bar
3166
+ </button>
3167
+ <button type="button" [class.active]="payloadMode() === 'stacked-area'" (click)="payloadMode.set('stacked-area')">
3168
+ Stacked area
3169
+ </button>
3170
+ <button type="button" [class.active]="payloadMode() === 'multi-metric-bar'" (click)="payloadMode.set('multi-metric-bar')">
3171
+ Multi-metric bar
3172
+ </button>
3173
+ <button type="button" [class.active]="payloadMode() === 'scatter'" (click)="payloadMode.set('scatter')">
3174
+ Scatter
3175
+ </button>
3176
+ <button type="button" [class.active]="payloadMode() === 'combo'" (click)="payloadMode.set('combo')">
3177
+ Combo
3178
+ </button>
2488
3179
  </div>
2489
3180
  </div>
2490
3181
 
@@ -2518,7 +3209,7 @@ class PraxisChartCompositionShowcaseComponent {
2518
3209
  <p class="panel-kicker">Runtime</p>
2519
3210
  <h3>{{ layoutMode() === 'widget' ? 'DynamicWidgetPage' : 'DynamicGridPage' }}</h3>
2520
3211
  </div>
2521
- <span class="panel-chip">{{ layoutMode() }} · {{ scenarioMode() }}</span>
3212
+ <span class="panel-chip">{{ layoutMode() }} / {{ scenarioMode() }}</span>
2522
3213
  </div>
2523
3214
 
2524
3215
  @if (layoutMode() === 'widget') {
@@ -2587,6 +3278,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
2587
3278
  <button type="button" [class.active]="payloadMode() === 'distribution'" (click)="payloadMode.set('distribution')">
2588
3279
  Distribution
2589
3280
  </button>
3281
+ <button type="button" [class.active]="payloadMode() === 'horizontal-bar'" (click)="payloadMode.set('horizontal-bar')">
3282
+ Horizontal bar
3283
+ </button>
3284
+ <button type="button" [class.active]="payloadMode() === 'stacked-area'" (click)="payloadMode.set('stacked-area')">
3285
+ Stacked area
3286
+ </button>
3287
+ <button type="button" [class.active]="payloadMode() === 'multi-metric-bar'" (click)="payloadMode.set('multi-metric-bar')">
3288
+ Multi-metric bar
3289
+ </button>
3290
+ <button type="button" [class.active]="payloadMode() === 'scatter'" (click)="payloadMode.set('scatter')">
3291
+ Scatter
3292
+ </button>
3293
+ <button type="button" [class.active]="payloadMode() === 'combo'" (click)="payloadMode.set('combo')">
3294
+ Combo
3295
+ </button>
2590
3296
  </div>
2591
3297
  </div>
2592
3298
 
@@ -2620,7 +3326,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
2620
3326
  <p class="panel-kicker">Runtime</p>
2621
3327
  <h3>{{ layoutMode() === 'widget' ? 'DynamicWidgetPage' : 'DynamicGridPage' }}</h3>
2622
3328
  </div>
2623
- <span class="panel-chip">{{ layoutMode() }} · {{ scenarioMode() }}</span>
3329
+ <span class="panel-chip">{{ layoutMode() }} / {{ scenarioMode() }}</span>
2624
3330
  </div>
2625
3331
 
2626
3332
  @if (layoutMode() === 'widget') {
@@ -2658,5 +3364,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
2658
3364
  * Generated bundle index. Do not edit.
2659
3365
  */
2660
3366
 
2661
- export { PRAXIS_CHART_BACKEND_MOCK_BAR, PRAXIS_CHART_BACKEND_MOCK_DONUT, PRAXIS_CHART_BACKEND_MOCK_TIMESERIES, PRAXIS_CHART_COMPONENT_METADATA, PRAXIS_CHART_DRILLDOWN_DATA_BY_MONTH, PRAXIS_CHART_DRILLDOWN_PANEL_METADATA, PRAXIS_CHART_ENGINE, PRAXIS_CHART_STATE_PROBE_COMPONENT_METADATA, PraxisChartBackendPayloadAdapterService, PraxisChartCanonicalContractMapperService, PraxisChartComponent, PraxisChartCompositionShowcaseComponent, PraxisChartDataTransformerService, PraxisChartDrilldownPanelComponent, PraxisChartOptionBuilderService, PraxisChartSchemaMapperService, PraxisChartStateProbeComponent, PraxisChartStatsApiService, buildPraxisChartInteractiveGridPage, buildPraxisChartInteractiveWidgetPage, buildPraxisChartMockGridPage, buildPraxisChartMockWidgetPage, providePraxisChartDrilldownPanelMetadata, providePraxisChartStateProbeMetadata, providePraxisCharts, providePraxisChartsMetadata };
3367
+ export { PRAXIS_CHART_BACKEND_MOCK_BAR, PRAXIS_CHART_BACKEND_MOCK_COMBO, PRAXIS_CHART_BACKEND_MOCK_DONUT, PRAXIS_CHART_BACKEND_MOCK_HORIZONTAL_BAR, PRAXIS_CHART_BACKEND_MOCK_MULTI_METRIC_BAR, PRAXIS_CHART_BACKEND_MOCK_SCATTER, PRAXIS_CHART_BACKEND_MOCK_STACKED_AREA, PRAXIS_CHART_BACKEND_MOCK_TIMESERIES, PRAXIS_CHART_COMPONENT_METADATA, PRAXIS_CHART_DRILLDOWN_DATA_BY_MONTH, PRAXIS_CHART_DRILLDOWN_PANEL_METADATA, PRAXIS_CHART_ENGINE, PRAXIS_CHART_STATE_PROBE_COMPONENT_METADATA, PraxisChartBackendPayloadAdapterService, PraxisChartCanonicalContractMapperService, PraxisChartComponent, PraxisChartCompositionShowcaseComponent, PraxisChartDataTransformerService, PraxisChartDrilldownPanelComponent, PraxisChartOptionBuilderService, PraxisChartSchemaMapperService, PraxisChartStateProbeComponent, PraxisChartStatsApiService, buildPraxisChartInteractiveGridPage, buildPraxisChartInteractiveWidgetPage, buildPraxisChartMockGridPage, buildPraxisChartMockWidgetPage, providePraxisChartDrilldownPanelMetadata, providePraxisChartStateProbeMetadata, providePraxisCharts, providePraxisChartsMetadata };
2662
3368
  //# sourceMappingURL=praxisui-charts.mjs.map