@praxisui/charts 1.0.0-beta.68 → 3.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.
@@ -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();
@@ -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,9 +1607,31 @@ 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) {
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.source.kind === 'praxis.stats' && contract.metrics.length > 1 && contract.kind !== 'combo') {
1406
1621
  throw new Error('x-ui.chart praxis.stats currently supports a single metric per chart in @praxisui/charts.');
1407
1622
  }
1623
+ if (contract.kind === 'horizontal-bar' && contract.orientation && contract.orientation !== 'horizontal') {
1624
+ throw new Error('x-ui.chart kind="horizontal-bar" requires orientation="horizontal" when orientation is provided.');
1625
+ }
1626
+ if (contract.kind === 'scatter') {
1627
+ const firstDimension = contract.dimensions?.[0];
1628
+ if (!firstDimension?.field) {
1629
+ throw new Error('x-ui.chart scatter charts require dimensions[0].field for the x axis.');
1630
+ }
1631
+ if (!contract.metrics?.[0]?.field) {
1632
+ throw new Error('x-ui.chart scatter charts require metrics[0].field for the y axis.');
1633
+ }
1634
+ }
1408
1635
  if (contract.events?.selectionChange || contract.events?.crossFilter) {
1409
1636
  throw new Error('x-ui.chart selectionChange/crossFilter declarative runtime actions are not yet implemented in @praxisui/charts.');
1410
1637
  }
