@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.
@@ -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 { createTimeSeriesTrace, getYAxisTitle, createDefaultPlotlyLayout } from './data-utils.js';
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
- // Add left Y-axis traces
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 trace = createTimeSeriesTrace(data, kpi, 'TIMESTAMP', 'y1', colorIndex);
74
- traces.push(trace);
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 trace = createTimeSeriesTrace(data, kpi, 'TIMESTAMP', 'y2', colorIndex);
81
- traces.push(trace);
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 '';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartnet360/svelte-components",
3
- "version": "0.0.20",
3
+ "version": "0.0.21",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build && npm run prepack",