@smartnet360/svelte-components 0.0.35 → 0.0.37
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/apps/index.d.ts +1 -0
- package/dist/apps/index.js +1 -0
- package/dist/apps/site-check/SiteCheck.svelte +51 -6
- package/dist/apps/site-check/transforms.js +34 -1
- package/dist/core/Charts/ChartCard.svelte +20 -0
- package/dist/core/Charts/ChartComponent.svelte +24 -0
- package/dist/core/Charts/data-processor.js +21 -0
- package/dist/core/TreeView/tree-utils.js +22 -2
- package/dist/core/TreeView/tree.store.js +38 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +2 -0
- package/dist/core/logger/index.d.ts +26 -0
- package/dist/core/logger/index.js +42 -0
- package/package.json +1 -1
package/dist/apps/index.d.ts
CHANGED
package/dist/apps/index.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { ChartComponent, type Layout } from '../../core/Charts';
|
|
6
6
|
import { buildTreeNodes, filterChartData, transformChartData, type CellTrafficRecord } from './index';
|
|
7
7
|
import { expandLayoutForCells } from './helper';
|
|
8
|
+
import { log } from '../../core/logger';
|
|
8
9
|
import { onMount } from 'svelte';
|
|
9
10
|
import type { Mode } from '../../index.js';
|
|
10
11
|
|
|
@@ -20,8 +21,18 @@
|
|
|
20
21
|
let treeStore = $state<ReturnType<typeof createTreeStore> | null>(null);
|
|
21
22
|
|
|
22
23
|
onMount(() => {
|
|
24
|
+
log('🚀 SiteCheck Initializing', {
|
|
25
|
+
totalRecords: rawData.length,
|
|
26
|
+
baseMetrics,
|
|
27
|
+
mode
|
|
28
|
+
});
|
|
29
|
+
|
|
23
30
|
// Build tree nodes from raw data
|
|
24
31
|
const treeNodes = buildTreeNodes(rawData);
|
|
32
|
+
log('🌲 Tree Nodes Built', {
|
|
33
|
+
nodeCount: treeNodes.length,
|
|
34
|
+
firstNode: treeNodes[0]
|
|
35
|
+
});
|
|
25
36
|
|
|
26
37
|
// Create tree store
|
|
27
38
|
treeStore = createTreeStore({
|
|
@@ -30,6 +41,7 @@
|
|
|
30
41
|
persistState: true,
|
|
31
42
|
defaultExpandAll: false
|
|
32
43
|
});
|
|
44
|
+
log('✅ Tree Store Created', { namespace: 'site-check' });
|
|
33
45
|
});
|
|
34
46
|
|
|
35
47
|
// Derive chart data from tree selection
|
|
@@ -37,22 +49,55 @@
|
|
|
37
49
|
if (!treeStore) return [];
|
|
38
50
|
const storeValue = $treeStore;
|
|
39
51
|
if (!storeValue) return [];
|
|
40
|
-
|
|
52
|
+
const filtered = filterChartData(rawData, storeValue.state.checkedPaths);
|
|
53
|
+
log('🔍 Filtered Data:', {
|
|
54
|
+
totalRaw: rawData.length,
|
|
55
|
+
checkedPaths: Array.from(storeValue.state.checkedPaths),
|
|
56
|
+
filteredCount: filtered.length,
|
|
57
|
+
cells: Array.from(new Set(filtered.map(r => r.cellName)))
|
|
58
|
+
});
|
|
59
|
+
return filtered;
|
|
41
60
|
});
|
|
42
61
|
|
|
43
62
|
// Transform data using base metrics from layout
|
|
44
|
-
let chartData = $derived(
|
|
63
|
+
let chartData = $derived.by(() => {
|
|
64
|
+
const transformed = transformChartData(filteredData, baseMetrics);
|
|
65
|
+
log('📊 Chart Data:', {
|
|
66
|
+
filteredRows: filteredData.length,
|
|
67
|
+
transformedRows: transformed.length,
|
|
68
|
+
baseMetrics,
|
|
69
|
+
sampleRow: transformed[0],
|
|
70
|
+
columns: transformed[0] ? Object.keys(transformed[0]) : []
|
|
71
|
+
});
|
|
72
|
+
return transformed;
|
|
73
|
+
});
|
|
45
74
|
|
|
46
75
|
// Expand layout based on selected cells
|
|
47
|
-
let chartLayout = $derived(
|
|
48
|
-
|
|
76
|
+
let chartLayout = $derived.by(() => {
|
|
77
|
+
const expanded = expandLayoutForCells(baseLayout, filteredData);
|
|
78
|
+
log('📐 Chart Layout:', {
|
|
79
|
+
grid: expanded.grid,
|
|
80
|
+
sectionsCount: expanded.sections.length,
|
|
81
|
+
totalCharts: expanded.sections.reduce((sum, s) => sum + s.charts.length, 0),
|
|
82
|
+
firstSection: expanded.sections[0]
|
|
83
|
+
});
|
|
84
|
+
return expanded;
|
|
85
|
+
});
|
|
49
86
|
|
|
50
87
|
let totalRecords = $derived(rawData.length);
|
|
51
88
|
let visibleRecords = $derived(filteredData.length);
|
|
52
89
|
|
|
53
90
|
// Compute simple stats
|
|
54
|
-
let totalCells = $derived
|
|
55
|
-
|
|
91
|
+
let totalCells = $derived.by(() => {
|
|
92
|
+
const count = new Set(filteredData.map((r) => r.cellName)).size;
|
|
93
|
+
log('📱 Total Cells:', count);
|
|
94
|
+
return count;
|
|
95
|
+
});
|
|
96
|
+
let totalSites = $derived.by(() => {
|
|
97
|
+
const count = new Set(filteredData.map((r) => r.siteName)).size;
|
|
98
|
+
log('📡 Total Sites:', count);
|
|
99
|
+
return count;
|
|
100
|
+
});
|
|
56
101
|
</script>
|
|
57
102
|
|
|
58
103
|
<div class="container-fluid vh-100 d-flex flex-column">
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
* Data Transforms for Site Check Component
|
|
3
3
|
* Converts raw CSV data to TreeView nodes and Chart configurations
|
|
4
4
|
*/
|
|
5
|
+
import { log } from '../../core/logger';
|
|
5
6
|
/**
|
|
6
7
|
* Build hierarchical tree structure: Site → Sector (Azimuth) → Cell (Band)
|
|
7
8
|
*/
|
|
8
9
|
export function buildTreeNodes(data) {
|
|
10
|
+
log('🔄 Building tree nodes', { recordCount: data.length });
|
|
9
11
|
// Group by site → azimuth → cell
|
|
10
12
|
const siteMap = new Map();
|
|
11
13
|
data.forEach((record) => {
|
|
@@ -33,6 +35,7 @@ export function buildTreeNodes(data) {
|
|
|
33
35
|
// icon: '📡',
|
|
34
36
|
metadata: { type: 'site', siteName },
|
|
35
37
|
defaultExpanded: false,
|
|
38
|
+
defaultChecked: false, // Don't check parent nodes
|
|
36
39
|
children: []
|
|
37
40
|
};
|
|
38
41
|
Array.from(azimuthMap.entries())
|
|
@@ -44,6 +47,7 @@ export function buildTreeNodes(data) {
|
|
|
44
47
|
// icon: '📍',
|
|
45
48
|
metadata: { type: 'sector', azimuth, siteName },
|
|
46
49
|
defaultExpanded: false,
|
|
50
|
+
defaultChecked: false, // Don't check parent nodes
|
|
47
51
|
children: []
|
|
48
52
|
};
|
|
49
53
|
Array.from(cellMap.entries())
|
|
@@ -69,6 +73,11 @@ export function buildTreeNodes(data) {
|
|
|
69
73
|
});
|
|
70
74
|
treeNodes.push(siteNode);
|
|
71
75
|
});
|
|
76
|
+
log('✅ Tree nodes built', {
|
|
77
|
+
totalNodes: treeNodes.length,
|
|
78
|
+
totalSites: siteMap.size,
|
|
79
|
+
sampleSite: treeNodes[0]?.label
|
|
80
|
+
});
|
|
72
81
|
return treeNodes;
|
|
73
82
|
}
|
|
74
83
|
/**
|
|
@@ -87,6 +96,11 @@ function getBandIcon(band) {
|
|
|
87
96
|
* Only include cells that are checked in the tree
|
|
88
97
|
*/
|
|
89
98
|
export function filterChartData(data, checkedPaths) {
|
|
99
|
+
log('🔄 Filtering chart data', {
|
|
100
|
+
totalRecords: data.length,
|
|
101
|
+
checkedPathsCount: checkedPaths.size,
|
|
102
|
+
paths: Array.from(checkedPaths)
|
|
103
|
+
});
|
|
90
104
|
// Extract cell names from checked leaf paths (format: "site:azimuth:cellName")
|
|
91
105
|
const selectedCells = new Set();
|
|
92
106
|
checkedPaths.forEach((path) => {
|
|
@@ -97,7 +111,13 @@ export function filterChartData(data, checkedPaths) {
|
|
|
97
111
|
}
|
|
98
112
|
});
|
|
99
113
|
// Filter data to only include selected cells
|
|
100
|
-
|
|
114
|
+
const filtered = data.filter((record) => selectedCells.has(record.cellName));
|
|
115
|
+
log('✅ Data filtered', {
|
|
116
|
+
selectedCells: Array.from(selectedCells),
|
|
117
|
+
filteredRecords: filtered.length,
|
|
118
|
+
uniqueCells: new Set(filtered.map(r => r.cellName)).size
|
|
119
|
+
});
|
|
120
|
+
return filtered;
|
|
101
121
|
}
|
|
102
122
|
/**
|
|
103
123
|
* Transform data for chart component consumption
|
|
@@ -108,6 +128,11 @@ export function filterChartData(data, checkedPaths) {
|
|
|
108
128
|
* @param baseMetrics - Array of metric names to pivot (e.g., ['dlGBytes', 'ulGBytes'])
|
|
109
129
|
*/
|
|
110
130
|
export function transformChartData(data, baseMetrics) {
|
|
131
|
+
log('🔄 Transforming chart data', {
|
|
132
|
+
inputRecords: data.length,
|
|
133
|
+
baseMetrics,
|
|
134
|
+
uniqueCells: new Set(data.map(r => r.cellName)).size
|
|
135
|
+
});
|
|
111
136
|
// Group data by date
|
|
112
137
|
const dateMap = new Map();
|
|
113
138
|
data.forEach((record) => {
|
|
@@ -138,5 +163,13 @@ export function transformChartData(data, baseMetrics) {
|
|
|
138
163
|
});
|
|
139
164
|
// Sort by date
|
|
140
165
|
pivotedData.sort((a, b) => a.TIMESTAMP.localeCompare(b.TIMESTAMP));
|
|
166
|
+
log('✅ Data transformed', {
|
|
167
|
+
outputRows: pivotedData.length,
|
|
168
|
+
dateRange: pivotedData.length > 0 ?
|
|
169
|
+
`${pivotedData[0].TIMESTAMP} to ${pivotedData[pivotedData.length - 1].TIMESTAMP}` :
|
|
170
|
+
'none',
|
|
171
|
+
columnsPerRow: pivotedData[0] ? Object.keys(pivotedData[0]).length : 0,
|
|
172
|
+
sampleRow: pivotedData[0]
|
|
173
|
+
});
|
|
141
174
|
return pivotedData;
|
|
142
175
|
}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { createTimeSeriesTraceWithMA, getYAxisTitle, createDefaultPlotlyLayout } from './data-utils.js';
|
|
8
8
|
import { adaptPlotlyLayout, addMarkersToLayout, type ContainerSize } from './adapt.js';
|
|
9
9
|
import { getKPIValues, type ProcessedChartData } from './data-processor.js';
|
|
10
|
+
import { log } from '../logger';
|
|
10
11
|
|
|
11
12
|
const dispatch = createEventDispatcher<{
|
|
12
13
|
chartcontextmenu: {
|
|
@@ -235,9 +236,16 @@
|
|
|
235
236
|
// Use Plotly.react() for updates (preserves zoom/pan) or newPlot for initial render
|
|
236
237
|
if (chartInitialized) {
|
|
237
238
|
// Update existing chart - much faster, preserves user interactions
|
|
239
|
+
log('🔄 Updating chart with Plotly.react', { chartTitle: chart.title });
|
|
238
240
|
Plotly.react(chartDiv, traces, finalLayout, config);
|
|
239
241
|
} else {
|
|
240
242
|
// Initial chart creation
|
|
243
|
+
log('📊 Creating new chart with Plotly.newPlot', {
|
|
244
|
+
chartTitle: chart.title,
|
|
245
|
+
traces: traces.length,
|
|
246
|
+
leftKPIs: chart.yLeft.length,
|
|
247
|
+
rightKPIs: chart.yRight.length
|
|
248
|
+
});
|
|
241
249
|
Plotly.newPlot(chartDiv, traces, finalLayout, config);
|
|
242
250
|
chartInitialized = true;
|
|
243
251
|
}
|
|
@@ -251,11 +259,23 @@
|
|
|
251
259
|
}
|
|
252
260
|
|
|
253
261
|
onMount(() => {
|
|
262
|
+
log('📈 ChartCard mounted', {
|
|
263
|
+
chartTitle: chart.title,
|
|
264
|
+
leftKPIs: chart.yLeft.length,
|
|
265
|
+
rightKPIs: chart.yRight.length
|
|
266
|
+
});
|
|
267
|
+
|
|
254
268
|
// Initial container size measurement
|
|
255
269
|
if (chartDiv) {
|
|
256
270
|
const rect = chartDiv.getBoundingClientRect();
|
|
257
271
|
containerSize.width = rect.width;
|
|
258
272
|
containerSize.height = rect.height;
|
|
273
|
+
|
|
274
|
+
log('📐 Initial container size', {
|
|
275
|
+
chartTitle: chart.title,
|
|
276
|
+
width: rect.width,
|
|
277
|
+
height: rect.height
|
|
278
|
+
});
|
|
259
279
|
}
|
|
260
280
|
|
|
261
281
|
renderChart();
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import ChartCard from './ChartCard.svelte';
|
|
7
7
|
import GlobalControls from './GlobalControls.svelte';
|
|
8
8
|
import { getPreprocessedData, type ProcessedChartData } from './data-processor.js';
|
|
9
|
+
import { log } from '../logger';
|
|
9
10
|
|
|
10
11
|
interface Props {
|
|
11
12
|
layout: Layout;
|
|
@@ -55,6 +56,18 @@
|
|
|
55
56
|
|
|
56
57
|
let { layout, data, mode, markers, plotlyLayout, enableAdaptation = true, showGlobalControls = true }: Props = $props();
|
|
57
58
|
|
|
59
|
+
// Log component initialization
|
|
60
|
+
$effect(() => {
|
|
61
|
+
log('📊 ChartComponent initialized', {
|
|
62
|
+
mode,
|
|
63
|
+
dataRows: data.length,
|
|
64
|
+
sections: layout.sections.length,
|
|
65
|
+
totalCharts: layout.sections.reduce((sum, s) => sum + s.charts.length, 0),
|
|
66
|
+
enableAdaptation,
|
|
67
|
+
showGlobalControls
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
58
71
|
// Preprocess raw data once - automatically memoized by Svelte's $derived
|
|
59
72
|
// This extracts all KPI values and timestamps, cached until data or layout changes
|
|
60
73
|
let processedData = $derived(getPreprocessedData(data, layout));
|
|
@@ -76,6 +89,12 @@
|
|
|
76
89
|
|
|
77
90
|
// Handler for global controls updates
|
|
78
91
|
function handleControlsUpdate(updatedControls: GlobalChartControls) {
|
|
92
|
+
log('🎛️ Global controls updated', {
|
|
93
|
+
movingAverageEnabled: updatedControls.movingAverage?.enabled,
|
|
94
|
+
windowOverride: updatedControls.movingAverage?.windowOverride,
|
|
95
|
+
markersEnabled: updatedControls.markers?.enabled,
|
|
96
|
+
legendEnabled: updatedControls.legend?.enabled
|
|
97
|
+
});
|
|
79
98
|
globalControls = updatedControls;
|
|
80
99
|
}
|
|
81
100
|
|
|
@@ -166,12 +185,17 @@
|
|
|
166
185
|
|
|
167
186
|
function zoomSelectedChart() {
|
|
168
187
|
if (contextMenu.chart && contextMenu.section) {
|
|
188
|
+
log('🔍 Zooming chart', {
|
|
189
|
+
chartTitle: contextMenu.chart.title,
|
|
190
|
+
sectionId: contextMenu.section.id
|
|
191
|
+
});
|
|
169
192
|
zoomedChart = { chart: contextMenu.chart, section: contextMenu.section };
|
|
170
193
|
}
|
|
171
194
|
closeContextMenu();
|
|
172
195
|
}
|
|
173
196
|
|
|
174
197
|
function exitZoom() {
|
|
198
|
+
log('🔍 Exiting zoom mode');
|
|
175
199
|
zoomedChart = null;
|
|
176
200
|
closeContextMenu();
|
|
177
201
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { log } from '../logger';
|
|
1
2
|
/**
|
|
2
3
|
* Extract all unique KPI rawNames from a layout configuration
|
|
3
4
|
* This determines which columns we need to extract from raw data
|
|
@@ -16,6 +17,10 @@ export function extractKPINames(layout) {
|
|
|
16
17
|
}
|
|
17
18
|
}
|
|
18
19
|
}
|
|
20
|
+
log('📋 KPI names extracted', {
|
|
21
|
+
totalKPIs: kpiNames.size,
|
|
22
|
+
kpiNames: Array.from(kpiNames)
|
|
23
|
+
});
|
|
19
24
|
return kpiNames;
|
|
20
25
|
}
|
|
21
26
|
/**
|
|
@@ -28,6 +33,11 @@ export function extractKPINames(layout) {
|
|
|
28
33
|
* @returns Preprocessed data ready for chart rendering
|
|
29
34
|
*/
|
|
30
35
|
export function preprocessChartData(data, layout, timestampField = 'TIMESTAMP') {
|
|
36
|
+
log('🔄 Preprocessing chart data', {
|
|
37
|
+
rawDataRows: data.length,
|
|
38
|
+
sections: layout.sections.length,
|
|
39
|
+
timestampField
|
|
40
|
+
});
|
|
31
41
|
// Extract all unique KPI names we need to process
|
|
32
42
|
const kpiNames = extractKPINames(layout);
|
|
33
43
|
// Initialize the result map
|
|
@@ -42,9 +52,18 @@ export function preprocessChartData(data, layout, timestampField = 'TIMESTAMP')
|
|
|
42
52
|
})
|
|
43
53
|
.filter(val => !isNaN(val)); // Remove invalid values
|
|
44
54
|
kpiValues.set(kpiName, values);
|
|
55
|
+
if (values.length === 0) {
|
|
56
|
+
log('⚠️ No valid values found for KPI', { kpiName });
|
|
57
|
+
}
|
|
45
58
|
}
|
|
46
59
|
// Extract timestamps once
|
|
47
60
|
const timestamps = data.map(row => row[timestampField]);
|
|
61
|
+
log('✅ Data preprocessing complete', {
|
|
62
|
+
processedKPIs: kpiValues.size,
|
|
63
|
+
timestampCount: timestamps.length,
|
|
64
|
+
sampleKPI: kpiValues.keys().next().value,
|
|
65
|
+
sampleValues: kpiValues.values().next().value?.slice(0, 3)
|
|
66
|
+
});
|
|
48
67
|
return {
|
|
49
68
|
kpiValues,
|
|
50
69
|
timestamps,
|
|
@@ -72,9 +91,11 @@ export function getPreprocessedData(data, layout, timestampField = 'TIMESTAMP')
|
|
|
72
91
|
if (cached) {
|
|
73
92
|
// Verify cache is still valid (data reference matches)
|
|
74
93
|
if (cached._rawDataRef === data) {
|
|
94
|
+
log('💾 Using cached preprocessed data');
|
|
75
95
|
return cached;
|
|
76
96
|
}
|
|
77
97
|
}
|
|
98
|
+
log('🔄 Cache miss - preprocessing data');
|
|
78
99
|
// Cache miss or invalid - compute and cache
|
|
79
100
|
const processed = preprocessChartData(data, layout, timestampField);
|
|
80
101
|
preprocessCache.set(data, processed);
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Tree utility functions
|
|
3
3
|
* Helper functions for path manipulation, tree flattening, and state management
|
|
4
4
|
*/
|
|
5
|
+
import { log } from '../logger';
|
|
5
6
|
/**
|
|
6
7
|
* Get parent path from a node path
|
|
7
8
|
* @example getParentPath("site-a:sector-1:700", ":") => "site-a:sector-1"
|
|
@@ -144,10 +145,18 @@ export function saveStateToStorage(namespace, state) {
|
|
|
144
145
|
if (!namespace)
|
|
145
146
|
return;
|
|
146
147
|
try {
|
|
147
|
-
|
|
148
|
-
|
|
148
|
+
const checkedArray = Array.from(state.checkedPaths);
|
|
149
|
+
const expandedArray = Array.from(state.expandedPaths);
|
|
150
|
+
localStorage.setItem(getStorageKey(namespace, 'checked'), JSON.stringify(checkedArray));
|
|
151
|
+
localStorage.setItem(getStorageKey(namespace, 'expanded'), JSON.stringify(expandedArray));
|
|
152
|
+
log('💾 State saved to localStorage', {
|
|
153
|
+
namespace,
|
|
154
|
+
checkedCount: checkedArray.length,
|
|
155
|
+
expandedCount: expandedArray.length
|
|
156
|
+
});
|
|
149
157
|
}
|
|
150
158
|
catch (error) {
|
|
159
|
+
log('❌ Failed to save tree state', { namespace, error });
|
|
151
160
|
console.warn('Failed to save tree state to localStorage:', error);
|
|
152
161
|
}
|
|
153
162
|
}
|
|
@@ -166,14 +175,23 @@ export function loadStateFromStorage(namespace, state) {
|
|
|
166
175
|
updates.checkedPaths = new Set(checkedArray);
|
|
167
176
|
// Recalculate indeterminate states
|
|
168
177
|
updates.indeterminatePaths = calculateIndeterminateStates(state.nodes, updates.checkedPaths);
|
|
178
|
+
log('📂 Loaded checked paths from localStorage', {
|
|
179
|
+
namespace,
|
|
180
|
+
checkedCount: checkedArray.length
|
|
181
|
+
});
|
|
169
182
|
}
|
|
170
183
|
if (expandedJson) {
|
|
171
184
|
const expandedArray = JSON.parse(expandedJson);
|
|
172
185
|
updates.expandedPaths = new Set(expandedArray);
|
|
186
|
+
log('📂 Loaded expanded paths from localStorage', {
|
|
187
|
+
namespace,
|
|
188
|
+
expandedCount: expandedArray.length
|
|
189
|
+
});
|
|
173
190
|
}
|
|
174
191
|
return updates;
|
|
175
192
|
}
|
|
176
193
|
catch (error) {
|
|
194
|
+
log('❌ Failed to load tree state', { namespace, error });
|
|
177
195
|
console.warn('Failed to load tree state from localStorage:', error);
|
|
178
196
|
return {};
|
|
179
197
|
}
|
|
@@ -187,8 +205,10 @@ export function clearStorageForNamespace(namespace) {
|
|
|
187
205
|
try {
|
|
188
206
|
localStorage.removeItem(getStorageKey(namespace, 'checked'));
|
|
189
207
|
localStorage.removeItem(getStorageKey(namespace, 'expanded'));
|
|
208
|
+
log('🗑️ Cleared tree state from localStorage', { namespace });
|
|
190
209
|
}
|
|
191
210
|
catch (error) {
|
|
211
|
+
log('❌ Failed to clear tree state', { namespace, error });
|
|
192
212
|
console.warn('Failed to clear tree state from localStorage:', error);
|
|
193
213
|
}
|
|
194
214
|
}
|
|
@@ -4,19 +4,39 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { writable } from 'svelte/store';
|
|
6
6
|
import { flattenTree, buildInitialState, calculateIndeterminateStates, getDescendantPaths, getParentPath, saveStateToStorage, loadStateFromStorage, clearStorageForNamespace } from './tree-utils';
|
|
7
|
+
import { log } from '../logger';
|
|
7
8
|
/**
|
|
8
9
|
* Create a tree store with state management and persistence
|
|
9
10
|
*/
|
|
10
11
|
export function createTreeStore(config) {
|
|
12
|
+
log('🌲 Creating TreeStore', {
|
|
13
|
+
namespace: config.namespace,
|
|
14
|
+
nodeCount: config.nodes.length,
|
|
15
|
+
persistState: config.persistState,
|
|
16
|
+
defaultExpandAll: config.defaultExpandAll
|
|
17
|
+
});
|
|
11
18
|
const separator = config.pathSeparator || ':';
|
|
12
19
|
// Flatten tree structure
|
|
13
20
|
const nodesMap = flattenTree(config.nodes, config);
|
|
21
|
+
log('📊 Tree flattened', {
|
|
22
|
+
totalNodes: nodesMap.size,
|
|
23
|
+
separator
|
|
24
|
+
});
|
|
14
25
|
// Build initial state
|
|
15
26
|
let state = buildInitialState(nodesMap, config);
|
|
27
|
+
log('🔧 Initial state built', {
|
|
28
|
+
checkedPaths: state.checkedPaths.size,
|
|
29
|
+
expandedPaths: state.expandedPaths.size
|
|
30
|
+
});
|
|
16
31
|
// Load persisted state if enabled
|
|
17
32
|
if (config.persistState && config.namespace) {
|
|
18
33
|
const persistedState = loadStateFromStorage(config.namespace, state);
|
|
19
34
|
state = { ...state, ...persistedState };
|
|
35
|
+
log('💾 Loaded persisted state', {
|
|
36
|
+
namespace: config.namespace,
|
|
37
|
+
checkedPaths: state.checkedPaths.size,
|
|
38
|
+
expandedPaths: state.expandedPaths.size
|
|
39
|
+
});
|
|
20
40
|
}
|
|
21
41
|
// Create writable store
|
|
22
42
|
const store = writable({
|
|
@@ -52,10 +72,13 @@ export function createTreeStore(config) {
|
|
|
52
72
|
* Toggle a node's checked state (with cascading)
|
|
53
73
|
*/
|
|
54
74
|
function toggle(path) {
|
|
75
|
+
log('🔄 Toggling node', { path });
|
|
55
76
|
updateState(state => {
|
|
56
77
|
const nodeState = state.nodes.get(path);
|
|
57
|
-
if (!nodeState)
|
|
78
|
+
if (!nodeState) {
|
|
79
|
+
log('⚠️ Node not found', { path });
|
|
58
80
|
return state;
|
|
81
|
+
}
|
|
59
82
|
const newChecked = !state.checkedPaths.has(path);
|
|
60
83
|
const newCheckedPaths = new Set(state.checkedPaths);
|
|
61
84
|
// Update this node
|
|
@@ -67,6 +90,11 @@ export function createTreeStore(config) {
|
|
|
67
90
|
}
|
|
68
91
|
// Cascade to all descendants
|
|
69
92
|
const descendants = getDescendantPaths(path, state.nodes, separator);
|
|
93
|
+
log('📦 Cascading to descendants', {
|
|
94
|
+
path,
|
|
95
|
+
descendantCount: descendants.length,
|
|
96
|
+
newChecked
|
|
97
|
+
});
|
|
70
98
|
descendants.forEach(descendantPath => {
|
|
71
99
|
if (newChecked) {
|
|
72
100
|
newCheckedPaths.add(descendantPath);
|
|
@@ -99,6 +127,12 @@ export function createTreeStore(config) {
|
|
|
99
127
|
}
|
|
100
128
|
// Recalculate indeterminate states
|
|
101
129
|
const newIndeterminatePaths = calculateIndeterminateStates(state.nodes, newCheckedPaths);
|
|
130
|
+
log('✅ Toggle complete', {
|
|
131
|
+
path,
|
|
132
|
+
newChecked,
|
|
133
|
+
totalChecked: newCheckedPaths.size,
|
|
134
|
+
indeterminate: newIndeterminatePaths.size
|
|
135
|
+
});
|
|
102
136
|
return {
|
|
103
137
|
...state,
|
|
104
138
|
checkedPaths: newCheckedPaths,
|
|
@@ -155,11 +189,13 @@ export function createTreeStore(config) {
|
|
|
155
189
|
* Check all nodes
|
|
156
190
|
*/
|
|
157
191
|
function checkAll() {
|
|
192
|
+
log('✅ Check all nodes');
|
|
158
193
|
updateState(state => {
|
|
159
194
|
const newCheckedPaths = new Set();
|
|
160
195
|
state.nodes.forEach((_, path) => {
|
|
161
196
|
newCheckedPaths.add(path);
|
|
162
197
|
});
|
|
198
|
+
log('✅ All nodes checked', { totalChecked: newCheckedPaths.size });
|
|
163
199
|
return {
|
|
164
200
|
...state,
|
|
165
201
|
checkedPaths: newCheckedPaths,
|
|
@@ -171,6 +207,7 @@ export function createTreeStore(config) {
|
|
|
171
207
|
* Uncheck all nodes
|
|
172
208
|
*/
|
|
173
209
|
function uncheckAll() {
|
|
210
|
+
log('❌ Uncheck all nodes');
|
|
174
211
|
updateState(state => ({
|
|
175
212
|
...state,
|
|
176
213
|
checkedPaths: new Set(),
|
package/dist/core/index.d.ts
CHANGED
package/dist/core/index.js
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple debug logger utility
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```typescript
|
|
6
|
+
* import { log } from './';
|
|
7
|
+
*
|
|
8
|
+
* log('User clicked button', { userId: 123 });
|
|
9
|
+
* log('Data loaded', data);
|
|
10
|
+
*
|
|
11
|
+
* // Disable logging globally
|
|
12
|
+
* log.disable();
|
|
13
|
+
*
|
|
14
|
+
* // Enable logging globally
|
|
15
|
+
* log.enable();
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Simple log function - logs to console when enabled
|
|
20
|
+
*/
|
|
21
|
+
export declare function log(message: string, ...args: any[]): void;
|
|
22
|
+
export declare namespace log {
|
|
23
|
+
var disable: () => void;
|
|
24
|
+
var enable: () => void;
|
|
25
|
+
var isEnabled: () => boolean;
|
|
26
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple debug logger utility
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```typescript
|
|
6
|
+
* import { log } from './';
|
|
7
|
+
*
|
|
8
|
+
* log('User clicked button', { userId: 123 });
|
|
9
|
+
* log('Data loaded', data);
|
|
10
|
+
*
|
|
11
|
+
* // Disable logging globally
|
|
12
|
+
* log.disable();
|
|
13
|
+
*
|
|
14
|
+
* // Enable logging globally
|
|
15
|
+
* log.enable();
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
let enabled = true;
|
|
19
|
+
/**
|
|
20
|
+
* Simple log function - logs to console when enabled
|
|
21
|
+
*/
|
|
22
|
+
export function log(message, ...args) {
|
|
23
|
+
if (enabled) {
|
|
24
|
+
console.log(`[DEBUG]`, message, ...args);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Disable logging globally
|
|
29
|
+
*/
|
|
30
|
+
log.disable = () => {
|
|
31
|
+
enabled = false;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Enable logging globally
|
|
35
|
+
*/
|
|
36
|
+
log.enable = () => {
|
|
37
|
+
enabled = true;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Check if logging is enabled
|
|
41
|
+
*/
|
|
42
|
+
log.isEnabled = () => enabled;
|