@@ -1412,6 +1639,7 @@ class PraxisChartCanonicalContractMapperService {
1412
1639
  buildAxes(contract) {
1413
1640
  const firstDimension = contract.dimensions?.[0];
1414
1641
  const firstMetric = contract.metrics?.[0];
1642
+ const secondaryMetric = contract.metrics?.find((metric) => metric.axis === 'secondary');
1415
1643
  if (contract.kind === 'pie' || contract.kind === 'donut') {
1416
1644
  return {
1417
1645
  x: {
@@ -1420,6 +1648,20 @@ class PraxisChartCanonicalContractMapperService {
1420
1648
  },
1421
1649
  };
1422
1650
  }
1651
+ if (contract.kind === 'scatter') {
1652
+ return {
1653
+ x: {
1654
+ field: firstDimension?.field,
1655
+ label: this.toLabel(firstDimension?.label),
1656
+ type: firstDimension?.role === 'time' ? 'time' : 'value',
1657
+ },
1658
+ y: {
1659
+ field: firstMetric?.field,
1660
+ label: this.toLabel(firstMetric?.label),
1661
+ type: 'value',
1662
+ },
1663
+ };
1664
+ }
1423
1665
  return {
1424
1666
  x: {
1425
1667
  field: firstDimension?.field,
@@ -1430,6 +1672,13 @@ class PraxisChartCanonicalContractMapperService {
1430
1672
  label: this.toLabel(firstMetric?.label),
1431
1673
  type: 'value',
1432
1674
  },
1675
+ ySecondary: contract.kind === 'combo' && secondaryMetric
1676
+ ? {
1677
+ label: this.toLabel(secondaryMetric.label),
1678
+ type: 'value',
1679
+ position: 'right',
1680
+ }
1681
+ : undefined,
1433
1682
  };
1434
1683
  }
1435
1684
  buildSeries(contract) {
@@ -1438,19 +1687,24 @@ class PraxisChartCanonicalContractMapperService {
1438
1687
  return (contract.metrics ?? []).map((metric, index) => ({
1439
1688
  id: `${metric.field}-${index + 1}`,
1440
1689
  name: this.toLabel(metric.label) ?? metric.field,
1441
- type: contract.kind === 'stacked-bar' ? 'bar' : contract.kind,
1690
+ type: this.resolveSeriesType(contract.kind, metric.seriesKind, index),
1691
+ axis: metric.axis ?? 'primary',
1442
1692
  categoryField: contract.kind === 'pie' || contract.kind === 'donut' ? firstDimension?.field : undefined,
1443
1693
  metric: {
1444
1694
  field: metric.field,
1445
1695
  aggregation: this.mapAggregation(metric.aggregation),
1446
1696
  label: this.toLabel(metric.label),
1447
1697
  },
1448
- stackId: contract.kind === 'stacked-bar' ? 'stack-1' : undefined,
1698
+ color: metric.color,
1699
+ stackId: contract.kind === 'stacked-bar' || contract.kind === 'stacked-area' ? 'stack-1' : undefined,
1449
1700
  labels: labelsVisible ? { visible: true } : undefined,
1450
- smooth: contract.kind === 'line' || contract.kind === 'area' ? true : undefined,
1701
+ smooth: this.shouldSmoothSeries(contract.kind, metric.seriesKind),
1451
1702
  }));
1452
1703
  }
1453
1704
  buildDataSource(contract) {
1705
+ if (contract.source.kind === 'derived') {
1706
+ return undefined;
1707
+ }
1454
1708
  return {
1455
1709
  kind: 'remote',
1456
1710
  resourcePath: contract.source.resource,
@@ -1459,6 +1713,9 @@ class PraxisChartCanonicalContractMapperService {
1459
1713
  };
1460
1714
  }
1461
1715
  buildQuery(contract) {
1716
+ if (contract.source.kind !== 'praxis.stats') {
1717
+ return undefined;
1718
+ }
1462
1719
  const filtersFromContract = this.toQueryFilterMap(contract.filters);
1463
1720
  const combinedFilters = {
1464
1721
  ...filtersFromContract,
@@ -1498,10 +1755,28 @@ class PraxisChartCanonicalContractMapperService {
1498
1755
  legend: { visible: this.resolveToggle(contract.legend, true) },
1499
1756
  tooltip: {
1500
1757
  enabled: this.resolveToggle(contract.tooltip, true),
1501
- trigger: contract.kind === 'pie' || contract.kind === 'donut' ? 'item' : 'axis',
1758
+ trigger: contract.kind === 'pie' || contract.kind === 'donut' || contract.kind === 'scatter' ? 'item' : 'axis',
1502
1759
  },
1503
1760
  };
1504
1761
  }
1762
+ resolveOrientation(contract) {
1763
+ if (contract.kind === 'horizontal-bar') {
1764
+ return 'horizontal';
1765
+ }
1766
+ return contract.orientation;
1767
+ }
1768
+ resolveSeriesType(chartKind, seriesKind, index) {
1769
+ if (chartKind === 'combo') {
1770
+ return seriesKind ?? (index === 0 ? 'bar' : 'line');
1771
+ }
1772
+ return chartKind === 'stacked-bar' ? 'bar' : chartKind;
1773
+ }
1774
+ shouldSmoothSeries(chartKind, seriesKind) {
1775
+ if (chartKind === 'combo') {
1776
+ return seriesKind === 'line' || seriesKind === 'area' ? true : undefined;
1777
+ }
1778
+ return chartKind === 'line' || chartKind === 'area' || chartKind === 'stacked-area' ? true : undefined;
1779
+ }
1505
1780
  mapTextValue(value) {
1506
1781
  if (!value)
1507
1782
  return undefined;
@@ -1559,15 +1834,18 @@ class PraxisChartCanonicalContractMapperService {
1559
1834
  }
1560
1835
  buildStatsRequest(contract, filters) {
1561
1836
  const field = this.resolveStatsField(contract);
1562
- const metric = this.buildStatsMetricRequest(contract);
1837
+ const metrics = this.buildStatsMetricRequests(contract);
1838
+ const metric = metrics[0];
1563
1839
  const limit = contract.limit ?? contract.source.options?.limit;
1564
1840
  const orderBy = this.mapStatsOrderByToBackend(contract.source.options?.orderBy);
1841
+ const requestMetrics = metrics.length > 1 ? metrics : undefined;
1565
1842
  switch (contract.source.operation) {
1566
1843
  case 'group-by':
1567
1844
  return {
1568
1845
  filter: filters,
1569
1846
  field,
1570
1847
  metric,
1848
+ metrics: requestMetrics,
1571
1849
  limit,
1572
1850
  orderBy,
1573
1851
  };
@@ -1577,6 +1855,7 @@ class PraxisChartCanonicalContractMapperService {
1577
1855
  field,
1578
1856
  granularity: this.mapStatsGranularityToBackend(contract.source.options?.granularity),
1579
1857
  metric,
1858
+ metrics: requestMetrics,
1580
1859
  fillGaps: contract.source.options?.fillGaps,
1581
1860
  };
1582
1861
  case 'distribution':
@@ -1610,8 +1889,22 @@ class PraxisChartCanonicalContractMapperService {
1610
1889
  return {
1611
1890
  operation,
1612
1891
  field: operation === 'COUNT' ? undefined : metric.field,
1892
+ alias: metric.field,
1613
1893
  };
1614
1894
  }
1895
+ buildStatsMetricRequests(contract) {
1896
+ if (!contract.metrics?.length) {
1897
+ throw new Error('x-ui.chart requires at least one metric to derive the canonical praxis.stats request.');
1898
+ }
1899
+ return contract.metrics.map((metric) => {
1900
+ const operation = this.mapStatsMetricOperation(metric.aggregation);
1901
+ return {
1902
+ operation,
1903
+ field: operation === 'COUNT' ? undefined : metric.field,
1904
+ alias: metric.field,
1905
+ };
1906
+ });
1907
+ }
1615
1908
  mapStatsOrderBy(value) {
1616
1909
  if (!value)
1617
1910
  return undefined;
@@ -2007,7 +2300,7 @@ const PRAXIS_CHART_BACKEND_MOCK_TIMESERIES = {
2007
2300
  widget: {
2008
2301
  kind: 'x-ui.chart',
2009
2302
  key: 'incident-timeseries-chart',
2010
- layout: { col: 7, row: 1, colSpan: 5, rowSpan: 4 },
2303
+ layout: { col: 7, row: 1, colSpan: 6, rowSpan: 4 },
2011
2304
  shell: {
2012
2305
  title: 'Incidentes por mes',
2013
2306
  subtitle: 'Time series real consumindo praxis.stats publicado',
@@ -2063,7 +2356,7 @@ const PRAXIS_CHART_BACKEND_MOCK_DONUT = {
2063
2356
  widget: {
2064
2357
  kind: 'x-ui.chart',
2065
2358
  key: 'ticket-status-chart',
2066
- layout: { col: 7, row: 1, colSpan: 5, rowSpan: 4 },
2359
+ layout: { col: 7, row: 5, colSpan: 6, rowSpan: 4 },
2067
2360
  shell: {
2068
2361
  title: 'Severidade de incidentes',
2069
2362
  subtitle: 'Distribution real consumindo praxis.stats publicado',
@@ -2107,6 +2400,247 @@ const PRAXIS_CHART_BACKEND_MOCK_DONUT = {
2107
2400
  },
2108
2401
  },
2109
2402
  };
2403
+ const PRAXIS_CHART_BACKEND_MOCK_HORIZONTAL_BAR = {
2404
+ schemaMeta: {
2405
+ schemaId: 'api/human-resources/vw-analytics-folha-pagamento|post|response|tenant:demo|locale:pt-BR',
2406
+ schemaHash: 'published-hash-payroll-department-ranking-v1',
2407
+ resourcePath: 'api/human-resources/vw-analytics-folha-pagamento',
2408
+ operation: 'post',
2409
+ schemaType: 'response',
2410
+ source: 'backend',
2411
+ },
2412
+ widget: {
2413
+ kind: 'x-ui.chart',
2414
+ key: 'payroll-department-horizontal-bar',
2415
+ layout: { col: 1, row: 5, colSpan: 6, rowSpan: 4 },
2416
+ shell: {
2417
+ title: 'Massa salarial por departamento',
2418
+ subtitle: 'Ranking horizontal em payroll analytics',
2419
+ },
2420
+ chart: {
2421
+ version: '0.1.0',
2422
+ kind: 'horizontal-bar',
2423
+ preset: 'ranking',
2424
+ chartId: 'payroll-department-horizontal-bar',
2425
+ title: { key: 'charts.payroll.department.title', fallback: 'Massa salarial por departamento' },
2426
+ subtitle: { key: 'charts.payroll.department.subtitle', fallback: 'Ranking horizontal consumindo praxis.stats' },
2427
+ orientation: 'horizontal',
2428
+ height: 340,
2429
+ source: {
2430
+ kind: 'praxis.stats',
2431
+ resource: 'api/human-resources/vw-analytics-folha-pagamento',
2432
+ operation: 'group-by',
2433
+ options: {
2434
+ orderBy: 'value-desc',
2435
+ limit: 8,
2436
+ },
2437
+ },
2438
+ dimensions: [
2439
+ {
2440
+ field: 'departamento',
2441
+ label: 'Departamento',
2442
+ role: 'category',
2443
+ },
2444
+ ],
2445
+ metrics: [
2446
+ {
2447
+ field: 'salarioLiquido',
2448
+ label: 'Massa liquida',
2449
+ aggregation: 'sum',
2450
+ },
2451
+ ],
2452
+ legend: { enabled: false },
2453
+ tooltip: { enabled: true },
2454
+ theme: {
2455
+ palette: ['#0f766e'],
2456
+ },
2457
+ events: {
2458
+ pointClick: {
2459
+ action: 'emit',
2460
+ },
2461
+ },
2462
+ },
2463
+ },
2464
+ };
2465
+ const PRAXIS_CHART_BACKEND_MOCK_STACKED_AREA = {
2466
+ schemaMeta: {
2467
+ schemaId: 'api/human-resources/vw-analytics-folha-pagamento|post|response|tenant:demo|locale:pt-BR',
2468
+ schemaHash: 'published-hash-payroll-net-trend-stacked-v1',
2469
+ resourcePath: 'api/human-resources/vw-analytics-folha-pagamento',
2470
+ operation: 'post',
2471
+ schemaType: 'response',
2472
+ source: 'backend',
2473
+ },
2474
+ widget: {
2475
+ kind: 'x-ui.chart',
2476
+ key: 'payroll-net-stacked-area',
2477
+ layout: { col: 1, row: 9, colSpan: 12, rowSpan: 4 },
2478
+ shell: {
2479
+ title: 'Composicao temporal da folha',
2480
+ subtitle: 'Stacked area inicial sobre timeseries published',
2481
+ },
2482
+ chart: {
2483
+ version: '0.1.0',
2484
+ kind: 'stacked-area',
2485
+ preset: 'composition',
2486
+ chartId: 'payroll-net-stacked-area',
2487
+ title: { key: 'charts.payroll.stackedArea.title', fallback: 'Composicao temporal da folha' },
2488
+ subtitle: { key: 'charts.payroll.stackedArea.subtitle', fallback: 'Stacked area sobre payroll analytics' },
2489
+ height: 340,
2490
+ source: {
2491
+ kind: 'praxis.stats',
2492
+ resource: 'api/human-resources/vw-analytics-folha-pagamento',
2493
+ operation: 'timeseries',
2494
+ options: {
2495
+ granularity: 'month',
2496
+ fillGaps: false,
2497
+ },
2498
+ },
2499
+ dimensions: [
2500
+ {
2501
+ field: 'competencia',
2502
+ label: 'Competencia',
2503
+ role: 'time',
2504
+ },
2505
+ ],
2506
+ metrics: [
2507
+ {
2508
+ field: 'salarioLiquido',
2509
+ label: 'Massa liquida',
2510
+ aggregation: 'sum',
2511
+ },
2512
+ ],
2513
+ legend: { enabled: true },
2514
+ tooltip: { enabled: true },
2515
+ theme: {
2516
+ palette: ['#1263b4'],
2517
+ },
2518
+ },
2519
+ },
2520
+ };
2521
+ const PRAXIS_CHART_BACKEND_MOCK_SCATTER = {
2522
+ schemaMeta: {
2523
+ schemaId: 'api/human-resources/vw-analytics-folha-pagamento|post|response|tenant:demo|locale:pt-BR',
2524
+ schemaHash: 'published-hash-payroll-scatter-v1',
2525
+ resourcePath: 'api/human-resources/vw-analytics-folha-pagamento',
2526
+ operation: 'post',
2527
+ schemaType: 'response',
2528
+ source: 'backend',
2529
+ },
2530
+ widget: {
2531
+ kind: 'x-ui.chart',
2532
+ key: 'payroll-net-vs-discount-scatter',
2533
+ layout: { col: 1, row: 13, colSpan: 12, rowSpan: 4 },
2534
+ shell: {
2535
+ title: 'Liquido x desconto medio',
2536
+ subtitle: 'Dispersao inicial sobre payroll analytics',
2537
+ },
2538
+ chart: {
2539
+ version: '0.1.0',
2540
+ kind: 'scatter',
2541
+ preset: 'comparison',
2542
+ chartId: 'payroll-net-vs-discount-scatter',
2543
+ title: { key: 'charts.payroll.scatter.title', fallback: 'Liquido x desconto medio' },
2544
+ subtitle: { key: 'charts.payroll.scatter.subtitle', fallback: 'Scatter sobre recortes do payroll' },
2545
+ height: 340,
2546
+ source: {
2547
+ kind: 'praxis.stats',
2548
+ resource: 'api/human-resources/vw-analytics-folha-pagamento',
2549
+ operation: 'group-by',
2550
+ options: {
2551
+ orderBy: 'key-asc',
2552
+ limit: 25,
2553
+ },
2554
+ },
2555
+ dimensions: [
2556
+ {
2557
+ field: 'salarioLiquido',
2558
+ label: 'Salario liquido',
2559
+ role: 'value',
2560
+ },
2561
+ ],
2562
+ metrics: [
2563
+ {
2564
+ field: 'pctDesconto',
2565
+ label: 'Percentual medio de desconto',
2566
+ aggregation: 'avg',
2567
+ },
2568
+ ],
2569
+ legend: { enabled: false },
2570
+ tooltip: { enabled: true },
2571
+ theme: {
2572
+ palette: ['#c92a2a'],
2573
+ },
2574
+ },
2575
+ },
2576
+ };
2577
+ const PRAXIS_CHART_BACKEND_MOCK_COMBO = {
2578
+ schemaMeta: {
2579
+ schemaId: 'api/human-resources/vw-analytics-folha-pagamento|post|response|tenant:demo|locale:pt-BR',
2580
+ schemaHash: 'published-hash-payroll-combo-v2',
2581
+ resourcePath: 'api/human-resources/vw-analytics-folha-pagamento',
2582
+ operation: 'post',
2583
+ schemaType: 'response',
2584
+ source: 'backend',
2585
+ },
2586
+ widget: {
2587
+ kind: 'x-ui.chart',
2588
+ key: 'payroll-combo-variance',
2589
+ layout: { col: 1, row: 17, colSpan: 12, rowSpan: 4 },
2590
+ shell: {
2591
+ title: 'Folha liquida e desconto medio',
2592
+ subtitle: 'Combo remoto sobre praxis.stats multi-metrica',
2593
+ },
2594
+ chart: {
2595
+ version: '0.1.0',
2596
+ kind: 'combo',
2597
+ preset: 'comparison',
2598
+ chartId: 'payroll-combo-variance',
2599
+ title: { key: 'charts.payroll.combo.title', fallback: 'Folha liquida e desconto medio' },
2600
+ subtitle: { key: 'charts.payroll.combo.subtitle', fallback: 'Barras para liquido e linha para desconto medio via praxis.stats' },
2601
+ height: 340,
2602
+ source: {
2603
+ kind: 'praxis.stats',
2604
+ resource: 'api/human-resources/vw-analytics-folha-pagamento',
2605
+ operation: 'timeseries',
2606
+ options: {
2607
+ granularity: 'month',
2608
+ fillGaps: true,
2609
+ },
2610
+ },
2611
+ dimensions: [
2612
+ {
2613
+ field: 'competencia',
2614
+ label: 'Competencia',
2615
+ role: 'time',
2616
+ },
2617
+ ],
2618
+ metrics: [
2619
+ {
2620
+ field: 'salarioLiquido',
2621
+ label: 'Massa liquida',
2622
+ aggregation: 'sum',
2623
+ seriesKind: 'bar',
2624
+ axis: 'primary',
2625
+ color: '#1263b4',
2626
+ },
2627
+ {
2628
+ field: 'pctDesconto',
2629
+ label: 'Desconto medio',
2630
+ aggregation: 'avg',
2631
+ seriesKind: 'line',
2632
+ axis: 'secondary',
2633
+ color: '#c92a2a',
2634
+ },
2635
+ ],
2636
+ legend: { enabled: true },
2637
+ tooltip: { enabled: true },
2638
+ theme: {
2639
+ palette: ['#1263b4', '#c92a2a'],
2640
+ },
2641
+ },
2642
+ },
2643
+ };
2110
2644
 
2111
2645
  function buildPraxisChartMockWidgetPage(adapter) {
2112
2646
  return {
@@ -2114,6 +2648,10 @@ function buildPraxisChartMockWidgetPage(adapter) {
2114
2648
  withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_BAR)),
2115
2649
  withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_TIMESERIES)),
2116
2650
  withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_DONUT)),
2651
+ withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_HORIZONTAL_BAR)),
2652
+ withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_STACKED_AREA)),
2653
+ withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_SCATTER)),
2654
+ withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_COMBO)),
2117
2655
  ],
2118
2656
  layout: {
2119
2657
  orientation: 'columns',
@@ -2128,6 +2666,10 @@ function buildPraxisChartMockGridPage(adapter) {
2128
2666
  withShowcaseViewToggle(adapter.toGridWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_BAR)),
2129
2667
  withShowcaseViewToggle(adapter.toGridWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_TIMESERIES)),
2130
2668
  withShowcaseViewToggle(adapter.toGridWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_DONUT)),
2669
+ withShowcaseViewToggle(adapter.toGridWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_HORIZONTAL_BAR)),
2670
+ withShowcaseViewToggle(adapter.toGridWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_STACKED_AREA)),
2671
+ withShowcaseViewToggle(adapter.toGridWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_SCATTER)),
2672
+ withShowcaseViewToggle(adapter.toGridWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_COMBO)),
2131
2673
  ],
