@smartnet360/svelte-components 0.0.20 → 0.0.21
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/core/Charts/ChartCard.svelte +49 -9
- package/dist/core/Charts/ChartCard.svelte.d.ts +3 -1
- package/dist/core/Charts/ChartComponent.svelte +4 -0
- package/dist/core/Charts/charts.model.d.ts +10 -0
- package/dist/core/Charts/data-utils.d.ts +17 -0
- package/dist/core/Charts/data-utils.js +73 -0
- package/package.json +1 -1
@@ -3,8 +3,8 @@
|
|
3
3
|
<script lang="ts">
|
4
4
|
import { onMount, createEventDispatcher } from 'svelte';
|
5
5
|
import Plotly from 'plotly.js-dist-min';
|
6
|
-
import type { Chart as ChartModel, ChartMarker } from './charts.model.js';
|
7
|
-
import {
|
6
|
+
import type { Chart as ChartModel, ChartMarker, MovingAverageConfig } from './charts.model.js';
|
7
|
+
import { createTimeSeriesTraceWithMA, getYAxisTitle, createDefaultPlotlyLayout } from './data-utils.js';
|
8
8
|
import { adaptPlotlyLayout, addMarkersToLayout, type ContainerSize } from './adapt.js';
|
9
9
|
|
10
10
|
const dispatch = createEventDispatcher<{
|
@@ -23,9 +23,11 @@
|
|
23
23
|
plotlyLayout?: any; // Optional custom Plotly layout for styling/theming
|
24
24
|
enableAdaptation?: boolean; // Enable size-based adaptations (default: true)
|
25
25
|
sectionId?: string;
|
26
|
+
sectionMovingAverage?: MovingAverageConfig; // Section-level MA config
|
27
|
+
layoutMovingAverage?: MovingAverageConfig; // Layout-level MA config
|
26
28
|
}
|
27
29
|
|
28
|
-
let { chart, data, markers, plotlyLayout, enableAdaptation = true, sectionId }: Props = $props();
|
30
|
+
let { chart, data, markers, plotlyLayout, enableAdaptation = true, sectionId, sectionMovingAverage, layoutMovingAverage }: Props = $props();
|
29
31
|
|
30
32
|
// Chart container div and state
|
31
33
|
let chartDiv: HTMLElement;
|
@@ -68,17 +70,55 @@
|
|
68
70
|
const traces: any[] = [];
|
69
71
|
let colorIndex = 0;
|
70
72
|
|
71
|
-
//
|
73
|
+
// Helper function to apply MA with full hierarchy:
|
74
|
+
// KPI > Chart > Section > Layout (higher priority wins)
|
75
|
+
const applyMovingAverageHierarchy = (kpi: any) => {
|
76
|
+
// If KPI has its own MA config, use it (highest priority)
|
77
|
+
if (kpi.movingAverage) {
|
78
|
+
return kpi;
|
79
|
+
}
|
80
|
+
|
81
|
+
// Check chart-level MA
|
82
|
+
if (chart.movingAverage) {
|
83
|
+
return {
|
84
|
+
...kpi,
|
85
|
+
movingAverage: chart.movingAverage
|
86
|
+
};
|
87
|
+
}
|
88
|
+
|
89
|
+
// Check section-level MA
|
90
|
+
if (sectionMovingAverage) {
|
91
|
+
return {
|
92
|
+
...kpi,
|
93
|
+
movingAverage: sectionMovingAverage
|
94
|
+
};
|
95
|
+
}
|
96
|
+
|
97
|
+
// Check layout-level MA (lowest priority, global default)
|
98
|
+
if (layoutMovingAverage) {
|
99
|
+
return {
|
100
|
+
...kpi,
|
101
|
+
movingAverage: layoutMovingAverage
|
102
|
+
};
|
103
|
+
}
|
104
|
+
|
105
|
+
// No MA configuration at any level
|
106
|
+
return kpi;
|
107
|
+
};
|
108
|
+
|
109
|
+
// Add left Y-axis traces (with moving average support)
|
72
110
|
chart.yLeft.forEach(kpi => {
|
73
|
-
const
|
74
|
-
|
111
|
+
const kpiWithMA = applyMovingAverageHierarchy(kpi);
|
112
|
+
const kpiTraces = createTimeSeriesTraceWithMA(data, kpiWithMA, 'TIMESTAMP', 'y1', colorIndex);
|
113
|
+
traces.push(...kpiTraces);
|
75
114
|
colorIndex++;
|
76
115
|
});
|
77
116
|
|
78
|
-
// Add right Y-axis traces
|
117
|
+
// Add right Y-axis traces (with moving average support)
|
79
118
|
chart.yRight.forEach(kpi => {
|
80
|
-
const
|
81
|
-
|
119
|
+
const kpiWithMA = applyMovingAverageHierarchy(kpi);
|
120
|
+
const kpiTraces = createTimeSeriesTraceWithMA(data, kpiWithMA, 'TIMESTAMP', 'y2', colorIndex);
|
121
|
+
traces.push(...kpiTraces);
|
82
122
|
colorIndex++;
|
83
123
|
});
|
84
124
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import type { Chart as ChartModel, ChartMarker } from './charts.model.js';
|
1
|
+
import type { Chart as ChartModel, ChartMarker, MovingAverageConfig } from './charts.model.js';
|
2
2
|
interface Props {
|
3
3
|
chart: ChartModel;
|
4
4
|
data: any[];
|
@@ -6,6 +6,8 @@ interface Props {
|
|
6
6
|
plotlyLayout?: any;
|
7
7
|
enableAdaptation?: boolean;
|
8
8
|
sectionId?: string;
|
9
|
+
sectionMovingAverage?: MovingAverageConfig;
|
10
|
+
layoutMovingAverage?: MovingAverageConfig;
|
9
11
|
}
|
10
12
|
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
11
13
|
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
@@ -246,6 +246,8 @@
|
|
246
246
|
{plotlyLayout}
|
247
247
|
{enableAdaptation}
|
248
248
|
sectionId={section.id}
|
249
|
+
sectionMovingAverage={section.movingAverage}
|
250
|
+
layoutMovingAverage={layout.movingAverage}
|
249
251
|
on:chartcontextmenu={(event) => handleChartContextMenu(event.detail, section)}
|
250
252
|
/>
|
251
253
|
</div>
|
@@ -343,6 +345,8 @@
|
|
343
345
|
{plotlyLayout}
|
344
346
|
{enableAdaptation}
|
345
347
|
sectionId={activeZoom.section.id}
|
348
|
+
sectionMovingAverage={activeZoom.section.movingAverage}
|
349
|
+
layoutMovingAverage={layout.movingAverage}
|
346
350
|
on:chartcontextmenu={(event) => handleChartContextMenu(event.detail, activeZoom.section)}
|
347
351
|
/>
|
348
352
|
</div>
|
@@ -1,10 +1,17 @@
|
|
1
1
|
export type Scale = "percent" | "absolute";
|
2
|
+
export interface MovingAverageConfig {
|
3
|
+
enabled: boolean;
|
4
|
+
window: number;
|
5
|
+
showOriginal?: boolean;
|
6
|
+
label?: string;
|
7
|
+
}
|
2
8
|
export interface KPI {
|
3
9
|
rawName: string;
|
4
10
|
name: string;
|
5
11
|
scale: Scale;
|
6
12
|
unit: string;
|
7
13
|
color?: string;
|
14
|
+
movingAverage?: MovingAverageConfig;
|
8
15
|
}
|
9
16
|
export type ChartPosition = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
|
10
17
|
export type ChartGrid = "2x2" | "3x3" | "4x4" | "1x2" | "1x4" | "1x8";
|
@@ -13,17 +20,20 @@ export interface Chart {
|
|
13
20
|
title: string;
|
14
21
|
yLeft: KPI[];
|
15
22
|
yRight: KPI[];
|
23
|
+
movingAverage?: MovingAverageConfig;
|
16
24
|
}
|
17
25
|
export interface Section {
|
18
26
|
id: string;
|
19
27
|
title: string;
|
20
28
|
charts: Chart[];
|
21
29
|
grid?: ChartGrid;
|
30
|
+
movingAverage?: MovingAverageConfig;
|
22
31
|
}
|
23
32
|
export type Mode = "tabs" | "scrollspy";
|
24
33
|
export interface Layout {
|
25
34
|
layoutName: string;
|
26
35
|
sections: Section[];
|
36
|
+
movingAverage?: MovingAverageConfig;
|
27
37
|
}
|
28
38
|
export interface ChartMarker {
|
29
39
|
date: string | Date;
|
@@ -1,6 +1,23 @@
|
|
1
1
|
import type { KPI } from './charts.model.js';
|
2
2
|
export declare function processKPIData(data: any[], kpi: KPI): number[];
|
3
|
+
/**
|
4
|
+
* Calculate moving average for a series of values
|
5
|
+
* @param values - Array of numeric values
|
6
|
+
* @param window - Number of periods for MA calculation
|
7
|
+
* @returns Array of moving average values (NaN for insufficient data points)
|
8
|
+
*/
|
9
|
+
export declare function calculateMovingAverage(values: number[], window: number): number[];
|
3
10
|
export declare function createTimeSeriesTrace(data: any[], kpi: KPI, timestampField?: string, yaxis?: 'y1' | 'y2', colorIndex?: number): any;
|
11
|
+
/**
|
12
|
+
* Create time series trace(s) with optional moving average
|
13
|
+
* @param data - Raw data array
|
14
|
+
* @param kpi - KPI configuration (may include movingAverage config)
|
15
|
+
* @param timestampField - Field name for timestamp column
|
16
|
+
* @param yaxis - Which Y-axis to use ('y1' or 'y2')
|
17
|
+
* @param colorIndex - Index for color selection
|
18
|
+
* @returns Array of traces (original + MA if configured)
|
19
|
+
*/
|
20
|
+
export declare function createTimeSeriesTraceWithMA(data: any[], kpi: KPI, timestampField?: string, yaxis?: 'y1' | 'y2', colorIndex?: number): any[];
|
4
21
|
export declare function getYAxisTitle(kpis: KPI[]): string;
|
5
22
|
export declare function formatValue(value: number, scale: 'percent' | 'absolute', unit: string): string;
|
6
23
|
export declare function createDefaultPlotlyLayout(title?: string): any;
|
@@ -19,6 +19,28 @@ export function processKPIData(data, kpi) {
|
|
19
19
|
})
|
20
20
|
.filter(val => !isNaN(val));
|
21
21
|
}
|
22
|
+
/**
|
23
|
+
* Calculate moving average for a series of values
|
24
|
+
* @param values - Array of numeric values
|
25
|
+
* @param window - Number of periods for MA calculation
|
26
|
+
* @returns Array of moving average values (NaN for insufficient data points)
|
27
|
+
*/
|
28
|
+
export function calculateMovingAverage(values, window) {
|
29
|
+
const result = [];
|
30
|
+
for (let i = 0; i < values.length; i++) {
|
31
|
+
if (i < window - 1) {
|
32
|
+
// Not enough data points yet - use NaN to skip these points
|
33
|
+
result.push(NaN);
|
34
|
+
}
|
35
|
+
else {
|
36
|
+
// Calculate average of the window
|
37
|
+
const windowValues = values.slice(i - window + 1, i + 1);
|
38
|
+
const sum = windowValues.reduce((a, b) => a + b, 0);
|
39
|
+
result.push(sum / window);
|
40
|
+
}
|
41
|
+
}
|
42
|
+
return result;
|
43
|
+
}
|
22
44
|
export function createTimeSeriesTrace(data, kpi, timestampField = 'TIMESTAMP', yaxis = 'y1', colorIndex = 0) {
|
23
45
|
const values = processKPIData(data, kpi);
|
24
46
|
const timestamps = data.map(row => row[timestampField]);
|
@@ -43,6 +65,57 @@ export function createTimeSeriesTrace(data, kpi, timestampField = 'TIMESTAMP', y
|
|
43
65
|
'<extra></extra>'
|
44
66
|
};
|
45
67
|
}
|
68
|
+
/**
|
69
|
+
* Create time series trace(s) with optional moving average
|
70
|
+
* @param data - Raw data array
|
71
|
+
* @param kpi - KPI configuration (may include movingAverage config)
|
72
|
+
* @param timestampField - Field name for timestamp column
|
73
|
+
* @param yaxis - Which Y-axis to use ('y1' or 'y2')
|
74
|
+
* @param colorIndex - Index for color selection
|
75
|
+
* @returns Array of traces (original + MA if configured)
|
76
|
+
*/
|
77
|
+
export function createTimeSeriesTraceWithMA(data, kpi, timestampField = 'TIMESTAMP', yaxis = 'y1', colorIndex = 0) {
|
78
|
+
const traces = [];
|
79
|
+
const values = processKPIData(data, kpi);
|
80
|
+
const timestamps = data.map(row => row[timestampField]);
|
81
|
+
const traceColor = kpi.color || modernColors[colorIndex % modernColors.length];
|
82
|
+
// Add original trace (unless explicitly disabled)
|
83
|
+
if (!kpi.movingAverage || kpi.movingAverage.showOriginal !== false) {
|
84
|
+
const originalTrace = createTimeSeriesTrace(data, kpi, timestampField, yaxis, colorIndex);
|
85
|
+
// If MA is enabled, make the original line slightly transparent
|
86
|
+
if (kpi.movingAverage?.enabled) {
|
87
|
+
originalTrace.opacity = 0.4;
|
88
|
+
originalTrace.line.width = 2;
|
89
|
+
}
|
90
|
+
traces.push(originalTrace);
|
91
|
+
}
|
92
|
+
// Add moving average trace if configured
|
93
|
+
if (kpi.movingAverage?.enabled) {
|
94
|
+
const maValues = calculateMovingAverage(values, kpi.movingAverage.window);
|
95
|
+
const maLabel = kpi.movingAverage.label ||
|
96
|
+
`${kpi.name} (MA${kpi.movingAverage.window})`;
|
97
|
+
const maTrace = {
|
98
|
+
x: timestamps,
|
99
|
+
y: maValues,
|
100
|
+
type: 'scatter',
|
101
|
+
mode: 'lines',
|
102
|
+
name: maLabel,
|
103
|
+
yaxis: yaxis,
|
104
|
+
line: {
|
105
|
+
color: traceColor,
|
106
|
+
width: 3,
|
107
|
+
shape: 'spline',
|
108
|
+
smoothing: 0.3,
|
109
|
+
dash: yaxis === 'y1' ? 'solid' : 'dot'
|
110
|
+
},
|
111
|
+
hovertemplate: `<b>${maLabel}</b><br>` +
|
112
|
+
`Value: %{y:,.2f} ${kpi.unit}<br>` +
|
113
|
+
'<extra></extra>'
|
114
|
+
};
|
115
|
+
traces.push(maTrace);
|
116
|
+
}
|
117
|
+
return traces;
|
118
|
+
}
|
46
119
|
export function getYAxisTitle(kpis) {
|
47
120
|
if (kpis.length === 0)
|
48
121
|
return '';
|