@smartnet360/svelte-components 0.0.53 → 0.0.55

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.
Files changed (86) hide show
  1. package/dist/core/TreeView/TreeNode.svelte +40 -45
  2. package/dist/core/TreeView/TreeNode.svelte.d.ts +10 -0
  3. package/dist/core/TreeView/TreeView.svelte +14 -2
  4. package/dist/core/TreeView/TreeView.svelte.d.ts +10 -0
  5. package/dist/core/TreeView/tree-utils.d.ts +3 -0
  6. package/dist/core/TreeView/tree-utils.js +33 -9
  7. package/dist/core/TreeView/tree.store.js +49 -24
  8. package/dist/index.d.ts +1 -1
  9. package/dist/index.js +2 -2
  10. package/dist/map-v2/core/controls/MapStyleControl.svelte +289 -0
  11. package/dist/map-v2/core/controls/MapStyleControl.svelte.d.ts +24 -0
  12. package/dist/{map → map-v2/core}/hooks/useMapbox.d.ts +1 -1
  13. package/dist/{map → map-v2/core}/hooks/useMapbox.js +1 -1
  14. package/dist/map-v2/core/index.d.ts +11 -0
  15. package/dist/map-v2/core/index.js +14 -0
  16. package/dist/map-v2/core/providers/MapboxProvider.svelte +140 -0
  17. package/dist/map-v2/core/providers/MapboxProvider.svelte.d.ts +33 -0
  18. package/dist/{map → map-v2/core}/stores/mapStore.d.ts +2 -2
  19. package/dist/{map → map-v2/core}/stores/mapStore.js +2 -2
  20. package/dist/map-v2/core/types.d.ts +13 -0
  21. package/dist/map-v2/core/types.js +7 -0
  22. package/dist/map-v2/demo/DemoMap.svelte +63 -0
  23. package/dist/{map → map-v2}/demo/DemoMap.svelte.d.ts +3 -4
  24. package/dist/map-v2/demo/demo-data.d.ts +8 -0
  25. package/dist/map-v2/demo/demo-data.js +128 -0
  26. package/dist/map-v2/demo/index.d.ts +7 -0
  27. package/dist/map-v2/demo/index.js +9 -0
  28. package/dist/{map → map-v2/features/sites}/controls/SiteFilterControl.svelte +27 -41
  29. package/dist/{map → map-v2/features/sites}/controls/SiteFilterControl.svelte.d.ts +4 -6
  30. package/dist/map-v2/features/sites/controls/SiteSizeSlider.svelte +185 -0
  31. package/dist/map-v2/features/sites/controls/SiteSizeSlider.svelte.d.ts +20 -0
  32. package/dist/map-v2/features/sites/index.d.ts +14 -0
  33. package/dist/map-v2/features/sites/index.js +16 -0
  34. package/dist/map-v2/features/sites/layers/SitesLayer.svelte +277 -0
  35. package/dist/map-v2/features/sites/layers/SitesLayer.svelte.d.ts +12 -0
  36. package/dist/map-v2/features/sites/stores/siteStore.d.ts +18 -0
  37. package/dist/map-v2/features/sites/stores/siteStore.js +36 -0
  38. package/dist/map-v2/features/sites/stores/siteStoreContext.svelte.d.ts +29 -0
  39. package/dist/map-v2/features/sites/stores/siteStoreContext.svelte.js +73 -0
  40. package/dist/map-v2/features/sites/types.d.ts +36 -0
  41. package/dist/map-v2/features/sites/types.js +4 -0
  42. package/dist/map-v2/features/sites/utils/siteGeoJSON.d.ts +31 -0
  43. package/dist/map-v2/features/sites/utils/siteGeoJSON.js +34 -0
  44. package/dist/map-v2/features/sites/utils/siteTreeUtils.d.ts +14 -0
  45. package/dist/{map → map-v2/features/sites}/utils/siteTreeUtils.js +3 -50
  46. package/dist/map-v2/index.d.ts +10 -0
  47. package/dist/map-v2/index.js +22 -0
  48. package/dist/{map → map-v2/shared}/controls/MapControl.svelte +1 -1
  49. package/dist/map-v2/shared/index.d.ts +7 -0
  50. package/dist/map-v2/shared/index.js +9 -0
  51. package/package.json +1 -1
  52. package/dist/map/demo/DemoMap.svelte +0 -98
  53. package/dist/map/demo/demo-data.d.ts +0 -12
  54. package/dist/map/demo/demo-data.js +0 -220
  55. package/dist/map/hooks/useCellData.d.ts +0 -14
  56. package/dist/map/hooks/useCellData.js +0 -29
  57. package/dist/map/index.d.ts +0 -27
  58. package/dist/map/index.js +0 -47
  59. package/dist/map/layers/CellsLayer.svelte +0 -242
  60. package/dist/map/layers/CellsLayer.svelte.d.ts +0 -21
  61. package/dist/map/layers/CoverageLayer.svelte +0 -37
  62. package/dist/map/layers/CoverageLayer.svelte.d.ts +0 -9
  63. package/dist/map/layers/LayerBase.d.ts +0 -42
  64. package/dist/map/layers/LayerBase.js +0 -58
  65. package/dist/map/layers/SitesLayer.svelte +0 -282
  66. package/dist/map/layers/SitesLayer.svelte.d.ts +0 -19
  67. package/dist/map/providers/CellDataProvider.svelte +0 -43
  68. package/dist/map/providers/CellDataProvider.svelte.d.ts +0 -12
  69. package/dist/map/providers/MapboxProvider.svelte +0 -38
  70. package/dist/map/providers/MapboxProvider.svelte.d.ts +0 -9
  71. package/dist/map/providers/providerHelpers.d.ts +0 -17
  72. package/dist/map/providers/providerHelpers.js +0 -26
  73. package/dist/map/stores/cellDataStore.d.ts +0 -21
  74. package/dist/map/stores/cellDataStore.js +0 -53
  75. package/dist/map/stores/interactions.d.ts +0 -20
  76. package/dist/map/stores/interactions.js +0 -33
  77. package/dist/map/types.d.ts +0 -115
  78. package/dist/map/types.js +0 -10
  79. package/dist/map/utils/geojson.d.ts +0 -20
  80. package/dist/map/utils/geojson.js +0 -78
  81. package/dist/map/utils/math.d.ts +0 -40
  82. package/dist/map/utils/math.js +0 -95
  83. package/dist/map/utils/siteTreeUtils.d.ts +0 -27
  84. /package/dist/{map → map-v2/shared}/controls/MapControl.svelte.d.ts +0 -0
  85. /package/dist/{map → map-v2/shared}/utils/mapboxHelpers.d.ts +0 -0
  86. /package/dist/{map → map-v2/shared}/utils/mapboxHelpers.js +0 -0