2132
2674
  options: {
2133
2675
  cols: 12,
@@ -2141,6 +2683,7 @@ function buildPraxisChartInteractiveWidgetPage(adapter) {
2141
2683
  widgets: [
2142
2684
  withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_BAR)),
2143
2685
  withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_TIMESERIES)),
2686
+ withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_HORIZONTAL_BAR)),
2144
2687
  {
2145
2688
  key: 'chart-drilldown-panel',
2146
2689
  definition: {
@@ -2170,6 +2713,9 @@ function buildPraxisChartInteractiveWidgetPage(adapter) {
2170
2713
  },
2171
2714
  },
2172
2715
  withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_DONUT)),
2716
+ withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_STACKED_AREA)),
2717
+ withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_SCATTER)),
2718
+ withShowcaseViewToggle(adapter.toWidgetInstance(PRAXIS_CHART_BACKEND_MOCK_COMBO)),
2173
2719
  ],
2174
2720
  connections: [
2175
2721
  {
@@ -2208,6 +2754,34 @@ function buildPraxisChartInteractiveGridPage(adapter) {
2208
2754
  layout: { col: 7, row: 5, colSpan: 6, rowSpan: 4 },
2209
2755
  },
2210
2756
  })),
2757
+ withShowcaseViewToggle(adapter.toGridWidgetInstance({
2758
+ ...PRAXIS_CHART_BACKEND_MOCK_HORIZONTAL_BAR,
2759
+ widget: {
2760
+ ...PRAXIS_CHART_BACKEND_MOCK_HORIZONTAL_BAR.widget,
2761
+ layout: { col: 1, row: 9, colSpan: 6, rowSpan: 4 },
2762
+ },
2763
+ })),
2764
+ withShowcaseViewToggle(adapter.toGridWidgetInstance({
2765
+ ...PRAXIS_CHART_BACKEND_MOCK_STACKED_AREA,
2766
+ widget: {
2767
+ ...PRAXIS_CHART_BACKEND_MOCK_STACKED_AREA.widget,
2768
+ layout: { col: 1, row: 13, colSpan: 12, rowSpan: 4 },
2769
+ },
2770
+ })),
2771
+ withShowcaseViewToggle(adapter.toGridWidgetInstance({
2772
+ ...PRAXIS_CHART_BACKEND_MOCK_SCATTER,
2773
+ widget: {
2774
+ ...PRAXIS_CHART_BACKEND_MOCK_SCATTER.widget,
2775
+ layout: { col: 1, row: 17, colSpan: 12, rowSpan: 4 },
2776
+ },
2777
+ })),
2778
+ withShowcaseViewToggle(adapter.toGridWidgetInstance({
2779
+ ...PRAXIS_CHART_BACKEND_MOCK_COMBO,
2780
+ widget: {
2781
+ ...PRAXIS_CHART_BACKEND_MOCK_COMBO.widget,
2782
+ layout: { col: 1, row: 21, colSpan: 12, rowSpan: 4 },
2783
+ },
2784
+ })),
2211
2785
  ],
