@smartnet360/svelte-components 0.0.30 → 0.0.32
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 +15 -9
- package/dist/core/Charts/ChartCard.svelte.d.ts +2 -2
- package/dist/core/Charts/ChartComponent.svelte +6 -6
- package/dist/core/Charts/data-processor.d.ts +43 -0
- package/dist/core/Charts/data-processor.js +89 -0
- package/dist/core/Charts/data-utils.d.ts +4 -5
- package/dist/core/Charts/data-utils.js +5 -10
- package/dist/core/Charts/index.d.ts +2 -0
- package/dist/core/Charts/index.js +1 -0
- package/package.json +1 -1
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import type { Chart as ChartModel, ChartMarker, MovingAverageConfig } from './charts.model.js';
|
|
7
7
|
import { createTimeSeriesTraceWithMA, getYAxisTitle, createDefaultPlotlyLayout } from './data-utils.js';
|
|
8
8
|
import { adaptPlotlyLayout, addMarkersToLayout, type ContainerSize } from './adapt.js';
|
|
9
|
+
import { getKPIValues, type ProcessedChartData } from './data-processor.js';
|
|
9
10
|
|
|
10
11
|
const dispatch = createEventDispatcher<{
|
|
11
12
|
chartcontextmenu: {
|
|
@@ -18,8 +19,7 @@
|
|
|
18
19
|
|
|
19
20
|
interface Props {
|
|
20
21
|
chart: ChartModel;
|
|
21
|
-
|
|
22
|
-
timestamps: any[]; // Pre-extracted timestamps from ChartComponent
|
|
22
|
+
processedData: ProcessedChartData; // Pre-processed KPI values and timestamps
|
|
23
23
|
markers?: ChartMarker[]; // Global markers for all charts
|
|
24
24
|
plotlyLayout?: any; // Optional custom Plotly layout for styling/theming
|
|
25
25
|
enableAdaptation?: boolean; // Enable size-based adaptations (default: true)
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
runtimeShowLegend?: boolean; // Runtime control for showing legend (default: true)
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
let { chart,
|
|
35
|
+
let { chart, processedData, markers, plotlyLayout, enableAdaptation = true, sectionId, sectionMovingAverage, layoutMovingAverage, runtimeMAOverride, runtimeShowOriginal, runtimeShowMarkers = true, runtimeShowLegend = true }: Props = $props();
|
|
36
36
|
|
|
37
37
|
// Chart container div and state
|
|
38
38
|
let chartDiv: HTMLElement;
|
|
@@ -146,22 +146,27 @@
|
|
|
146
146
|
};
|
|
147
147
|
});
|
|
148
148
|
|
|
149
|
+
// Timestamps are already extracted in processedData
|
|
150
|
+
let timestamps = $derived(processedData.timestamps);
|
|
151
|
+
|
|
149
152
|
function renderChart() {
|
|
150
|
-
if (!chartDiv || !
|
|
153
|
+
if (!chartDiv || !processedData?.kpiValues.size) return;
|
|
151
154
|
|
|
152
155
|
const traces: any[] = [];
|
|
153
156
|
let colorIndex = 0;
|
|
154
157
|
|
|
155
158
|
// Add left Y-axis traces (with moving average support)
|
|
156
159
|
resolvedKPIs.left.forEach(kpi => {
|
|
157
|
-
const
|
|
160
|
+
const values = getKPIValues(processedData, kpi);
|
|
161
|
+
const kpiTraces = createTimeSeriesTraceWithMA(values, timestamps, kpi, 'y1', colorIndex);
|
|
158
162
|
traces.push(...kpiTraces);
|
|
159
163
|
colorIndex++;
|
|
160
164
|
});
|
|
161
165
|
|
|
162
166
|
// Add right Y-axis traces (with moving average support)
|
|
163
167
|
resolvedKPIs.right.forEach(kpi => {
|
|
164
|
-
const
|
|
168
|
+
const values = getKPIValues(processedData, kpi);
|
|
169
|
+
const kpiTraces = createTimeSeriesTraceWithMA(values, timestamps, kpi, 'y2', colorIndex);
|
|
165
170
|
traces.push(...kpiTraces);
|
|
166
171
|
colorIndex++;
|
|
167
172
|
});
|
|
@@ -269,8 +274,9 @@
|
|
|
269
274
|
containerSize.height = height;
|
|
270
275
|
|
|
271
276
|
if (chartDiv && chartDiv.children.length > 0) {
|
|
272
|
-
//
|
|
273
|
-
|
|
277
|
+
// Re-render chart to apply size-based adaptations
|
|
278
|
+
// This recalculates layout with new containerSize for proper adaptation
|
|
279
|
+
renderChart();
|
|
274
280
|
}
|
|
275
281
|
}
|
|
276
282
|
}
|
|
@@ -292,7 +298,7 @@
|
|
|
292
298
|
// React to prop changes - debounce re-renders for better performance
|
|
293
299
|
$effect(() => {
|
|
294
300
|
// Watch these props and re-render when they change
|
|
295
|
-
const
|
|
301
|
+
const currentProcessedData = processedData;
|
|
296
302
|
const currentMarkers = markers;
|
|
297
303
|
const currentMAOverride = runtimeMAOverride;
|
|
298
304
|
const currentShowOriginal = runtimeShowOriginal;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Chart as ChartModel, ChartMarker, MovingAverageConfig } from './charts.model.js';
|
|
2
|
+
import { type ProcessedChartData } from './data-processor.js';
|
|
2
3
|
interface Props {
|
|
3
4
|
chart: ChartModel;
|
|
4
|
-
|
|
5
|
-
timestamps: any[];
|
|
5
|
+
processedData: ProcessedChartData;
|
|
6
6
|
markers?: ChartMarker[];
|
|
7
7
|
plotlyLayout?: any;
|
|
8
8
|
enableAdaptation?: boolean;
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import type { Layout, Mode, ChartMarker, Section, ChartGrid, Chart, GlobalChartControls, MovingAverageConfig } from './charts.model.js';
|
|
6
6
|
import ChartCard from './ChartCard.svelte';
|
|
7
7
|
import GlobalControls from './GlobalControls.svelte';
|
|
8
|
+
import { getPreprocessedData, type ProcessedChartData } from './data-processor.js';
|
|
8
9
|
|
|
9
10
|
interface Props {
|
|
10
11
|
layout: Layout;
|
|
@@ -54,8 +55,9 @@
|
|
|
54
55
|
|
|
55
56
|
let { layout, data, mode, markers, plotlyLayout, enableAdaptation = true, showGlobalControls = true }: Props = $props();
|
|
56
57
|
|
|
57
|
-
//
|
|
58
|
-
|
|
58
|
+
// Preprocess raw data once - automatically memoized by Svelte's $derived
|
|
59
|
+
// This extracts all KPI values and timestamps, cached until data or layout changes
|
|
60
|
+
let processedData = $derived(getPreprocessedData(data, layout));
|
|
59
61
|
|
|
60
62
|
// Global runtime controls state - initialize from layout config
|
|
61
63
|
let globalControls = $state<GlobalChartControls>({
|
|
@@ -294,8 +296,7 @@
|
|
|
294
296
|
<div class="chart-slot">
|
|
295
297
|
<ChartCard
|
|
296
298
|
{chart}
|
|
297
|
-
{
|
|
298
|
-
{timestamps}
|
|
299
|
+
{processedData}
|
|
299
300
|
{markers}
|
|
300
301
|
{plotlyLayout}
|
|
301
302
|
{enableAdaptation}
|
|
@@ -437,8 +438,7 @@
|
|
|
437
438
|
</button>
|
|
438
439
|
<ChartCard
|
|
439
440
|
chart={activeZoom.chart}
|
|
440
|
-
{
|
|
441
|
-
{timestamps}
|
|
441
|
+
{processedData}
|
|
442
442
|
{markers}
|
|
443
443
|
{plotlyLayout}
|
|
444
444
|
{enableAdaptation}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Layout, KPI } from './charts.model.js';
|
|
2
|
+
/**
|
|
3
|
+
* Preprocessed chart data structure
|
|
4
|
+
* Contains extracted numeric values and timestamps, ready for chart rendering
|
|
5
|
+
*/
|
|
6
|
+
export interface ProcessedChartData {
|
|
7
|
+
/** Map of KPI rawName to numeric values array */
|
|
8
|
+
kpiValues: Map<string, number[]>;
|
|
9
|
+
/** Extracted timestamps array */
|
|
10
|
+
timestamps: any[];
|
|
11
|
+
/** Original raw data reference (for cache invalidation) */
|
|
12
|
+
_rawDataRef: any[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Extract all unique KPI rawNames from a layout configuration
|
|
16
|
+
* This determines which columns we need to extract from raw data
|
|
17
|
+
*/
|
|
18
|
+
export declare function extractKPINames(layout: Layout): Set<string>;
|
|
19
|
+
/**
|
|
20
|
+
* Process raw data into numeric arrays for each KPI
|
|
21
|
+
* Handles type conversion and filtering of invalid values
|
|
22
|
+
*
|
|
23
|
+
* @param data - Raw data array from API/database
|
|
24
|
+
* @param layout - Chart layout configuration containing all KPIs
|
|
25
|
+
* @param timestampField - Name of timestamp field in raw data (default: 'TIMESTAMP')
|
|
26
|
+
* @returns Preprocessed data ready for chart rendering
|
|
27
|
+
*/
|
|
28
|
+
export declare function preprocessChartData(data: any[], layout: Layout, timestampField?: string): ProcessedChartData;
|
|
29
|
+
/**
|
|
30
|
+
* Get preprocessed chart data with automatic caching
|
|
31
|
+
* Returns cached result if raw data hasn't changed
|
|
32
|
+
*
|
|
33
|
+
* @param data - Raw data array
|
|
34
|
+
* @param layout - Chart layout configuration
|
|
35
|
+
* @param timestampField - Name of timestamp field (default: 'TIMESTAMP')
|
|
36
|
+
* @returns Preprocessed data (cached or freshly computed)
|
|
37
|
+
*/
|
|
38
|
+
export declare function getPreprocessedData(data: any[], layout: Layout, timestampField?: string): ProcessedChartData;
|
|
39
|
+
/**
|
|
40
|
+
* Helper to get values for a specific KPI from preprocessed data
|
|
41
|
+
* Returns empty array if KPI not found
|
|
42
|
+
*/
|
|
43
|
+
export declare function getKPIValues(processedData: ProcessedChartData, kpi: KPI): number[];
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract all unique KPI rawNames from a layout configuration
|
|
3
|
+
* This determines which columns we need to extract from raw data
|
|
4
|
+
*/
|
|
5
|
+
export function extractKPINames(layout) {
|
|
6
|
+
const kpiNames = new Set();
|
|
7
|
+
for (const section of layout.sections) {
|
|
8
|
+
for (const chart of section.charts) {
|
|
9
|
+
// Collect from left Y-axis KPIs
|
|
10
|
+
for (const kpi of chart.yLeft) {
|
|
11
|
+
kpiNames.add(kpi.rawName);
|
|
12
|
+
}
|
|
13
|
+
// Collect from right Y-axis KPIs
|
|
14
|
+
for (const kpi of chart.yRight) {
|
|
15
|
+
kpiNames.add(kpi.rawName);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return kpiNames;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Process raw data into numeric arrays for each KPI
|
|
23
|
+
* Handles type conversion and filtering of invalid values
|
|
24
|
+
*
|
|
25
|
+
* @param data - Raw data array from API/database
|
|
26
|
+
* @param layout - Chart layout configuration containing all KPIs
|
|
27
|
+
* @param timestampField - Name of timestamp field in raw data (default: 'TIMESTAMP')
|
|
28
|
+
* @returns Preprocessed data ready for chart rendering
|
|
29
|
+
*/
|
|
30
|
+
export function preprocessChartData(data, layout, timestampField = 'TIMESTAMP') {
|
|
31
|
+
// Extract all unique KPI names we need to process
|
|
32
|
+
const kpiNames = extractKPINames(layout);
|
|
33
|
+
// Initialize the result map
|
|
34
|
+
const kpiValues = new Map();
|
|
35
|
+
// Process each KPI column
|
|
36
|
+
for (const kpiName of kpiNames) {
|
|
37
|
+
const values = data
|
|
38
|
+
.map(row => {
|
|
39
|
+
const val = row[kpiName];
|
|
40
|
+
// Convert to number if it's a string, return as-is if already a number
|
|
41
|
+
return typeof val === 'number' ? val : parseFloat(val);
|
|
42
|
+
})
|
|
43
|
+
.filter(val => !isNaN(val)); // Remove invalid values
|
|
44
|
+
kpiValues.set(kpiName, values);
|
|
45
|
+
}
|
|
46
|
+
// Extract timestamps once
|
|
47
|
+
const timestamps = data.map(row => row[timestampField]);
|
|
48
|
+
return {
|
|
49
|
+
kpiValues,
|
|
50
|
+
timestamps,
|
|
51
|
+
_rawDataRef: data // Keep reference for cache invalidation
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Memoized wrapper for preprocessChartData
|
|
56
|
+
* Uses WeakMap to cache results by raw data reference
|
|
57
|
+
* Cache is automatically garbage collected when raw data is no longer referenced
|
|
58
|
+
*/
|
|
59
|
+
const preprocessCache = new WeakMap();
|
|
60
|
+
/**
|
|
61
|
+
* Get preprocessed chart data with automatic caching
|
|
62
|
+
* Returns cached result if raw data hasn't changed
|
|
63
|
+
*
|
|
64
|
+
* @param data - Raw data array
|
|
65
|
+
* @param layout - Chart layout configuration
|
|
66
|
+
* @param timestampField - Name of timestamp field (default: 'TIMESTAMP')
|
|
67
|
+
* @returns Preprocessed data (cached or freshly computed)
|
|
68
|
+
*/
|
|
69
|
+
export function getPreprocessedData(data, layout, timestampField = 'TIMESTAMP') {
|
|
70
|
+
// Check cache first
|
|
71
|
+
const cached = preprocessCache.get(data);
|
|
72
|
+
if (cached) {
|
|
73
|
+
// Verify cache is still valid (data reference matches)
|
|
74
|
+
if (cached._rawDataRef === data) {
|
|
75
|
+
return cached;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Cache miss or invalid - compute and cache
|
|
79
|
+
const processed = preprocessChartData(data, layout, timestampField);
|
|
80
|
+
preprocessCache.set(data, processed);
|
|
81
|
+
return processed;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Helper to get values for a specific KPI from preprocessed data
|
|
85
|
+
* Returns empty array if KPI not found
|
|
86
|
+
*/
|
|
87
|
+
export function getKPIValues(processedData, kpi) {
|
|
88
|
+
return processedData.kpiValues.get(kpi.rawName) || [];
|
|
89
|
+
}
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
import type { KPI } from './charts.model.js';
|
|
2
2
|
export declare function processKPIData(data: any[], kpi: KPI): number[];
|
|
3
3
|
export declare function calculateMovingAverage(values: number[], window: number): number[];
|
|
4
|
-
export declare function createTimeSeriesTrace(
|
|
4
|
+
export declare function createTimeSeriesTrace(values: number[], timestamps: any[], kpi: KPI, yaxis?: 'y1' | 'y2', colorIndex?: number): any;
|
|
5
5
|
/**
|
|
6
6
|
* Create time series trace(s) with optional moving average
|
|
7
|
-
* @param
|
|
7
|
+
* @param values - Pre-processed numeric values array for the KPI
|
|
8
|
+
* @param timestamps - Pre-extracted timestamps array
|
|
8
9
|
* @param kpi - KPI configuration (may include movingAverage config)
|
|
9
|
-
* @param timestampField - Field name for timestamp column
|
|
10
10
|
* @param yaxis - Which Y-axis to use ('y1' or 'y2')
|
|
11
11
|
* @param colorIndex - Index for color selection
|
|
12
|
-
* @param precomputedTimestamps - Optional pre-extracted timestamps array (performance optimization)
|
|
13
12
|
* @returns Array of traces (original + MA if configured)
|
|
14
13
|
*/
|
|
15
|
-
export declare function createTimeSeriesTraceWithMA(
|
|
14
|
+
export declare function createTimeSeriesTraceWithMA(values: number[], timestamps: any[], kpi: KPI, yaxis?: 'y1' | 'y2', colorIndex?: number): any[];
|
|
16
15
|
export declare function getYAxisTitle(kpis: KPI[]): string;
|
|
17
16
|
export declare function formatValue(value: number, scale: 'percent' | 'absolute', unit: string): string;
|
|
18
17
|
export declare function createDefaultPlotlyLayout(title?: string): any;
|
|
@@ -72,9 +72,7 @@ export function calculateMovingAverage(values, window) {
|
|
|
72
72
|
maCache.set(cacheKey, result);
|
|
73
73
|
return result;
|
|
74
74
|
}
|
|
75
|
-
export function createTimeSeriesTrace(
|
|
76
|
-
const values = processKPIData(data, kpi);
|
|
77
|
-
const timestamps = data.map(row => row[timestampField]);
|
|
75
|
+
export function createTimeSeriesTrace(values, timestamps, kpi, yaxis = 'y1', colorIndex = 0) {
|
|
78
76
|
// Use KPI color if provided, otherwise cycle through modern colors
|
|
79
77
|
const traceColor = kpi.color || modernColors[colorIndex % modernColors.length];
|
|
80
78
|
return {
|
|
@@ -98,22 +96,19 @@ export function createTimeSeriesTrace(data, kpi, timestampField = 'TIMESTAMP', y
|
|
|
98
96
|
}
|
|
99
97
|
/**
|
|
100
98
|
* Create time series trace(s) with optional moving average
|
|
101
|
-
* @param
|
|
99
|
+
* @param values - Pre-processed numeric values array for the KPI
|
|
100
|
+
* @param timestamps - Pre-extracted timestamps array
|
|
102
101
|
* @param kpi - KPI configuration (may include movingAverage config)
|
|
103
|
-
* @param timestampField - Field name for timestamp column
|
|
104
102
|
* @param yaxis - Which Y-axis to use ('y1' or 'y2')
|
|
105
103
|
* @param colorIndex - Index for color selection
|
|
106
|
-
* @param precomputedTimestamps - Optional pre-extracted timestamps array (performance optimization)
|
|
107
104
|
* @returns Array of traces (original + MA if configured)
|
|
108
105
|
*/
|
|
109
|
-
export function createTimeSeriesTraceWithMA(
|
|
106
|
+
export function createTimeSeriesTraceWithMA(values, timestamps, kpi, yaxis = 'y1', colorIndex = 0) {
|
|
110
107
|
const traces = [];
|
|
111
|
-
const values = processKPIData(data, kpi);
|
|
112
|
-
const timestamps = precomputedTimestamps || data.map(row => row[timestampField]);
|
|
113
108
|
const traceColor = kpi.color || modernColors[colorIndex % modernColors.length];
|
|
114
109
|
// Add original trace (unless explicitly disabled)
|
|
115
110
|
if (!kpi.movingAverage || kpi.movingAverage.showOriginal !== false) {
|
|
116
|
-
const originalTrace = createTimeSeriesTrace(
|
|
111
|
+
const originalTrace = createTimeSeriesTrace(values, timestamps, kpi, yaxis, colorIndex);
|
|
117
112
|
// If MA is enabled, make the original line slightly transparent
|
|
118
113
|
if (kpi.movingAverage?.enabled) {
|
|
119
114
|
originalTrace.opacity = 0.4;
|
|
@@ -4,6 +4,8 @@ export type { Layout, Section, Chart, KPI, Mode, Scale, ChartMarker, ChartGrid,
|
|
|
4
4
|
export { createTimeSeriesTrace, getYAxisTitle, formatValue, processKPIData, createDefaultPlotlyLayout } from './data-utils.js';
|
|
5
5
|
export { adaptPlotlyLayout, getSizeCategory, createMarkerShapes, createMarkerAnnotations, addMarkersToLayout } from './adapt.js';
|
|
6
6
|
export type { ContainerSize, ChartInfo, AdaptationConfig } from './adapt.js';
|
|
7
|
+
export { getPreprocessedData, extractKPINames, getKPIValues, preprocessChartData } from './data-processor.js';
|
|
8
|
+
export type { ProcessedChartData } from './data-processor.js';
|
|
7
9
|
export { default as ChartLayoutEditor } from './editor/ChartLayoutEditor.svelte';
|
|
8
10
|
export { editorStore, currentLayout, savedLayouts, selection, isDirty, selectedItem } from './editor/editorState.js';
|
|
9
11
|
export type { Selection, SelectionType, EditorState } from './editor/editorState.js';
|
|
@@ -2,6 +2,7 @@ export { default as ChartComponent } from './ChartComponent.svelte';
|
|
|
2
2
|
export { default as ChartCard } from './ChartCard.svelte';
|
|
3
3
|
export { createTimeSeriesTrace, getYAxisTitle, formatValue, processKPIData, createDefaultPlotlyLayout } from './data-utils.js';
|
|
4
4
|
export { adaptPlotlyLayout, getSizeCategory, createMarkerShapes, createMarkerAnnotations, addMarkersToLayout } from './adapt.js';
|
|
5
|
+
export { getPreprocessedData, extractKPINames, getKPIValues, preprocessChartData } from './data-processor.js';
|
|
5
6
|
// Editor exports
|
|
6
7
|
export { default as ChartLayoutEditor } from './editor/ChartLayoutEditor.svelte';
|
|
7
8
|
export { editorStore, currentLayout, savedLayouts, selection, isDirty, selectedItem } from './editor/editorState.js';
|