@@ -0,0 +1,277 @@
1
+ <script lang="ts">
2
+ /**
3
+ * SitesLayer - Visualizes cellular sites as circles on the map
4
+ *
5
+ * Reads from siteStore to get filtered sites and visual properties.
6
+ * Completely independent from cell data.
7
+ *
8
+ * Features:
9
+ * - Displays sites as circles with configurable size, color, opacity
10
+ * - Supports labels (optional)
11
+ * - Click to show popup
12
+ * - Hover effects
13
+ */
14
+ import { onMount, onDestroy } from 'svelte';
15
+ import mapboxgl from 'mapbox-gl';
16
+ import { useMapbox } from '../../../core/hooks/useMapbox';
17
+ import { sitesToGeoJSON } from '../utils/siteGeoJSON';
18
+ import {
19
+ addSourceIfMissing,
20
+ addLayerIfMissing,
21
+ updateGeoJSONSource,
22
+ removeLayerAndSource,
23
+ generateLayerId,
24
+ generateSourceId
25
+ } from '../../../shared/utils/mapboxHelpers';
26
+ import type { SiteStoreContext } from '../stores/siteStoreContext.svelte';
27
+
28
+ interface Props {
29
+ /** Site store instance */
30
+ store: SiteStoreContext;
31
+ /** Namespace for layer IDs (default: 'sites') */
32
+ namespace?: string;
33
+ /** Enable click to show popup (default: true) */
34
+ clickable?: boolean;
35
+ }
36
+
37
+ let {
38
+ store,
39
+ namespace = 'sites',
40
+ clickable = true
41
+ }: Props = $props();
42
+
43
+ const mapStore = useMapbox();
44
+
45
+ let map: mapboxgl.Map | null = null;
46
+ let popup: mapboxgl.Popup | null = null;
47
+ let hoveredSiteId: string | null = null;
48
+
49
+ const layerId = generateLayerId(namespace, 'circles');
50
+ const labelLayerId = generateLayerId(namespace, 'labels');
51
+ const sourceId = generateSourceId(namespace, 'source');
52
+
53
+ // Watch store changes and update layer
54
+ $effect(() => {
55
+ // Access store properties to track them
56
+ const filteredSites = store.filteredSites;
57
+ const size = store.size;
58
+ const color = store.color;
59
+ const opacity = store.opacity;
60
+ const showLabels = store.showLabels;
61
+ const labelSize = store.labelSize;
62
+ const labelColor = store.labelColor;
63
+
64
+ if (map) {
65
+ updateLayer();
66
+ }
67
+ });
68
+
69
+ onMount(() => {
70
+ const mapUnsub = mapStore.subscribe((m: mapboxgl.Map | null) => {
71
+ if (!m) return;
72
+ map = m;
73
+ // Map is guaranteed to be ready (style loaded) by MapboxProvider
74
+ initializeLayer();
75
+
76
+ // Re-initialize layer when map style changes
77
+ // Mapbox removes all custom layers/sources when setStyle() is called
78
+ map.on('style.load', initializeLayer);
79
+ });
80
+
81
+ return () => {
82
+ mapUnsub();
83
+ cleanup();
84
+ };
85
+ });
86
+
87
+ onDestroy(() => {
88
+ cleanup();
89
+ });
90
+
91
+ function initializeLayer(): void {
92
+ if (!map) return;
93
+
94
+ // Add empty GeoJSON source
95
+ addSourceIfMissing(map, sourceId, {
96
+ type: 'geojson',
97
+ data: {
98
+ type: 'FeatureCollection',
99
+ features: []
100
+ }
101
+ });
102
+
103
+ // Add circle layer with current store values
104
+ addLayerIfMissing(map, {
105
+ id: layerId,
106
+ type: 'circle',
107
+ source: sourceId,
108
+ paint: {
109
+ 'circle-radius': store.size,
110
+ 'circle-color': store.color,
111
+ 'circle-opacity': store.opacity,
112
+ 'circle-stroke-width': 2,
113
+ 'circle-stroke-color': '#ffffff',
114
+ 'circle-stroke-opacity': store.opacity
115
+ }
116
+ });
117
+
118
+ // Add label layer
119
+ addLayerIfMissing(map, {
120
+ id: labelLayerId,
121
+ type: 'symbol',
122
+ source: sourceId,
123
+ layout: {
124
+ 'text-field': ['get', 'name'],
125
+ 'text-size': store.labelSize,
126
+ 'text-offset': [0, 1.5],
127
+ 'text-anchor': 'top',
128
+ 'text-font': ['Open Sans Regular', 'Arial Unicode MS Regular']
129
+ },
130
+ paint: {
131
+ 'text-color': store.labelColor,
132
+ 'text-halo-color': '#fff',
133
+ 'text-halo-width': 1.5
134
+ }
135
+ });
136
+
137
+ // Set initial visibility
138
+ map.setLayoutProperty(labelLayerId, 'visibility', store.showLabels ? 'visible' : 'none');
139
+
140
+ // Add event handlers
141
+ if (clickable) {
142
+ map.on('click', layerId, handleClick);
143
+ map.on('mouseenter', layerId, handleMouseEnter);
144
+ map.on('mouseleave', layerId, handleMouseLeave);
145
+ }
146
+
147
+ // Initial data load
148
+ updateLayer();
149
+ }
150
+
151
+ function updateLayer(): void {
152
+ if (!map) return;
153
+
154
+ // Update data
155
+ const geojson = sitesToGeoJSON(store.filteredSites);
156
+ updateGeoJSONSource(map, sourceId, geojson);
157
+
158
+ // Update circle visual properties
159
+ map.setPaintProperty(layerId, 'circle-radius', store.size);
160
+ map.setPaintProperty(layerId, 'circle-color', store.color);
161
+ map.setPaintProperty(layerId, 'circle-opacity', store.opacity);
162
+ map.setPaintProperty(layerId, 'circle-stroke-opacity', store.opacity);
163
+
164
+ // Update label properties
165
+ map.setLayoutProperty(labelLayerId, 'text-size', store.labelSize);
166
+ map.setPaintProperty(labelLayerId, 'text-color', store.labelColor);
167
+ map.setLayoutProperty(labelLayerId, 'visibility', store.showLabels ? 'visible' : 'none');
168
+ }
169
+
170
+ function handleClick(e: mapboxgl.MapLayerMouseEvent): void {
171
+ if (!e.features || e.features.length === 0) return;
172
+ const feature = e.features[0];
173
+ const siteProps = feature.properties;
174
+ if (siteProps && e.lngLat) {
175
+ createSitePopup(siteProps, e.lngLat);
176
+ }
177
+ }
178
+
179
+ function createSitePopup(siteProps: any, lngLat: mapboxgl.LngLat): void {
180
+ if (!map) return;
181
+
182
+ // Remove existing popup
183
+ if (popup) {
184
+ popup.remove();
185
+ }
186
+
187
+ // Parse arrays if needed
188
+ const fbands = siteProps.fbands ? JSON.parse(siteProps.fbands) : [];
189
+ const cellNames = siteProps.cellNames ? JSON.parse(siteProps.cellNames) : [];
190
+
191
+ const fbandsHtml = fbands.length > 0
192
+ ? `<div><strong>Bands:</strong> ${fbands.join(', ')} MHz</div>`
193
+ : '';
194
+
195
+ const html = `
196
+ <div style="font-family: system-ui, -apple-system, sans-serif; font-size: 13px;">
197
+ <h6 style="margin: 0 0 8px 0; font-size: 14px; font-weight: 600;">${siteProps.name}</h6>
198
+ <div style="color: #666; line-height: 1.6;">
199
+ <div><strong>ID:</strong> ${siteProps.id}</div>
200
+ <div><strong>Technology:</strong> <span class="badge bg-primary">${siteProps.technology}</span></div>
201
+ ${fbandsHtml}
202
+ <div><strong>Provider:</strong> ${siteProps.provider}</div>
203
+ <div><strong>Group:</strong> ${siteProps.featureGroup}</div>
204
+ <div><strong>Cells:</strong> ${cellNames.length}</div>
205
+ <div style="margin-top: 8px; padding-top: 8px; border-top: 1px solid #eee; font-size: 11px;">
206
+ <strong>Coordinates:</strong><br/>
207
+ ${Number(siteProps.latitude).toFixed(6)}, ${Number(siteProps.longitude).toFixed(6)}
208
+ </div>
209
+ </div>
210
+ </div>
211
+ `;
212
+
213
+ // Create and add popup
214
+ popup = new mapboxgl.Popup({
215
+ closeButton: true,
216
+ closeOnClick: false,
217
+ maxWidth: '300px'
218
+ })
219
+ .setLngLat(lngLat)
220
+ .setHTML(html)
221
+ .addTo(map);
222
+ }
223
+
224
+ function handleMouseEnter(e: mapboxgl.MapLayerMouseEvent): void {
225
+ if (!map) return;
226
+ map.getCanvas().style.cursor = 'pointer';
227
+
228
+ if (!e.features || e.features.length === 0) return;
229
+ const feature = e.features[0];
230
+ const siteId = feature.properties?.id;
231
+
232
+ if (siteId) {
233
+ hoveredSiteId = siteId;
234
+ // Optional: Add hover effect via feature state
235
+ map.setFeatureState(
236
+ { source: sourceId, id: siteId },
237
+ { hovered: true }
238
+ );
239
+ }
240
+ }
241
+
242
+ function handleMouseLeave(): void {
243
+ if (!map) return;
244
+ map.getCanvas().style.cursor = '';
245
+
246
+ if (hoveredSiteId) {
247
+ // Remove hover state
248
+ map.removeFeatureState(
249
+ { source: sourceId, id: hoveredSiteId },
250
+ 'hovered'
251
+ );
252
+ hoveredSiteId = null;
253
+ }
254
+ }
255
+
256
+ function cleanup(): void {
257
+ if (popup) {
258
+ popup.remove();
259
+ popup = null;
260
+ }
261
+
262
+ if (map) {
263
+ // Remove style.load listener
264
+ map.off('style.load', initializeLayer);
265
+
266
+ if (clickable) {
267
+ map.off('click', layerId, handleClick);
268
+ map.off('mouseenter', layerId, handleMouseEnter);
269
+ map.off('mouseleave', layerId, handleMouseLeave);
270
+ }
271
+
272
+ // Remove layers and source
273
+ removeLayerAndSource(map, labelLayerId, sourceId);
274
+ removeLayerAndSource(map, layerId, sourceId);
275
+ }
276
+ }
277
+ </script>
@@ -0,0 +1,12 @@
1
+ import type { SiteStoreContext } from '../stores/siteStoreContext.svelte';
2
+ interface Props {
3
+ /** Site store instance */
4
+ store: SiteStoreContext;
5
+ /** Namespace for layer IDs (default: 'sites') */
6
+ namespace?: string;
7
+ /** Enable click to show popup (default: true) */
8
+ clickable?: boolean;
9
+ }
10
+ declare const SitesLayer: import("svelte").Component<Props, {}, "">;
11
+ type SitesLayer = ReturnType<typeof SitesLayer>;
12
+ export default SitesLayer;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Site store - State management for site feature
3
+ */
4
+ import type { Writable } from 'svelte/store';
5
+ import type { Site, SiteStoreValue } from '../types';
6
+ /**
7
+ * Creates a site store with default values
8
+ */
9
+ export declare function createSiteStore(initialSites?: Site[]): Writable<SiteStoreValue> & {
10
+ setAllSites: (sites: Site[]) => void;
11
+ setFilteredSites: (sites: Site[]) => void;
12
+ setSize: (size: number) => void;
13
+ setColor: (color: string) => void;
14
+ setOpacity: (opacity: number) => void;
15
+ setShowLabels: (show: boolean) => void;
16
+ reset: () => void;
17
+ };
18
+ export type SiteStore = ReturnType<typeof createSiteStore>;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Site store - State management for site feature
3
+ */
4
+ import { writable } from 'svelte/store';
5
+ /**
6
+ * Creates a site store with default values
7
+ */
8
+ export function createSiteStore(initialSites = []) {
9
+ const defaultState = {
10
+ allSites: initialSites,
11
+ filteredSites: initialSites,
12
+ size: 10,
13
+ color: '#3b82f6',
14
+ opacity: 1.0,
15
+ showLabels: false,
16
+ showOnHover: true,
17
+ strokeWidth: 2,
18
+ strokeColor: '#ffffff'
19
+ };
20
+ const { subscribe, update, set } = writable(defaultState);
21
+ return {
22
+ subscribe,
23
+ set,
24
+ update,
25
+ // Data setters
26
+ setAllSites: (sites) => update((s) => ({ ...s, allSites: sites })),
27
+ setFilteredSites: (sites) => update((s) => ({ ...s, filteredSites: sites })),
28
+ // Visual property setters
29
+ setSize: (size) => update((s) => ({ ...s, size })),
30
+ setColor: (color) => update((s) => ({ ...s, color })),
31
+ setOpacity: (opacity) => update((s) => ({ ...s, opacity })),
32
+ setShowLabels: (show) => update((s) => ({ ...s, showLabels: show })),
33
+ // Reset to defaults
34
+ reset: () => set({ ...defaultState, allSites: initialSites, filteredSites: initialSites })
35
+ };
36
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Site Store Context - Bindable reactive state for site feature
3
+ *
4
+ * Uses Svelte 5 runes ($state) to create a directly bindable reactive object.
5
+ * This allows components to use bind:value instead of manual event handlers.
6
+ */
7
+ import type { Site, SiteStoreValue } from '../types';
8
+ export declare function createSiteStoreContext(initialSites?: Site[]): {
9
+ allSites: Site[];
10
+ filteredSites: Site[];
11
+ size: number;
12
+ color: string;
13
+ opacity: number;
14
+ showLabels: boolean;
15
+ showOnHover: boolean;
16
+ labelSize: number;
17
+ labelColor: string;
18
+ strokeWidth: number;
19
+ strokeColor: string;
20
+ setAllSites(sites: Site[]): void;
21
+ setFilteredSites(sites: Site[]): void;
22
+ setSize(size: number): void;
23
+ setColor(color: string): void;
24
+ setOpacity(opacity: number): void;
25
+ setShowLabels(show: boolean): void;
26
+ reset(): void;
27
+ getState(): SiteStoreValue;
28
+ };
29
+ export type SiteStoreContext = ReturnType<typeof createSiteStoreContext>;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Site Store Context - Bindable reactive state for site feature
3
+ *
4
+ * Uses Svelte 5 runes ($state) to create a directly bindable reactive object.
5
+ * This allows components to use bind:value instead of manual event handlers.
6
+ */
7
+ export function createSiteStoreContext(initialSites = []) {
8
+ // Internal reactive state
9
+ let state = $state({
10
+ allSites: initialSites,
11
+ filteredSites: initialSites,
12
+ size: 10,
13
+ color: '#3b82f6',
14
+ opacity: 1.0,
15
+ showLabels: false,
16
+ showOnHover: true,
17
+ labelSize: 12,
18
+ labelColor: '#000000',
19
+ strokeWidth: 2,
20
+ strokeColor: '#ffffff'
21
+ });
22
+ // Return object with getters/setters for direct binding
23
+ return {
24
+ // Bindable properties with getters/setters
25
+ get allSites() { return state.allSites; },
26
+ set allSites(value) { state.allSites = value; },
27
+ get filteredSites() { return state.filteredSites; },
28
+ set filteredSites(value) { state.filteredSites = value; },
29
+ get size() { return state.size; },
30
+ set size(value) { state.size = value; },
31
+ get color() { return state.color; },
32
+ set color(value) { state.color = value; },
33
+ get opacity() { return state.opacity; },
34
+ set opacity(value) { state.opacity = value; },
35
+ get showLabels() { return state.showLabels; },
36
+ set showLabels(value) { state.showLabels = value; },
37
+ get showOnHover() { return state.showOnHover; },
38
+ set showOnHover(value) { state.showOnHover = value; },
39
+ get labelSize() { return state.labelSize; },
40
+ set labelSize(value) { state.labelSize = value; },
41
+ get labelColor() { return state.labelColor; },
42
+ set labelColor(value) { state.labelColor = value; },
43
+ get strokeWidth() { return state.strokeWidth ?? 2; },
44
+ set strokeWidth(value) { state.strokeWidth = value; },
45
+ get strokeColor() { return state.strokeColor ?? '#ffffff'; },
46
+ set strokeColor(value) { state.strokeColor = value; },
47
+ // Convenience methods (optional, but nice to have)
48
+ setAllSites(sites) { state.allSites = sites; },
49
+ setFilteredSites(sites) { state.filteredSites = sites; },
50
+ setSize(size) { state.size = size; },
51
+ setColor(color) { state.color = color; },
52
+ setOpacity(opacity) { state.opacity = opacity; },
53
+ setShowLabels(show) { state.showLabels = show; },
54
+ // Reset to defaults
55
+ reset() {
56
+ state.allSites = initialSites;
57
+ state.filteredSites = initialSites;
58
+ state.size = 10;
59
+ state.color = '#3b82f6';
60
+ state.opacity = 1.0;
61
+ state.showLabels = false;
62
+ state.showOnHover = true;
63
+ state.labelSize = 12;
64
+ state.labelColor = '#000000';
65
+ state.strokeWidth = 2;
66
+ state.strokeColor = '#ffffff';
67
+ },
68
+ // Get snapshot of current state (useful for debugging)
69
+ getState() {
70
+ return { ...state };
71
+ }
72
+ };
73
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Site feature types
3
+ */
4
+ /**
5
+ * Cellular site/tower
6
+ */
7
+ export interface Site {
8
+ id: string;
9
+ name: string;
10
+ latitude: number;
11
+ longitude: number;
12
+ fbands?: string[];
13
+ technology: string;
14
+ properties: Record<string, any>;
15
+ cellNames: string[];
16
+ provider: string;
17
+ featureGroup: string;
18
+ }
19
+ /**
20
+ * Site store state - contains all site data and visual properties
21
+ */
22
+ export interface SiteStoreValue {
23
+ allSites: Site[];
24
+ filteredSites: Site[];
25
+ size: number;
26
+ color: string;
27
+ opacity: number;
28
+ showLabels: boolean;
29
+ showOnHover: boolean;
30
+ labelSize: number;
31
+ labelColor: string;
32
+ hoverColor?: string;
33
+ selectedColor?: string;
34
+ strokeWidth?: number;
35
+ strokeColor?: string;
36
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Site feature types
3
+ */
4
+ export {};
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Site GeoJSON utilities
3
+ */
4
+ import type { Site } from '../types';
5
+ /**
6
+ * GeoJSON Feature for a site (Point geometry)
7
+ */
8
+ export interface SiteFeature {
9
+ type: 'Feature';
10
+ geometry: {
11
+ type: 'Point';
12
+ coordinates: [number, number];
13
+ };
14
+ properties: Site;
15
+ }
16
+ /**
17
+ * GeoJSON FeatureCollection
18
+ */
19
+ export interface FeatureCollection<T> {
20
+ type: 'FeatureCollection';
21
+ features: T[];
22
+ }
23
+ /**
24
+ * Converts an array of sites to a GeoJSON FeatureCollection
25
+ * Sites are represented as Point features (circles will be drawn by Mapbox)
26
+ */
27
+ export declare function sitesToGeoJSON(sites: Site[]): FeatureCollection<SiteFeature>;
28
+ /**
29
+ * Converts a single site to a GeoJSON Feature
30
+ */
31
+ export declare function siteToFeature(site: Site): SiteFeature;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Site GeoJSON utilities
3
+ */
4
+ /**
5
+ * Converts an array of sites to a GeoJSON FeatureCollection
6
+ * Sites are represented as Point features (circles will be drawn by Mapbox)
7
+ */
8
+ export function sitesToGeoJSON(sites) {
9
+ const features = sites.map((site) => ({
10
+ type: 'Feature',
11
+ geometry: {
12
+ type: 'Point',
13
+ coordinates: [site.longitude, site.latitude]
14
+ },
15
+ properties: site
16
+ }));
17
+ return {
18
+ type: 'FeatureCollection',
19
+ features
20
+ };
21
+ }
22
+ /**
23
+ * Converts a single site to a GeoJSON Feature
24
+ */
25
+ export function siteToFeature(site) {
26
+ return {
27
+ type: 'Feature',
28
+ geometry: {
29
+ type: 'Point',
30
+ coordinates: [site.longitude, site.latitude]
31
+ },
32
+ properties: site
33
+ };
34
+ }
@@ -0,0 +1,14 @@
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[];
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * Utilities for building and managing site filter tree
3
3
  */
4
- const STORAGE_PREFIX = 'cellular:siteFilter';
5
4
  /**
6
5
  * Builds a hierarchical tree from flat site array
7
6
  * Structure: All Sites -> Provider -> Feature Group
@@ -19,8 +18,6 @@ export function buildSiteTree(sites) {
19
18
  }
20
19
  featureGroups.get(site.featureGroup).push(site);
21
20
  });
22
- // Load saved state from localStorage
23
- const savedState = loadTreeState();
24
21
  // Build tree structure
25
22
  const children = [];
26
23
  // Sort providers alphabetically
@@ -36,7 +33,7 @@ export function buildSiteTree(sites) {
36
33
  providerChildren.push({
37
34
  id: nodeId,
38
35
  label: `${featureGroup} (${groupSites.length})`,
39
- defaultChecked: savedState[nodeId] ?? true, // Default checked
36
+ defaultChecked: true,
40
37
  children: [],
41
38
  metadata: {
42
39
  type: 'featureGroup',
@@ -50,7 +47,7 @@ export function buildSiteTree(sites) {
50
47
  children.push({
51
48
  id: providerId,
52
49
  label: `${provider} (${Array.from(featureGroups.values()).flat().length})`,
53
- defaultChecked: savedState[providerId] ?? true, // Default checked
50
+ defaultChecked: true,
54
51
  children: providerChildren,
55
52
  metadata: {
56
53
  type: 'provider',
@@ -62,7 +59,7 @@ export function buildSiteTree(sites) {
62
59
  return {
63
60
  id: 'all-sites',
64
61
  label: `All Sites (${sites.length})`,
65
- defaultChecked: savedState['all-sites'] ?? true, // Default checked
62
+ defaultChecked: true,
66
63
  children,
67
64
  metadata: {
68
65
  type: 'root'
@@ -118,47 +115,3 @@ export function getFilteredSites(checkedPaths, allSites) {
118
115
  console.log('Filtered:', filtered.length, 'of', allSites.length, 'sites');
119
116
  return filtered;
120
117
  }
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
- }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Map V2 - Feature-Based Cellular Visualization Library
3
+ *
4
+ * A decoupled, feature-based architecture for Mapbox cellular network visualization.
5
+ * Each feature (sites, cells) is completely independent with its own store, layers, and controls.
6
+ */
7
+ export { type MapStore, MAP_CONTEXT_KEY, MapboxProvider, MapStyleControl, createMapStore, useMapbox, tryUseMapbox } from './core';
8
+ export { MapControl, addSourceIfMissing, removeSourceIfExists, addLayerIfMissing, removeLayerIfExists, updateGeoJSONSource, removeLayerAndSource, isStyleLoaded, waitForStyleLoad, setFeatureState, removeFeatureState, generateLayerId, generateSourceId } from './shared';
9
+ export { type Site, type SiteStoreValue, createSiteStore, SitesLayer, SiteFilterControl, SiteSizeSlider, sitesToGeoJSON, siteToFeature, buildSiteTree, getFilteredSites } from './features/sites';
10
+ export { DemoMap, demoSites } from './demo';