2212
2786
  connections: [
2213
2787
  {
@@ -2429,6 +3003,14 @@ class PraxisChartCompositionShowcaseComponent {
2429
3003
  return 'Envelope timeseries';
2430
3004
  case 'distribution':
2431
3005
  return 'Envelope distribution';
3006
+ case 'horizontal-bar':
3007
+ return 'Envelope horizontal-bar';
3008
+ case 'stacked-area':
3009
+ return 'Envelope stacked-area';
3010
+ case 'scatter':
3011
+ return 'Envelope scatter';
3012
+ case 'combo':
3013
+ return 'Envelope combo';
2432
3014
  case 'group-by':
2433
3015
  default:
2434
3016
  return 'Envelope group-by';
@@ -2441,6 +3023,14 @@ class PraxisChartCompositionShowcaseComponent {
2441
3023
  return PRAXIS_CHART_BACKEND_MOCK_TIMESERIES;
2442
3024
  case 'distribution':
2443
3025
  return PRAXIS_CHART_BACKEND_MOCK_DONUT;
3026
+ case 'horizontal-bar':
3027
+ return PRAXIS_CHART_BACKEND_MOCK_HORIZONTAL_BAR;
3028
+ case 'stacked-area':
3029
+ return PRAXIS_CHART_BACKEND_MOCK_STACKED_AREA;
3030
+ case 'scatter':
3031
+ return PRAXIS_CHART_BACKEND_MOCK_SCATTER;
3032
+ case 'combo':
3033
+ return PRAXIS_CHART_BACKEND_MOCK_COMBO;
2444
3034
  case 'group-by':
2445
3035
  default:
2446
3036
  return PRAXIS_CHART_BACKEND_MOCK_BAR;
@@ -2485,6 +3075,18 @@ class PraxisChartCompositionShowcaseComponent {
2485
3075
  <button type="button" [class.active]="payloadMode() === 'distribution'" (click)="payloadMode.set('distribution')">
2486
3076
  Distribution
2487
3077
  </button>
3078
+ <button type="button" [class.active]="payloadMode() === 'horizontal-bar'" (click)="payloadMode.set('horizontal-bar')">
3079
+ Horizontal bar
3080
+ </button>
3081
+ <button type="button" [class.active]="payloadMode() === 'stacked-area'" (click)="payloadMode.set('stacked-area')">
3082
+ Stacked area
3083
+ </button>
3084
+ <button type="button" [class.active]="payloadMode() === 'scatter'" (click)="payloadMode.set('scatter')">
3085
+ Scatter
3086
+ </button>
3087
+ <button type="button" [class.active]="payloadMode() === 'combo'" (click)="payloadMode.set('combo')">
3088
+ Combo
3089
+ </button>
2488
3090
  </div>
2489
3091
  </div>
2490
3092
 
@@ -2518,7 +3120,7 @@ class PraxisChartCompositionShowcaseComponent {
2518
3120
  <p class="panel-kicker">Runtime</p>
2519
3121
  <h3>{{ layoutMode() === 'widget' ? 'DynamicWidgetPage' : 'DynamicGridPage' }}</h3>
2520
3122
  </div>
2521
- <span class="panel-chip">{{ layoutMode() }} · {{ scenarioMode() }}</span>
3123
+ <span class="panel-chip">{{ layoutMode() }} / {{ scenarioMode() }}</span>
2522
3124
  </div>
2523
3125
 
2524
3126
  @if (layoutMode() === 'widget') {
@@ -2587,6 +3189,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
2587
3189
  <button type="button" [class.active]="payloadMode() === 'distribution'" (click)="payloadMode.set('distribution')">
2588
3190
  Distribution
2589
3191
  </button>
3192
+ <button type="button" [class.active]="payloadMode() === 'horizontal-bar'" (click)="payloadMode.set('horizontal-bar')">
3193
+ Horizontal bar
3194
+ </button>
3195
+ <button type="button" [class.active]="payloadMode() === 'stacked-area'" (click)="payloadMode.set('stacked-area')">
3196
+ Stacked area
3197
+ </button>
3198
+ <button type="button" [class.active]="payloadMode() === 'scatter'" (click)="payloadMode.set('scatter')">
3199
+ Scatter
3200
+ </button>
3201
+ <button type="button" [class.active]="payloadMode() === 'combo'" (click)="payloadMode.set('combo')">
3202
+ Combo
3203
+ </button>
2590
3204
  </div>
2591
3205
  </div>
2592
3206
 
@@ -2620,7 +3234,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
2620
3234
  <p class="panel-kicker">Runtime</p>
2621
3235
  <h3>{{ layoutMode() === 'widget' ? 'DynamicWidgetPage' : 'DynamicGridPage' }}</h3>
2622
3236
  </div>
2623
- <span class="panel-chip">{{ layoutMode() }} · {{ scenarioMode() }}</span>
3237
+ <span class="panel-chip">{{ layoutMode() }} / {{ scenarioMode() }}</span>
2624
3238
  </div>
2625
3239
 
2626
3240
  @if (layoutMode() === 'widget') {
@@ -2658,5 +3272,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
2658
3272
  * Generated bundle index. Do not edit.
2659
3273
  */
2660
3274
 
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 };
3275
+ 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_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
3276
  //# sourceMappingURL=praxisui-charts.mjs.map