@smartnet360/svelte-components 0.0.102 → 0.0.104
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/antenna-pattern/index.d.ts +1 -0
- package/dist/apps/antenna-pattern/index.js +1 -0
- package/dist/apps/antenna-pattern/utils/load-static-antennas.d.ts +17 -0
- package/dist/apps/antenna-pattern/utils/load-static-antennas.js +83 -0
- package/dist/apps/site-check/SiteCheck.svelte +13 -81
- package/dist/apps/site-check/SiteCheckControls.svelte +0 -7
- package/dist/apps/site-check/helper.js +0 -33
- package/dist/apps/site-check/transforms.js +15 -65
- package/dist/core/CellTable/CellTable.svelte +456 -0
- package/dist/core/CellTable/CellTable.svelte.d.ts +27 -0
- package/dist/core/CellTable/CellTablePanel.svelte +211 -0
- package/dist/core/CellTable/CellTablePanel.svelte.d.ts +49 -0
- package/dist/core/CellTable/CellTableToolbar.svelte +218 -0
- package/dist/core/CellTable/CellTableToolbar.svelte.d.ts +32 -0
- package/dist/core/CellTable/column-config.d.ts +63 -0
- package/dist/core/CellTable/column-config.js +465 -0
- package/dist/core/CellTable/index.d.ts +10 -0
- package/dist/core/CellTable/index.js +11 -0
- package/dist/core/CellTable/types.d.ts +166 -0
- package/dist/core/CellTable/types.js +6 -0
- package/dist/core/Charts/ChartCard.svelte +118 -31
- package/dist/core/Charts/ChartCard.svelte.d.ts +2 -0
- package/dist/core/Charts/ChartComponent.svelte +8 -31
- package/dist/core/Charts/data-processor.js +1 -19
- package/dist/core/CoverageMap/ai/AITools.d.ts +117 -0
- package/dist/core/CoverageMap/ai/AITools.js +380 -0
- package/dist/core/CoverageMap/core/CoverageCalculator.d.ts +138 -0
- package/dist/core/CoverageMap/core/CoverageCalculator.js +375 -0
- package/dist/core/CoverageMap/core/GridCalculator.d.ts +115 -0
- package/dist/core/CoverageMap/core/GridCalculator.js +484 -0
- package/dist/core/CoverageMap/core/PathLossModels.d.ts +253 -0
- package/dist/core/CoverageMap/core/PathLossModels.js +380 -0
- package/dist/core/CoverageMap/core/SignalProcessor.d.ts +288 -0
- package/dist/core/CoverageMap/core/SignalProcessor.js +424 -0
- package/dist/core/CoverageMap/data/AntennaStore.d.ts +165 -0
- package/dist/core/CoverageMap/data/AntennaStore.js +327 -0
- package/dist/core/CoverageMap/data/SiteStore.d.ts +155 -0
- package/dist/core/CoverageMap/data/SiteStore.js +355 -0
- package/dist/core/CoverageMap/index.d.ts +74 -0
- package/dist/core/CoverageMap/index.js +103 -0
- package/dist/core/CoverageMap/types.d.ts +252 -0
- package/dist/core/CoverageMap/types.js +7 -0
- package/dist/core/CoverageMap/utils/geoUtils.d.ts +223 -0
- package/dist/core/CoverageMap/utils/geoUtils.js +374 -0
- package/dist/core/CoverageMap/utils/rfUtils.d.ts +329 -0
- package/dist/core/CoverageMap/utils/rfUtils.js +434 -0
- package/dist/core/CoverageMap/visualization/ColorSchemes.d.ts +149 -0
- package/dist/core/CoverageMap/visualization/ColorSchemes.js +377 -0
- package/dist/core/TreeView/index.d.ts +4 -4
- package/dist/core/TreeView/index.js +5 -5
- package/dist/core/TreeView/tree-utils.d.ts +12 -0
- package/dist/core/TreeView/tree-utils.js +115 -6
- package/dist/core/TreeView/tree.store.svelte.d.ts +94 -0
- package/dist/core/TreeView/tree.store.svelte.js +274 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +2 -0
- package/dist/map-v2/features/cells/controls/CellFilterControl.svelte +16 -27
- package/dist/map-v2/features/repeaters/controls/RepeaterFilterControl.svelte +33 -42
- package/dist/map-v2/features/sites/controls/SiteFilterControl.svelte +12 -19
- package/dist/map-v3/core/components/Map.svelte +4 -0
- package/dist/map-v3/core/stores/map.store.svelte.js +2 -0
- package/dist/map-v3/features/cells/components/CellFilterControl.svelte +24 -30
- package/dist/map-v3/features/coverage/index.d.ts +12 -0
- package/dist/map-v3/features/coverage/index.js +16 -0
- package/dist/map-v3/features/coverage/layers/CoverageLayer.svelte +198 -0
- package/dist/map-v3/features/coverage/layers/CoverageLayer.svelte.d.ts +10 -0
- package/dist/map-v3/features/coverage/logic/coloring.d.ts +28 -0
- package/dist/map-v3/features/coverage/logic/coloring.js +77 -0
- package/dist/map-v3/features/coverage/logic/geometry.d.ts +33 -0
- package/dist/map-v3/features/coverage/logic/geometry.js +112 -0
- package/dist/map-v3/features/coverage/stores/coverage.data.svelte.d.ts +46 -0
- package/dist/map-v3/features/coverage/stores/coverage.data.svelte.js +95 -0
- package/dist/map-v3/features/coverage/stores/coverage.display.svelte.d.ts +33 -0
- package/dist/map-v3/features/coverage/stores/coverage.display.svelte.js +90 -0
- package/dist/map-v3/features/coverage/types.d.ts +52 -0
- package/dist/map-v3/features/coverage/types.js +7 -0
- package/dist/map-v3/features/repeaters/components/RepeaterFilterControl.svelte +14 -20
- package/dist/map-v3/features/sites/components/SiteFilterControl.svelte +23 -33
- package/dist/map-v3/index.d.ts +4 -0
- package/dist/map-v3/index.js +5 -0
- package/package.json +4 -3
- package/dist/apps/site-check/transforms-old.d.ts +0 -56
- package/dist/apps/site-check/transforms-old.js +0 -273
- package/dist/core/TreeView/tree.store.d.ts +0 -10
- package/dist/core/TreeView/tree.store.js +0 -320
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Color Schemes
|
|
3
|
+
*
|
|
4
|
+
* This module defines color schemes for coverage visualization.
|
|
5
|
+
* Supports multiple visualization styles:
|
|
6
|
+
* - Heatmap: Gradient from strong to weak signal
|
|
7
|
+
* - Categorical: Discrete quality levels
|
|
8
|
+
* - Single color: Opacity-based (for sector identification)
|
|
9
|
+
*
|
|
10
|
+
* All colors designed to be:
|
|
11
|
+
* - Colorblind-friendly (where possible)
|
|
12
|
+
* - Print-friendly
|
|
13
|
+
* - Intuitive (red = strong, blue = weak for heatmap)
|
|
14
|
+
*/
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// PREDEFINED COLOR SCHEMES
|
|
17
|
+
// ============================================================================
|
|
18
|
+
/**
|
|
19
|
+
* Heatmap - Red to Blue gradient
|
|
20
|
+
*
|
|
21
|
+
* Classic heatmap where:
|
|
22
|
+
* - Red = Excellent signal (hot)
|
|
23
|
+
* - Orange/Yellow = Good signal (warm)
|
|
24
|
+
* - Green = Fair signal (cool)
|
|
25
|
+
* - Blue = Poor signal (cold)
|
|
26
|
+
* - Gray = No signal
|
|
27
|
+
*
|
|
28
|
+
* This is intuitive for most users.
|
|
29
|
+
*/
|
|
30
|
+
export const HEATMAP_RED_BLUE = {
|
|
31
|
+
name: 'Heatmap (Red → Blue)',
|
|
32
|
+
description: 'Classic heatmap gradient from strong (red) to weak (blue)',
|
|
33
|
+
stops: [
|
|
34
|
+
{ value: -60, color: '#FF0000' }, // Excellent (bright red)
|
|
35
|
+
{ value: -70, color: '#FF4500' }, // Excellent/Good transition (orange-red)
|
|
36
|
+
{ value: -80, color: '#FFA500' }, // Good (orange)
|
|
37
|
+
{ value: -90, color: '#FFFF00' }, // Fair (yellow)
|
|
38
|
+
{ value: -100, color: '#00FF00' }, // Poor (green)
|
|
39
|
+
{ value: -110, color: '#0000FF' } // Edge (blue)
|
|
40
|
+
],
|
|
41
|
+
noSignalColor: '#CCCCCC' // Gray
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Heatmap - Green to Red (inverted)
|
|
45
|
+
*
|
|
46
|
+
* Alternative where:
|
|
47
|
+
* - Green = Excellent (good status)
|
|
48
|
+
* - Yellow = Fair (warning)
|
|
49
|
+
* - Red = Poor (problem)
|
|
50
|
+
*
|
|
51
|
+
* More intuitive for traffic light metaphor.
|
|
52
|
+
*/
|
|
53
|
+
export const HEATMAP_GREEN_RED = {
|
|
54
|
+
name: 'Heatmap (Green → Red)',
|
|
55
|
+
description: 'Traffic light style: green (good) to red (bad)',
|
|
56
|
+
stops: [
|
|
57
|
+
{ value: -60, color: '#00FF00' }, // Excellent (green)
|
|
58
|
+
{ value: -70, color: '#7FFF00' }, // Good (yellow-green)
|
|
59
|
+
{ value: -80, color: '#FFFF00' }, // Good/Fair (yellow)
|
|
60
|
+
{ value: -90, color: '#FFA500' }, // Fair (orange)
|
|
61
|
+
{ value: -100, color: '#FF4500' }, // Poor (orange-red)
|
|
62
|
+
{ value: -110, color: '#FF0000' } // Edge (red)
|
|
63
|
+
],
|
|
64
|
+
noSignalColor: '#CCCCCC'
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Categorical - Discrete quality levels
|
|
68
|
+
*
|
|
69
|
+
* Uses distinct colors for each quality level.
|
|
70
|
+
* No gradients - clear boundaries.
|
|
71
|
+
* Good for presentations and reports.
|
|
72
|
+
*/
|
|
73
|
+
export const CATEGORICAL = {
|
|
74
|
+
name: 'Categorical',
|
|
75
|
+
description: 'Discrete colors for each quality level',
|
|
76
|
+
stops: [
|
|
77
|
+
{ value: -70, color: '#2ECC40' }, // Excellent (green)
|
|
78
|
+
{ value: -85, color: '#FFDC00' }, // Good (yellow)
|
|
79
|
+
{ value: -95, color: '#FF851B' }, // Fair (orange)
|
|
80
|
+
{ value: -105, color: '#FF4136' } // Poor (red)
|
|
81
|
+
],
|
|
82
|
+
noSignalColor: '#AAAAAA'
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Viridis-inspired (colorblind-friendly)
|
|
86
|
+
*
|
|
87
|
+
* Based on matplotlib's viridis colormap.
|
|
88
|
+
* Optimized for colorblind users and grayscale printing.
|
|
89
|
+
*/
|
|
90
|
+
export const VIRIDIS = {
|
|
91
|
+
name: 'Viridis (Colorblind-friendly)',
|
|
92
|
+
description: 'Perceptually uniform, colorblind-safe gradient',
|
|
93
|
+
stops: [
|
|
94
|
+
{ value: -60, color: '#FDE724' }, // Excellent (yellow)
|
|
95
|
+
{ value: -70, color: '#B8DE29' }, // Good (yellow-green)
|
|
96
|
+
{ value: -80, color: '#73D055' }, // Good/Fair (green)
|
|
97
|
+
{ value: -90, color: '#3CBB75' }, // Fair (teal)
|
|
98
|
+
{ value: -100, color: '#20A387' }, // Poor (cyan)
|
|
99
|
+
{ value: -110, color: '#287D8E' } // Edge (blue-cyan)
|
|
100
|
+
],
|
|
101
|
+
noSignalColor: '#CCCCCC'
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Plasma-inspired (high contrast)
|
|
105
|
+
*
|
|
106
|
+
* High contrast colormap for visibility.
|
|
107
|
+
*/
|
|
108
|
+
export const PLASMA = {
|
|
109
|
+
name: 'Plasma (High Contrast)',
|
|
110
|
+
description: 'High contrast gradient for visibility',
|
|
111
|
+
stops: [
|
|
112
|
+
{ value: -60, color: '#F0F921' }, // Excellent (yellow)
|
|
113
|
+
{ value: -70, color: '#FCA636' }, // Good (orange)
|
|
114
|
+
{ value: -80, color: '#E16462' }, // Good/Fair (salmon)
|
|
115
|
+
{ value: -90, color: '#B12A90' }, // Fair (magenta)
|
|
116
|
+
{ value: -100, color: '#6A00A8' }, // Poor (purple)
|
|
117
|
+
{ value: -110, color: '#0D0887' } // Edge (dark blue)
|
|
118
|
+
],
|
|
119
|
+
noSignalColor: '#CCCCCC'
|
|
120
|
+
};
|
|
121
|
+
// ============================================================================
|
|
122
|
+
// COLOR SCHEME REGISTRY
|
|
123
|
+
// ============================================================================
|
|
124
|
+
/**
|
|
125
|
+
* All available color schemes
|
|
126
|
+
*/
|
|
127
|
+
export const COLOR_SCHEMES = {
|
|
128
|
+
'heatmap-red-blue': HEATMAP_RED_BLUE,
|
|
129
|
+
'heatmap-green-red': HEATMAP_GREEN_RED,
|
|
130
|
+
categorical: CATEGORICAL,
|
|
131
|
+
viridis: VIRIDIS,
|
|
132
|
+
plasma: PLASMA
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Default color scheme
|
|
136
|
+
*/
|
|
137
|
+
export const DEFAULT_COLOR_SCHEME = HEATMAP_RED_BLUE;
|
|
138
|
+
// ============================================================================
|
|
139
|
+
// COLOR INTERPOLATION
|
|
140
|
+
// ============================================================================
|
|
141
|
+
/**
|
|
142
|
+
* Interpolate color for a specific signal strength
|
|
143
|
+
*
|
|
144
|
+
* Uses linear interpolation between color stops to create
|
|
145
|
+
* smooth gradients.
|
|
146
|
+
*
|
|
147
|
+
* Process:
|
|
148
|
+
* 1. Find two nearest color stops
|
|
149
|
+
* 2. Calculate interpolation factor
|
|
150
|
+
* 3. Interpolate RGB components
|
|
151
|
+
* 4. Convert back to hex
|
|
152
|
+
*
|
|
153
|
+
* @param signalDbm - Signal strength in dBm
|
|
154
|
+
* @param scheme - Color scheme to use
|
|
155
|
+
* @returns Hex color string
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* const color = interpolateColor(-75, HEATMAP_RED_BLUE);
|
|
159
|
+
* // Returns color between -70 (orange-red) and -80 (orange)
|
|
160
|
+
* // e.g., "#FF7400"
|
|
161
|
+
*/
|
|
162
|
+
export function interpolateColor(signalDbm, scheme) {
|
|
163
|
+
const stops = scheme.stops;
|
|
164
|
+
// Handle out-of-range values
|
|
165
|
+
if (signalDbm >= stops[0].value) {
|
|
166
|
+
return stops[0].color; // Strongest signal color
|
|
167
|
+
}
|
|
168
|
+
if (signalDbm <= stops[stops.length - 1].value) {
|
|
169
|
+
return stops[stops.length - 1].color; // Weakest signal color
|
|
170
|
+
}
|
|
171
|
+
// Handle no signal
|
|
172
|
+
if (signalDbm < -120) {
|
|
173
|
+
return scheme.noSignalColor;
|
|
174
|
+
}
|
|
175
|
+
// Find surrounding color stops
|
|
176
|
+
for (let i = 0; i < stops.length - 1; i++) {
|
|
177
|
+
const stop1 = stops[i];
|
|
178
|
+
const stop2 = stops[i + 1];
|
|
179
|
+
if (signalDbm >= stop2.value && signalDbm <= stop1.value) {
|
|
180
|
+
// Calculate interpolation factor (0 to 1)
|
|
181
|
+
const factor = (signalDbm - stop2.value) / (stop1.value - stop2.value);
|
|
182
|
+
// Interpolate between colors
|
|
183
|
+
return interpolateHexColors(stop2.color, stop1.color, factor);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
// Fallback (shouldn't reach here)
|
|
187
|
+
return scheme.noSignalColor;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Get color for categorical quality level
|
|
191
|
+
*
|
|
192
|
+
* Returns discrete color based on quality level without interpolation.
|
|
193
|
+
*
|
|
194
|
+
* @param quality - Signal quality level
|
|
195
|
+
* @param scheme - Color scheme (optional, defaults to categorical)
|
|
196
|
+
* @returns Hex color string
|
|
197
|
+
*/
|
|
198
|
+
export function getCategoricalColor(quality, scheme = CATEGORICAL) {
|
|
199
|
+
switch (quality) {
|
|
200
|
+
case 'excellent':
|
|
201
|
+
return scheme.stops[0]?.color || '#00FF00';
|
|
202
|
+
case 'good':
|
|
203
|
+
return scheme.stops[1]?.color || '#FFFF00';
|
|
204
|
+
case 'fair':
|
|
205
|
+
return scheme.stops[2]?.color || '#FFA500';
|
|
206
|
+
case 'poor':
|
|
207
|
+
return scheme.stops[3]?.color || '#FF0000';
|
|
208
|
+
case 'no-signal':
|
|
209
|
+
default:
|
|
210
|
+
return scheme.noSignalColor;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Get color based on signal thresholds
|
|
215
|
+
*
|
|
216
|
+
* Convenience function that combines threshold checking and coloring.
|
|
217
|
+
*
|
|
218
|
+
* @param signalDbm - Signal strength in dBm
|
|
219
|
+
* @param thresholds - Signal quality thresholds
|
|
220
|
+
* @param scheme - Color scheme to use
|
|
221
|
+
* @returns Hex color string
|
|
222
|
+
*/
|
|
223
|
+
export function getColorForSignal(signalDbm, thresholds, scheme = DEFAULT_COLOR_SCHEME) {
|
|
224
|
+
// Determine quality
|
|
225
|
+
let quality;
|
|
226
|
+
if (signalDbm >= thresholds.excellent) {
|
|
227
|
+
quality = 'excellent';
|
|
228
|
+
}
|
|
229
|
+
else if (signalDbm >= thresholds.good) {
|
|
230
|
+
quality = 'good';
|
|
231
|
+
}
|
|
232
|
+
else if (signalDbm >= thresholds.fair) {
|
|
233
|
+
quality = 'fair';
|
|
234
|
+
}
|
|
235
|
+
else if (signalDbm >= thresholds.edge) {
|
|
236
|
+
quality = 'poor';
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
quality = 'no-signal';
|
|
240
|
+
}
|
|
241
|
+
// Use interpolation for smooth gradients or categorical for discrete
|
|
242
|
+
if (scheme.name.includes('Heatmap') || scheme.name.includes('friendly')) {
|
|
243
|
+
return interpolateColor(signalDbm, scheme);
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
return getCategoricalColor(quality, scheme);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
// ============================================================================
|
|
250
|
+
// HELPER FUNCTIONS
|
|
251
|
+
// ============================================================================
|
|
252
|
+
/**
|
|
253
|
+
* Interpolate between two hex colors
|
|
254
|
+
*
|
|
255
|
+
* Converts hex to RGB, interpolates, converts back.
|
|
256
|
+
*
|
|
257
|
+
* @param color1 - Start color (hex)
|
|
258
|
+
* @param color2 - End color (hex)
|
|
259
|
+
* @param factor - Interpolation factor (0 to 1)
|
|
260
|
+
* @returns Interpolated color (hex)
|
|
261
|
+
*/
|
|
262
|
+
function interpolateHexColors(color1, color2, factor) {
|
|
263
|
+
// Parse hex colors
|
|
264
|
+
const rgb1 = hexToRgb(color1);
|
|
265
|
+
const rgb2 = hexToRgb(color2);
|
|
266
|
+
if (!rgb1 || !rgb2) {
|
|
267
|
+
return color1; // Fallback
|
|
268
|
+
}
|
|
269
|
+
// Interpolate each component
|
|
270
|
+
const r = Math.round(rgb1.r + (rgb2.r - rgb1.r) * factor);
|
|
271
|
+
const g = Math.round(rgb1.g + (rgb2.g - rgb1.g) * factor);
|
|
272
|
+
const b = Math.round(rgb1.b + (rgb2.b - rgb1.b) * factor);
|
|
273
|
+
// Convert back to hex
|
|
274
|
+
return rgbToHex(r, g, b);
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Convert hex color to RGB
|
|
278
|
+
*
|
|
279
|
+
* @param hex - Hex color string (e.g., "#FF0000" or "FF0000")
|
|
280
|
+
* @returns RGB object or null
|
|
281
|
+
*/
|
|
282
|
+
function hexToRgb(hex) {
|
|
283
|
+
// Remove # if present
|
|
284
|
+
hex = hex.replace(/^#/, '');
|
|
285
|
+
// Parse hex digits
|
|
286
|
+
if (hex.length === 3) {
|
|
287
|
+
// Short form: #RGB → #RRGGBB
|
|
288
|
+
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
|
|
289
|
+
}
|
|
290
|
+
if (hex.length !== 6) {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
const r = parseInt(hex.substring(0, 2), 16);
|
|
294
|
+
const g = parseInt(hex.substring(2, 4), 16);
|
|
295
|
+
const b = parseInt(hex.substring(4, 6), 16);
|
|
296
|
+
if (isNaN(r) || isNaN(g) || isNaN(b)) {
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
return { r, g, b };
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Convert RGB to hex color
|
|
303
|
+
*
|
|
304
|
+
* @param r - Red (0-255)
|
|
305
|
+
* @param g - Green (0-255)
|
|
306
|
+
* @param b - Blue (0-255)
|
|
307
|
+
* @returns Hex color string
|
|
308
|
+
*/
|
|
309
|
+
function rgbToHex(r, g, b) {
|
|
310
|
+
// Clamp values to 0-255
|
|
311
|
+
r = Math.max(0, Math.min(255, r));
|
|
312
|
+
g = Math.max(0, Math.min(255, g));
|
|
313
|
+
b = Math.max(0, Math.min(255, b));
|
|
314
|
+
// Convert to hex
|
|
315
|
+
const rHex = r.toString(16).padStart(2, '0');
|
|
316
|
+
const gHex = g.toString(16).padStart(2, '0');
|
|
317
|
+
const bHex = b.toString(16).padStart(2, '0');
|
|
318
|
+
return `#${rHex}${gHex}${bHex}`.toUpperCase();
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Add opacity to hex color
|
|
322
|
+
*
|
|
323
|
+
* Converts hex to rgba with specified opacity.
|
|
324
|
+
*
|
|
325
|
+
* @param hex - Hex color
|
|
326
|
+
* @param opacity - Opacity (0-1)
|
|
327
|
+
* @returns RGBA color string
|
|
328
|
+
*
|
|
329
|
+
* @example
|
|
330
|
+
* const semi = addOpacity("#FF0000", 0.5);
|
|
331
|
+
* // Returns: "rgba(255, 0, 0, 0.5)"
|
|
332
|
+
*/
|
|
333
|
+
export function addOpacity(hex, opacity) {
|
|
334
|
+
const rgb = hexToRgb(hex);
|
|
335
|
+
if (!rgb) {
|
|
336
|
+
return `rgba(0, 0, 0, ${opacity})`;
|
|
337
|
+
}
|
|
338
|
+
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${opacity})`;
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Create legend entries for a color scheme
|
|
342
|
+
*
|
|
343
|
+
* Generates legend data for UI display.
|
|
344
|
+
*
|
|
345
|
+
* @param scheme - Color scheme
|
|
346
|
+
* @param thresholds - Signal thresholds
|
|
347
|
+
* @returns Array of legend entries
|
|
348
|
+
*/
|
|
349
|
+
export function createLegend(scheme, thresholds) {
|
|
350
|
+
return [
|
|
351
|
+
{
|
|
352
|
+
label: 'Excellent',
|
|
353
|
+
color: getColorForSignal(thresholds.excellent, thresholds, scheme),
|
|
354
|
+
range: `≥ ${thresholds.excellent} dBm`
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
label: 'Good',
|
|
358
|
+
color: getColorForSignal((thresholds.excellent + thresholds.good) / 2, thresholds, scheme),
|
|
359
|
+
range: `${thresholds.good} to ${thresholds.excellent} dBm`
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
label: 'Fair',
|
|
363
|
+
color: getColorForSignal((thresholds.good + thresholds.fair) / 2, thresholds, scheme),
|
|
364
|
+
range: `${thresholds.fair} to ${thresholds.good} dBm`
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
label: 'Poor',
|
|
368
|
+
color: getColorForSignal((thresholds.fair + thresholds.edge) / 2, thresholds, scheme),
|
|
369
|
+
range: `${thresholds.edge} to ${thresholds.fair} dBm`
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
label: 'No Signal',
|
|
373
|
+
color: scheme.noSignalColor,
|
|
374
|
+
range: `< ${thresholds.edge} dBm`
|
|
375
|
+
}
|
|
376
|
+
];
|
|
377
|
+
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* - Indeterminate states for partial selection
|
|
9
9
|
* - Expand/collapse functionality
|
|
10
10
|
* - LocalStorage persistence
|
|
11
|
-
* - Svelte
|
|
11
|
+
* - Svelte 5 runes for reactive state ($state, SvelteMap, SvelteSet)
|
|
12
12
|
* - Bootstrap styling
|
|
13
13
|
*
|
|
14
14
|
* @example
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
* const treeStore = createTreeStore(config);
|
|
35
35
|
*
|
|
36
36
|
* $effect(() => {
|
|
37
|
-
* console.log('Checked paths:',
|
|
37
|
+
* console.log('Checked paths:', treeStore.state.checkedPaths);
|
|
38
38
|
* });
|
|
39
39
|
* </script>
|
|
40
40
|
*
|
|
@@ -43,6 +43,6 @@
|
|
|
43
43
|
*/
|
|
44
44
|
export { default as TreeView } from './TreeView.svelte';
|
|
45
45
|
export { default as TreeNodeComponent } from './TreeNode.svelte';
|
|
46
|
-
export { createTreeStore } from './tree.store';
|
|
46
|
+
export { createTreeStore, TreeStore } from './tree.store.svelte';
|
|
47
47
|
export type { TreeNode, TreeConfig, TreeState, NodeState, TreeStoreValue } from './tree.model';
|
|
48
|
-
export { getParentPath, getAncestorPaths, isAncestor, getPathLevel, flattenTree, calculateIndeterminateStates, getDescendantPaths, buildInitialState } from './tree-utils';
|
|
48
|
+
export { getParentPath, getAncestorPaths, isAncestor, getPathLevel, flattenTree, flattenTreeToSvelteMap, calculateIndeterminateStates, calculateIndeterminateStatesAsSvelteSet, getDescendantPaths, buildInitialState } from './tree-utils';
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* - Indeterminate states for partial selection
|
|
9
9
|
* - Expand/collapse functionality
|
|
10
10
|
* - LocalStorage persistence
|
|
11
|
-
* - Svelte
|
|
11
|
+
* - Svelte 5 runes for reactive state ($state, SvelteMap, SvelteSet)
|
|
12
12
|
* - Bootstrap styling
|
|
13
13
|
*
|
|
14
14
|
* @example
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
* const treeStore = createTreeStore(config);
|
|
35
35
|
*
|
|
36
36
|
* $effect(() => {
|
|
37
|
-
* console.log('Checked paths:',
|
|
37
|
+
* console.log('Checked paths:', treeStore.state.checkedPaths);
|
|
38
38
|
* });
|
|
39
39
|
* </script>
|
|
40
40
|
*
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
// Components
|
|
45
45
|
export { default as TreeView } from './TreeView.svelte';
|
|
46
46
|
export { default as TreeNodeComponent } from './TreeNode.svelte';
|
|
47
|
-
// Store
|
|
48
|
-
export { createTreeStore } from './tree.store';
|
|
47
|
+
// Store (Svelte 5 runes-based implementation)
|
|
48
|
+
export { createTreeStore, TreeStore } from './tree.store.svelte';
|
|
49
49
|
// Utilities (for advanced usage)
|
|
50
|
-
export { getParentPath, getAncestorPaths, isAncestor, getPathLevel, flattenTree, calculateIndeterminateStates, getDescendantPaths, buildInitialState } from './tree-utils';
|
|
50
|
+
export { getParentPath, getAncestorPaths, isAncestor, getPathLevel, flattenTree, flattenTreeToSvelteMap, calculateIndeterminateStates, calculateIndeterminateStatesAsSvelteSet, getDescendantPaths, buildInitialState } from './tree-utils';
|
|
@@ -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 { SvelteMap, SvelteSet } from 'svelte/reactivity';
|
|
5
6
|
import type { TreeNode, NodeState, TreeState, TreeConfig } from './tree.model';
|
|
6
7
|
/**
|
|
7
8
|
* Get parent path from a node path
|
|
@@ -26,6 +27,10 @@ export declare function getPathLevel(path: string, separator?: string): number;
|
|
|
26
27
|
* Flatten tree structure into map of NodeState
|
|
27
28
|
*/
|
|
28
29
|
export declare function flattenTree<T = any>(nodes: TreeNode<T>[], config: TreeConfig<T>, parentPath?: string, level?: number): Map<string, NodeState>;
|
|
30
|
+
/**
|
|
31
|
+
* Flatten tree structure into SvelteMap for reactive state management (Svelte 5)
|
|
32
|
+
*/
|
|
33
|
+
export declare function flattenTreeToSvelteMap<T = any>(nodes: TreeNode<T>[], config: TreeConfig<T>, parentPath?: string, level?: number): SvelteMap<string, NodeState>;
|
|
29
34
|
/**
|
|
30
35
|
* Calculate which nodes should be indeterminate
|
|
31
36
|
* A node is indeterminate if:
|
|
@@ -33,12 +38,18 @@ export declare function flattenTree<T = any>(nodes: TreeNode<T>[], config: TreeC
|
|
|
33
38
|
* - Some (but not all) of its direct children are checked OR indeterminate
|
|
34
39
|
*/
|
|
35
40
|
export declare function calculateIndeterminateStates(nodes: Map<string, NodeState>, checkedPaths: Set<string>): Set<string>;
|
|
41
|
+
/**
|
|
42
|
+
* Calculate indeterminate states returning SvelteSet for reactive state management
|
|
43
|
+
* Same logic as calculateIndeterminateStates but returns SvelteSet
|
|
44
|
+
*/
|
|
45
|
+
export declare function calculateIndeterminateStatesAsSvelteSet(nodes: Map<string, NodeState>, checkedPaths: Set<string>): SvelteSet<string>;
|
|
36
46
|
/**
|
|
37
47
|
* Get all descendant paths of a node
|
|
38
48
|
*/
|
|
39
49
|
export declare function getDescendantPaths(path: string, nodes: Map<string, NodeState>, separator?: string): string[];
|
|
40
50
|
/**
|
|
41
51
|
* Build initial TreeState from flattened nodes
|
|
52
|
+
* Works with both regular Map/Set and SvelteMap/SvelteSet
|
|
42
53
|
*/
|
|
43
54
|
export declare function buildInitialState(nodes: Map<string, NodeState>, config: TreeConfig): TreeState;
|
|
44
55
|
/**
|
|
@@ -51,6 +62,7 @@ export declare function getStorageKey(namespace: string, key: string): string;
|
|
|
51
62
|
export declare function saveStateToStorage(namespace: string, state: TreeState): void;
|
|
52
63
|
/**
|
|
53
64
|
* Load state from localStorage
|
|
65
|
+
* Returns SvelteSet for reactive state management
|
|
54
66
|
*/
|
|
55
67
|
export declare function loadStateFromStorage(namespace: string, state: TreeState): Partial<TreeState>;
|
|
56
68
|
/**
|
|
@@ -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 { SvelteMap, SvelteSet } from 'svelte/reactivity';
|
|
5
6
|
import { log } from '../logger';
|
|
6
7
|
/**
|
|
7
8
|
* Get parent path from a node path
|
|
@@ -74,6 +75,70 @@ export function flattenTree(nodes, config, parentPath = '', level = 0) {
|
|
|
74
75
|
}
|
|
75
76
|
return map;
|
|
76
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Flatten tree structure into SvelteMap for reactive state management (Svelte 5)
|
|
80
|
+
*/
|
|
81
|
+
export function flattenTreeToSvelteMap(nodes, config, parentPath = '', level = 0) {
|
|
82
|
+
const map = new SvelteMap();
|
|
83
|
+
const separator = config.pathSeparator || ':';
|
|
84
|
+
for (const node of nodes) {
|
|
85
|
+
const path = parentPath ? `${parentPath}${separator}${node.id}` : node.id;
|
|
86
|
+
// Collect child paths
|
|
87
|
+
const childPaths = [];
|
|
88
|
+
if (node.children && node.children.length > 0) {
|
|
89
|
+
for (const child of node.children) {
|
|
90
|
+
childPaths.push(parentPath ? `${path}${separator}${child.id}` : `${path}${separator}${child.id}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const nodeState = {
|
|
94
|
+
path,
|
|
95
|
+
checked: node.defaultChecked ?? true,
|
|
96
|
+
indeterminate: false,
|
|
97
|
+
expanded: node.defaultExpanded ?? (config.defaultExpandAll || false),
|
|
98
|
+
node,
|
|
99
|
+
parentPath,
|
|
100
|
+
childPaths,
|
|
101
|
+
level
|
|
102
|
+
};
|
|
103
|
+
map.set(path, nodeState);
|
|
104
|
+
// Recursively flatten children
|
|
105
|
+
if (node.children && node.children.length > 0) {
|
|
106
|
+
flattenTreeToSvelteMapRecursive(node.children, config, path, level + 1, map);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return map;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Helper for recursive flattening into existing SvelteMap
|
|
113
|
+
*/
|
|
114
|
+
function flattenTreeToSvelteMapRecursive(nodes, config, parentPath, level, map) {
|
|
115
|
+
const separator = config.pathSeparator || ':';
|
|
116
|
+
for (const node of nodes) {
|
|
117
|
+
const path = `${parentPath}${separator}${node.id}`;
|
|
118
|
+
// Collect child paths
|
|
119
|
+
const childPaths = [];
|
|
120
|
+
if (node.children && node.children.length > 0) {
|
|
121
|
+
for (const child of node.children) {
|
|
122
|
+
childPaths.push(`${path}${separator}${child.id}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
const nodeState = {
|
|
126
|
+
path,
|
|
127
|
+
checked: node.defaultChecked ?? true,
|
|
128
|
+
indeterminate: false,
|
|
129
|
+
expanded: node.defaultExpanded ?? (config.defaultExpandAll || false),
|
|
130
|
+
node,
|
|
131
|
+
parentPath,
|
|
132
|
+
childPaths,
|
|
133
|
+
level
|
|
134
|
+
};
|
|
135
|
+
map.set(path, nodeState);
|
|
136
|
+
// Recursively flatten children
|
|
137
|
+
if (node.children && node.children.length > 0) {
|
|
138
|
+
flattenTreeToSvelteMapRecursive(node.children, config, path, level + 1, map);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
77
142
|
/**
|
|
78
143
|
* Calculate which nodes should be indeterminate
|
|
79
144
|
* A node is indeterminate if:
|
|
@@ -117,6 +182,47 @@ export function calculateIndeterminateStates(nodes, checkedPaths) {
|
|
|
117
182
|
}
|
|
118
183
|
return indeterminate;
|
|
119
184
|
}
|
|
185
|
+
/**
|
|
186
|
+
* Calculate indeterminate states returning SvelteSet for reactive state management
|
|
187
|
+
* Same logic as calculateIndeterminateStates but returns SvelteSet
|
|
188
|
+
*/
|
|
189
|
+
export function calculateIndeterminateStatesAsSvelteSet(nodes, checkedPaths) {
|
|
190
|
+
const indeterminate = new SvelteSet();
|
|
191
|
+
// For each node with children, check from deepest to shallowest
|
|
192
|
+
// This ensures we calculate indeterminate state correctly
|
|
193
|
+
const nodesArray = Array.from(nodes.entries());
|
|
194
|
+
// Sort by level (deepest first) to ensure we process children before parents
|
|
195
|
+
nodesArray.sort((a, b) => b[1].level - a[1].level);
|
|
196
|
+
for (const [path, nodeState] of nodesArray) {
|
|
197
|
+
// Skip leaf nodes - they can't be indeterminate
|
|
198
|
+
if (nodeState.childPaths.length === 0)
|
|
199
|
+
continue;
|
|
200
|
+
// Count direct children that are either checked or indeterminate
|
|
201
|
+
let checkedCount = 0;
|
|
202
|
+
let indeterminateCount = 0;
|
|
203
|
+
for (const childPath of nodeState.childPaths) {
|
|
204
|
+
if (checkedPaths.has(childPath)) {
|
|
205
|
+
checkedCount++;
|
|
206
|
+
}
|
|
207
|
+
else if (indeterminate.has(childPath)) {
|
|
208
|
+
indeterminateCount++;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
const totalChildren = nodeState.childPaths.length;
|
|
212
|
+
const affectedChildren = checkedCount + indeterminateCount;
|
|
213
|
+
// Node is indeterminate if:
|
|
214
|
+
// 1. Some children are checked/indeterminate, but not all
|
|
215
|
+
// 2. At least one child is indeterminate (even if all are checked/indeterminate)
|
|
216
|
+
if (affectedChildren > 0 && affectedChildren < totalChildren) {
|
|
217
|
+
indeterminate.add(path);
|
|
218
|
+
}
|
|
219
|
+
else if (indeterminateCount > 0) {
|
|
220
|
+
// If any child is indeterminate, parent is indeterminate
|
|
221
|
+
indeterminate.add(path);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return indeterminate;
|
|
225
|
+
}
|
|
120
226
|
/**
|
|
121
227
|
* Get all descendant paths of a node
|
|
122
228
|
*/
|
|
@@ -131,10 +237,12 @@ export function getDescendantPaths(path, nodes, separator = ':') {
|
|
|
131
237
|
}
|
|
132
238
|
/**
|
|
133
239
|
* Build initial TreeState from flattened nodes
|
|
240
|
+
* Works with both regular Map/Set and SvelteMap/SvelteSet
|
|
134
241
|
*/
|
|
135
242
|
export function buildInitialState(nodes, config) {
|
|
136
|
-
|
|
137
|
-
const
|
|
243
|
+
// Use SvelteSet for reactive collections
|
|
244
|
+
const checkedPaths = new SvelteSet();
|
|
245
|
+
const expandedPaths = new SvelteSet();
|
|
138
246
|
const rootPaths = [];
|
|
139
247
|
nodes.forEach((nodeState, path) => {
|
|
140
248
|
if (nodeState.checked) {
|
|
@@ -147,7 +255,7 @@ export function buildInitialState(nodes, config) {
|
|
|
147
255
|
rootPaths.push(path);
|
|
148
256
|
}
|
|
149
257
|
});
|
|
150
|
-
const indeterminatePaths =
|
|
258
|
+
const indeterminatePaths = calculateIndeterminateStatesAsSvelteSet(nodes, checkedPaths);
|
|
151
259
|
return {
|
|
152
260
|
nodes,
|
|
153
261
|
checkedPaths,
|
|
@@ -186,6 +294,7 @@ export function saveStateToStorage(namespace, state) {
|
|
|
186
294
|
}
|
|
187
295
|
/**
|
|
188
296
|
* Load state from localStorage
|
|
297
|
+
* Returns SvelteSet for reactive state management
|
|
189
298
|
*/
|
|
190
299
|
export function loadStateFromStorage(namespace, state) {
|
|
191
300
|
if (!namespace)
|
|
@@ -196,9 +305,9 @@ export function loadStateFromStorage(namespace, state) {
|
|
|
196
305
|
const updates = {};
|
|
197
306
|
if (checkedJson) {
|
|
198
307
|
const checkedArray = JSON.parse(checkedJson);
|
|
199
|
-
updates.checkedPaths = new
|
|
308
|
+
updates.checkedPaths = new SvelteSet(checkedArray);
|
|
200
309
|
// Recalculate indeterminate states
|
|
201
|
-
updates.indeterminatePaths =
|
|
310
|
+
updates.indeterminatePaths = calculateIndeterminateStatesAsSvelteSet(state.nodes, updates.checkedPaths);
|
|
202
311
|
log('📂 Loaded checked paths from localStorage', {
|
|
203
312
|
namespace,
|
|
204
313
|
checkedCount: checkedArray.length
|
|
@@ -206,7 +315,7 @@ export function loadStateFromStorage(namespace, state) {
|
|
|
206
315
|
}
|
|
207
316
|
if (expandedJson) {
|
|
208
317
|
const expandedArray = JSON.parse(expandedJson);
|
|
209
|
-
updates.expandedPaths = new
|
|
318
|
+
updates.expandedPaths = new SvelteSet(expandedArray);
|
|
210
319
|
log('📂 Loaded expanded paths from localStorage', {
|
|
211
320
|
namespace,
|
|
212
321
|
expandedCount: expandedArray.length
|