@smartnet360/svelte-components 0.0.51 → 0.0.53
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/site-check/transforms.js +2 -2
- package/dist/core/Charts/ChartCard.svelte +6 -1
- package/dist/core/Charts/ChartComponent.svelte +8 -3
- package/dist/core/Charts/GlobalControls.svelte +116 -14
- package/dist/core/Charts/adapt.js +1 -1
- package/dist/core/Charts/charts.model.d.ts +3 -0
- package/dist/core/FeatureRegistry/index.js +1 -1
- package/dist/core/index.d.ts +0 -1
- package/dist/core/index.js +2 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/map/controls/MapControl.svelte +204 -0
- package/dist/map/controls/MapControl.svelte.d.ts +17 -0
- package/dist/map/controls/SiteFilterControl.svelte +126 -0
- package/dist/map/controls/SiteFilterControl.svelte.d.ts +16 -0
- package/dist/map/demo/DemoMap.svelte +98 -0
- package/dist/map/demo/DemoMap.svelte.d.ts +12 -0
- package/dist/map/demo/demo-data.d.ts +12 -0
- package/dist/map/demo/demo-data.js +220 -0
- package/dist/map/hooks/useCellData.d.ts +14 -0
- package/dist/map/hooks/useCellData.js +29 -0
- package/dist/map/hooks/useMapbox.d.ts +14 -0
- package/dist/map/hooks/useMapbox.js +29 -0
- package/dist/map/index.d.ts +27 -0
- package/dist/map/index.js +47 -0
- package/dist/map/layers/CellsLayer.svelte +242 -0
- package/dist/map/layers/CellsLayer.svelte.d.ts +21 -0
- package/dist/map/layers/CoverageLayer.svelte +37 -0
- package/dist/map/layers/CoverageLayer.svelte.d.ts +9 -0
- package/dist/map/layers/LayerBase.d.ts +42 -0
- package/dist/map/layers/LayerBase.js +58 -0
- package/dist/map/layers/SitesLayer.svelte +282 -0
- package/dist/map/layers/SitesLayer.svelte.d.ts +19 -0
- package/dist/map/providers/CellDataProvider.svelte +43 -0
- package/dist/map/providers/CellDataProvider.svelte.d.ts +12 -0
- package/dist/map/providers/MapboxProvider.svelte +38 -0
- package/dist/map/providers/MapboxProvider.svelte.d.ts +9 -0
- package/dist/map/providers/providerHelpers.d.ts +17 -0
- package/dist/map/providers/providerHelpers.js +26 -0
- package/dist/map/stores/cellDataStore.d.ts +21 -0
- package/dist/map/stores/cellDataStore.js +53 -0
- package/dist/map/stores/interactions.d.ts +20 -0
- package/dist/map/stores/interactions.js +33 -0
- package/dist/map/stores/mapStore.d.ts +8 -0
- package/dist/map/stores/mapStore.js +10 -0
- package/dist/map/types.d.ts +115 -0
- package/dist/map/types.js +10 -0
- package/dist/map/utils/geojson.d.ts +20 -0
- package/dist/map/utils/geojson.js +78 -0
- package/dist/map/utils/mapboxHelpers.d.ts +51 -0
- package/dist/map/utils/mapboxHelpers.js +98 -0
- package/dist/map/utils/math.d.ts +40 -0
- package/dist/map/utils/math.js +95 -0
- package/dist/map/utils/siteTreeUtils.d.ts +27 -0
- package/dist/map/utils/siteTreeUtils.js +164 -0
- package/package.json +1 -1
- package/dist/core/Map/Map.svelte +0 -312
- package/dist/core/Map/Map.svelte.d.ts +0 -230
- package/dist/core/Map/index.d.ts +0 -9
- package/dist/core/Map/index.js +0 -9
- package/dist/core/Map/mapSettings.d.ts +0 -147
- package/dist/core/Map/mapSettings.js +0 -226
- package/dist/core/Map/mapStore.d.ts +0 -73
- package/dist/core/Map/mapStore.js +0 -136
- package/dist/core/Map/types.d.ts +0 -72
- package/dist/core/Map/types.js +0 -32
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mapbox GL JS helper utilities for layer and source management
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Safely adds a source to the map if it doesn't already exist
|
|
6
|
+
*/
|
|
7
|
+
export function addSourceIfMissing(map, sourceId, sourceSpec) {
|
|
8
|
+
if (!map.getSource(sourceId)) {
|
|
9
|
+
map.addSource(sourceId, sourceSpec);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Safely removes a source from the map if it exists
|
|
14
|
+
*/
|
|
15
|
+
export function removeSourceIfExists(map, sourceId) {
|
|
16
|
+
if (map.getSource(sourceId)) {
|
|
17
|
+
map.removeSource(sourceId);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Safely adds a layer to the map if it doesn't already exist
|
|
22
|
+
*/
|
|
23
|
+
export function addLayerIfMissing(map, layer, beforeId) {
|
|
24
|
+
if (!map.getLayer(layer.id)) {
|
|
25
|
+
map.addLayer(layer, beforeId);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Safely removes a layer from the map if it exists
|
|
30
|
+
*/
|
|
31
|
+
export function removeLayerIfExists(map, layerId) {
|
|
32
|
+
if (map.getLayer(layerId)) {
|
|
33
|
+
map.removeLayer(layerId);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Updates GeoJSON source data if the source exists
|
|
38
|
+
*/
|
|
39
|
+
export function updateGeoJSONSource(map, sourceId, data) {
|
|
40
|
+
const source = map.getSource(sourceId);
|
|
41
|
+
if (source && source.type === 'geojson') {
|
|
42
|
+
source.setData(data);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Removes a layer and its associated source
|
|
47
|
+
*/
|
|
48
|
+
export function removeLayerAndSource(map, layerId, sourceId) {
|
|
49
|
+
removeLayerIfExists(map, layerId);
|
|
50
|
+
removeSourceIfExists(map, sourceId);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Checks if the map style is loaded
|
|
54
|
+
*/
|
|
55
|
+
export function isStyleLoaded(map) {
|
|
56
|
+
return map.isStyleLoaded();
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Waits for the map style to be loaded
|
|
60
|
+
*/
|
|
61
|
+
export async function waitForStyleLoad(map) {
|
|
62
|
+
if (isStyleLoaded(map)) {
|
|
63
|
+
return Promise.resolve();
|
|
64
|
+
}
|
|
65
|
+
return new Promise((resolve) => {
|
|
66
|
+
map.once('styledata', () => resolve());
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Sets a feature state on a source
|
|
71
|
+
*/
|
|
72
|
+
export function setFeatureState(map, sourceId, featureId, state) {
|
|
73
|
+
map.setFeatureState({
|
|
74
|
+
source: sourceId,
|
|
75
|
+
id: featureId
|
|
76
|
+
}, state);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Removes a feature state from a source
|
|
80
|
+
*/
|
|
81
|
+
export function removeFeatureState(map, sourceId, featureId, key) {
|
|
82
|
+
map.removeFeatureState({
|
|
83
|
+
source: sourceId,
|
|
84
|
+
id: featureId
|
|
85
|
+
}, key);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Generates a unique layer ID with a namespace prefix
|
|
89
|
+
*/
|
|
90
|
+
export function generateLayerId(namespace, layerName) {
|
|
91
|
+
return `${namespace}:${layerName}`;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Generates a unique source ID with a namespace prefix
|
|
95
|
+
*/
|
|
96
|
+
export function generateSourceId(namespace, sourceName) {
|
|
97
|
+
return `${namespace}:${sourceName}`;
|
|
98
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mathematical utilities for cellular sector geometry
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Calculates the destination point given start point, bearing, and distance
|
|
6
|
+
* Uses Haversine formula
|
|
7
|
+
*
|
|
8
|
+
* @param lon - Starting longitude
|
|
9
|
+
* @param lat - Starting latitude
|
|
10
|
+
* @param bearing - Bearing in degrees (0 = North, 90 = East)
|
|
11
|
+
* @param distance - Distance in meters
|
|
12
|
+
* @returns [lon, lat] of destination point
|
|
13
|
+
*/
|
|
14
|
+
export declare function destinationPoint(lon: number, lat: number, bearing: number, distance: number): [number, number];
|
|
15
|
+
/**
|
|
16
|
+
* Creates a polygon representing a cellular sector
|
|
17
|
+
*
|
|
18
|
+
* @param centerLon - Center point longitude
|
|
19
|
+
* @param centerLat - Center point latitude
|
|
20
|
+
* @param azimuth - Sector azimuth/bearing in degrees (0-360, where 0 is North)
|
|
21
|
+
* @param beamwidth - Sector beamwidth in degrees (e.g., 65, 120)
|
|
22
|
+
* @param radius - Sector radius in meters
|
|
23
|
+
* @param arcPoints - Number of points to use for the arc (default: 32)
|
|
24
|
+
* @returns Array of [lon, lat] coordinates forming a closed polygon
|
|
25
|
+
*/
|
|
26
|
+
export declare function createSectorPolygon(centerLon: number, centerLat: number, azimuth: number, beamwidth: number, radius: number, arcPoints?: number): [number, number][];
|
|
27
|
+
/**
|
|
28
|
+
* Creates a circle polygon (360-degree sector)
|
|
29
|
+
*
|
|
30
|
+
* @param centerLon - Center point longitude
|
|
31
|
+
* @param centerLat - Center point latitude
|
|
32
|
+
* @param radius - Circle radius in meters
|
|
33
|
+
* @param points - Number of points to use for the circle (default: 64)
|
|
34
|
+
* @returns Array of [lon, lat] coordinates forming a closed polygon
|
|
35
|
+
*/
|
|
36
|
+
export declare function createCirclePolygon(centerLon: number, centerLat: number, radius: number, points?: number): [number, number][];
|
|
37
|
+
/**
|
|
38
|
+
* Normalizes an azimuth to 0-360 range
|
|
39
|
+
*/
|
|
40
|
+
export declare function normalizeAzimuth(azimuth: number): number;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mathematical utilities for cellular sector geometry
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Converts degrees to radians
|
|
6
|
+
*/
|
|
7
|
+
function toRadians(degrees) {
|
|
8
|
+
return (degrees * Math.PI) / 180;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Converts radians to degrees
|
|
12
|
+
*/
|
|
13
|
+
function toDegrees(radians) {
|
|
14
|
+
return (radians * 180) / Math.PI;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Calculates the destination point given start point, bearing, and distance
|
|
18
|
+
* Uses Haversine formula
|
|
19
|
+
*
|
|
20
|
+
* @param lon - Starting longitude
|
|
21
|
+
* @param lat - Starting latitude
|
|
22
|
+
* @param bearing - Bearing in degrees (0 = North, 90 = East)
|
|
23
|
+
* @param distance - Distance in meters
|
|
24
|
+
* @returns [lon, lat] of destination point
|
|
25
|
+
*/
|
|
26
|
+
export function destinationPoint(lon, lat, bearing, distance) {
|
|
27
|
+
const R = 6371e3; // Earth radius in meters
|
|
28
|
+
const δ = distance / R; // Angular distance
|
|
29
|
+
const θ = toRadians(bearing);
|
|
30
|
+
const φ1 = toRadians(lat);
|
|
31
|
+
const λ1 = toRadians(lon);
|
|
32
|
+
const φ2 = Math.asin(Math.sin(φ1) * Math.cos(δ) + Math.cos(φ1) * Math.sin(δ) * Math.cos(θ));
|
|
33
|
+
const λ2 = λ1 +
|
|
34
|
+
Math.atan2(Math.sin(θ) * Math.sin(δ) * Math.cos(φ1), Math.cos(δ) - Math.sin(φ1) * Math.sin(φ2));
|
|
35
|
+
return [toDegrees(λ2), toDegrees(φ2)];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Creates a polygon representing a cellular sector
|
|
39
|
+
*
|
|
40
|
+
* @param centerLon - Center point longitude
|
|
41
|
+
* @param centerLat - Center point latitude
|
|
42
|
+
* @param azimuth - Sector azimuth/bearing in degrees (0-360, where 0 is North)
|
|
43
|
+
* @param beamwidth - Sector beamwidth in degrees (e.g., 65, 120)
|
|
44
|
+
* @param radius - Sector radius in meters
|
|
45
|
+
* @param arcPoints - Number of points to use for the arc (default: 32)
|
|
46
|
+
* @returns Array of [lon, lat] coordinates forming a closed polygon
|
|
47
|
+
*/
|
|
48
|
+
export function createSectorPolygon(centerLon, centerLat, azimuth, beamwidth, radius, arcPoints = 32) {
|
|
49
|
+
const coordinates = [];
|
|
50
|
+
// Start at the center point
|
|
51
|
+
coordinates.push([centerLon, centerLat]);
|
|
52
|
+
// Calculate start and end bearings
|
|
53
|
+
const startBearing = azimuth - beamwidth / 2;
|
|
54
|
+
const endBearing = azimuth + beamwidth / 2;
|
|
55
|
+
// Generate arc points
|
|
56
|
+
const angleStep = beamwidth / (arcPoints - 1);
|
|
57
|
+
for (let i = 0; i < arcPoints; i++) {
|
|
58
|
+
const bearing = startBearing + angleStep * i;
|
|
59
|
+
const point = destinationPoint(centerLon, centerLat, bearing, radius);
|
|
60
|
+
coordinates.push(point);
|
|
61
|
+
}
|
|
62
|
+
// Close the polygon by returning to center
|
|
63
|
+
coordinates.push([centerLon, centerLat]);
|
|
64
|
+
return coordinates;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Creates a circle polygon (360-degree sector)
|
|
68
|
+
*
|
|
69
|
+
* @param centerLon - Center point longitude
|
|
70
|
+
* @param centerLat - Center point latitude
|
|
71
|
+
* @param radius - Circle radius in meters
|
|
72
|
+
* @param points - Number of points to use for the circle (default: 64)
|
|
73
|
+
* @returns Array of [lon, lat] coordinates forming a closed polygon
|
|
74
|
+
*/
|
|
75
|
+
export function createCirclePolygon(centerLon, centerLat, radius, points = 64) {
|
|
76
|
+
const coordinates = [];
|
|
77
|
+
const angleStep = 360 / points;
|
|
78
|
+
for (let i = 0; i < points; i++) {
|
|
79
|
+
const bearing = angleStep * i;
|
|
80
|
+
const point = destinationPoint(centerLon, centerLat, bearing, radius);
|
|
81
|
+
coordinates.push(point);
|
|
82
|
+
}
|
|
83
|
+
// Close the polygon
|
|
84
|
+
coordinates.push(coordinates[0]);
|
|
85
|
+
return coordinates;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Normalizes an azimuth to 0-360 range
|
|
89
|
+
*/
|
|
90
|
+
export function normalizeAzimuth(azimuth) {
|
|
91
|
+
let normalized = azimuth % 360;
|
|
92
|
+
if (normalized < 0)
|
|
93
|
+
normalized += 360;
|
|
94
|
+
return normalized;
|
|
95
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilities for building and managing site filter tree
|
|
3
|
+
*/
|
|
4
|
+
import type { Site } from '../types';
|
|
5
|
+
import type { TreeNode } from '../../core/TreeView/tree.model';
|
|
6
|
+
/**
|
|
7
|
+
* Builds a hierarchical tree from flat site array
|
|
8
|
+
* Structure: All Sites -> Provider -> Feature Group
|
|
9
|
+
*/
|
|
10
|
+
export declare function buildSiteTree(sites: Site[]): TreeNode;
|
|
11
|
+
/**
|
|
12
|
+
* Filters sites based on checked tree paths
|
|
13
|
+
*/
|
|
14
|
+
export declare function getFilteredSites(checkedPaths: string[], allSites: Site[]): Site[];
|
|
15
|
+
/**
|
|
16
|
+
* Saves tree checked paths to localStorage
|
|
17
|
+
*/
|
|
18
|
+
export declare function saveTreeState(checkedPaths: string[]): void;
|
|
19
|
+
/**
|
|
20
|
+
* Loads tree state from localStorage
|
|
21
|
+
* Returns map of node IDs to checked state
|
|
22
|
+
*/
|
|
23
|
+
export declare function loadTreeState(): Record<string, boolean>;
|
|
24
|
+
/**
|
|
25
|
+
* Clears saved tree state
|
|
26
|
+
*/
|
|
27
|
+
export declare function clearTreeState(): void;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utilities for building and managing site filter tree
|
|
3
|
+
*/
|
|
4
|
+
const STORAGE_PREFIX = 'cellular:siteFilter';
|
|
5
|
+
/**
|
|
6
|
+
* Builds a hierarchical tree from flat site array
|
|
7
|
+
* Structure: All Sites -> Provider -> Feature Group
|
|
8
|
+
*/
|
|
9
|
+
export function buildSiteTree(sites) {
|
|
10
|
+
// Group sites by provider, then by feature group
|
|
11
|
+
const providerGroups = new Map();
|
|
12
|
+
sites.forEach((site) => {
|
|
13
|
+
if (!providerGroups.has(site.provider)) {
|
|
14
|
+
providerGroups.set(site.provider, new Map());
|
|
15
|
+
}
|
|
16
|
+
const featureGroups = providerGroups.get(site.provider);
|
|
17
|
+
if (!featureGroups.has(site.featureGroup)) {
|
|
18
|
+
featureGroups.set(site.featureGroup, []);
|
|
19
|
+
}
|
|
20
|
+
featureGroups.get(site.featureGroup).push(site);
|
|
21
|
+
});
|
|
22
|
+
// Load saved state from localStorage
|
|
23
|
+
const savedState = loadTreeState();
|
|
24
|
+
// Build tree structure
|
|
25
|
+
const children = [];
|
|
26
|
+
// Sort providers alphabetically
|
|
27
|
+
const sortedProviders = Array.from(providerGroups.keys()).sort();
|
|
28
|
+
sortedProviders.forEach((provider) => {
|
|
29
|
+
const featureGroups = providerGroups.get(provider);
|
|
30
|
+
const providerChildren = [];
|
|
31
|
+
// Sort feature groups alphabetically
|
|
32
|
+
const sortedFeatureGroups = Array.from(featureGroups.keys()).sort();
|
|
33
|
+
sortedFeatureGroups.forEach((featureGroup) => {
|
|
34
|
+
const groupSites = featureGroups.get(featureGroup);
|
|
35
|
+
const nodeId = `${provider}:${featureGroup}`;
|
|
36
|
+
providerChildren.push({
|
|
37
|
+
id: nodeId,
|
|
38
|
+
label: `${featureGroup} (${groupSites.length})`,
|
|
39
|
+
defaultChecked: savedState[nodeId] ?? true, // Default checked
|
|
40
|
+
children: [],
|
|
41
|
+
metadata: {
|
|
42
|
+
type: 'featureGroup',
|
|
43
|
+
provider,
|
|
44
|
+
featureGroup,
|
|
45
|
+
siteIds: groupSites.map((s) => s.id)
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
const providerId = provider;
|
|
50
|
+
children.push({
|
|
51
|
+
id: providerId,
|
|
52
|
+
label: `${provider} (${Array.from(featureGroups.values()).flat().length})`,
|
|
53
|
+
defaultChecked: savedState[providerId] ?? true, // Default checked
|
|
54
|
+
children: providerChildren,
|
|
55
|
+
metadata: {
|
|
56
|
+
type: 'provider',
|
|
57
|
+
provider
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
// Root node
|
|
62
|
+
return {
|
|
63
|
+
id: 'all-sites',
|
|
64
|
+
label: `All Sites (${sites.length})`,
|
|
65
|
+
defaultChecked: savedState['all-sites'] ?? true, // Default checked
|
|
66
|
+
children,
|
|
67
|
+
metadata: {
|
|
68
|
+
type: 'root'
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Filters sites based on checked tree paths
|
|
74
|
+
*/
|
|
75
|
+
export function getFilteredSites(checkedPaths, allSites) {
|
|
76
|
+
console.log('getFilteredSites called with paths:', checkedPaths);
|
|
77
|
+
const checkedGroups = new Set();
|
|
78
|
+
const checkedProviders = new Set();
|
|
79
|
+
checkedPaths.forEach((path) => {
|
|
80
|
+
// TreeView adds parent prefixes, so paths look like:
|
|
81
|
+
// "all-sites:Verizon:Verizon:Urban Macro"
|
|
82
|
+
// We need to extract the actual provider:featureGroup
|
|
83
|
+
// Split by separator
|
|
84
|
+
const parts = path.split(':');
|
|
85
|
+
// Remove "all-sites" prefix if present
|
|
86
|
+
const filteredParts = parts.filter(p => p !== 'all-sites');
|
|
87
|
+
if (filteredParts.length === 3) {
|
|
88
|
+
// Format: [provider, provider, featureGroup]
|
|
89
|
+
// The provider is duplicated, extract it as provider:featureGroup
|
|
90
|
+
const provider = filteredParts[1];
|
|
91
|
+
const featureGroup = filteredParts[2];
|
|
92
|
+
checkedGroups.add(`${provider}:${featureGroup}`);
|
|
93
|
+
}
|
|
94
|
+
else if (filteredParts.length === 1) {
|
|
95
|
+
// Just a provider
|
|
96
|
+
checkedProviders.add(filteredParts[0]);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
console.log('Checked groups:', Array.from(checkedGroups));
|
|
100
|
+
console.log('Checked providers:', Array.from(checkedProviders));
|
|
101
|
+
// If "all-sites" is checked and it's the only thing, return all
|
|
102
|
+
if (checkedPaths.includes('all-sites') && checkedGroups.size === 0 && checkedProviders.size === 0) {
|
|
103
|
+
console.log('All sites root checked, returning all');
|
|
104
|
+
return allSites;
|
|
105
|
+
}
|
|
106
|
+
// If nothing is checked, return empty
|
|
107
|
+
if (checkedGroups.size === 0 && checkedProviders.size === 0) {
|
|
108
|
+
console.log('Nothing checked, returning empty');
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
// Filter sites based on checked groups or providers
|
|
112
|
+
const filtered = allSites.filter((site) => {
|
|
113
|
+
const key = `${site.provider}:${site.featureGroup}`;
|
|
114
|
+
// Include if the specific group is checked OR if the provider is checked
|
|
115
|
+
const included = checkedGroups.has(key) || checkedProviders.has(site.provider);
|
|
116
|
+
return included;
|
|
117
|
+
});
|
|
118
|
+
console.log('Filtered:', filtered.length, 'of', allSites.length, 'sites');
|
|
119
|
+
return filtered;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Saves tree checked paths to localStorage
|
|
123
|
+
*/
|
|
124
|
+
export function saveTreeState(checkedPaths) {
|
|
125
|
+
try {
|
|
126
|
+
localStorage.setItem(STORAGE_PREFIX, JSON.stringify(checkedPaths));
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
console.warn('Failed to save tree state to localStorage:', error);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Loads tree state from localStorage
|
|
134
|
+
* Returns map of node IDs to checked state
|
|
135
|
+
*/
|
|
136
|
+
export function loadTreeState() {
|
|
137
|
+
try {
|
|
138
|
+
const saved = localStorage.getItem(STORAGE_PREFIX);
|
|
139
|
+
if (saved) {
|
|
140
|
+
const paths = JSON.parse(saved);
|
|
141
|
+
// Convert paths to map for easier lookup during tree building
|
|
142
|
+
const stateMap = {};
|
|
143
|
+
paths.forEach((path) => {
|
|
144
|
+
stateMap[path] = true;
|
|
145
|
+
});
|
|
146
|
+
return stateMap;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
console.warn('Failed to load tree state from localStorage:', error);
|
|
151
|
+
}
|
|
152
|
+
return {};
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Clears saved tree state
|
|
156
|
+
*/
|
|
157
|
+
export function clearTreeState() {
|
|
158
|
+
try {
|
|
159
|
+
localStorage.removeItem(STORAGE_PREFIX);
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
console.warn('Failed to clear tree state from localStorage:', error);
|
|
163
|
+
}
|
|
164
|
+
}
|