@smartnet360/svelte-components 0.0.85 → 0.0.87

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 (115) hide show
  1. package/dist/apps/antenna-pattern/components/AntennaControls.svelte +1 -106
  2. package/dist/apps/antenna-pattern/components/AntennaDiagrams.svelte +0 -36
  3. package/dist/apps/antenna-pattern/components/AntennaSettingsModal.svelte +0 -2
  4. package/dist/apps/antenna-pattern/components/PlotlyRadarChart.svelte +0 -22
  5. package/dist/apps/antenna-pattern/components/chart-engines/PolarAreaChart.svelte +0 -2
  6. package/dist/apps/antenna-pattern/components/chart-engines/PolarBarChart.svelte +0 -2
  7. package/dist/apps/antenna-pattern/components/chart-engines/PolarLineChart.svelte +0 -2
  8. package/dist/apps/site-check/data-loader.js +0 -8
  9. package/dist/core/Charts/GlobalControls.svelte +0 -4
  10. package/dist/core/Desktop/Grid/ResizeHandle.svelte +0 -7
  11. package/dist/core/Desktop/Grid/resizeStore.js +0 -1
  12. package/dist/index.d.ts +1 -0
  13. package/dist/index.js +2 -0
  14. package/dist/map-v2/demo/DemoMap.svelte +0 -2
  15. package/dist/map-v2/demo/demo-cells.js +0 -1
  16. package/dist/map-v2/features/cells/layers/CellsLayer.svelte +7 -26
  17. package/dist/map-v2/features/cells/utils/cellTree.js +0 -29
  18. package/dist/map-v2/features/repeaters/layers/RepeaterLabelsLayer.svelte +3 -27
  19. package/dist/map-v2/features/repeaters/layers/RepeatersLayer.svelte +8 -25
  20. package/dist/map-v2/features/repeaters/utils/repeaterTree.js +0 -6
  21. package/dist/map-v2/features/sites/controls/SiteFilterControl.svelte +0 -8
  22. package/dist/map-v2/features/sites/utils/siteTreeUtils.js +0 -6
  23. package/dist/map-v3/core/components/Map.svelte +89 -0
  24. package/dist/map-v3/core/components/Map.svelte.d.ts +13 -0
  25. package/dist/map-v3/core/controls/FeatureSettingsControl.svelte +103 -0
  26. package/dist/map-v3/core/controls/FeatureSettingsControl.svelte.d.ts +15 -0
  27. package/dist/map-v3/core/controls/MapStyleControl.svelte +271 -0
  28. package/dist/map-v3/core/controls/MapStyleControl.svelte.d.ts +28 -0
  29. package/dist/map-v3/core/index.d.ts +6 -0
  30. package/dist/map-v3/core/index.js +5 -0
  31. package/dist/map-v3/core/stores/map.store.svelte.d.ts +8 -0
  32. package/dist/map-v3/core/stores/map.store.svelte.js +29 -0
  33. package/dist/map-v3/core/stores/viewport.store.svelte.d.ts +38 -0
  34. package/dist/map-v3/core/stores/viewport.store.svelte.js +107 -0
  35. package/dist/map-v3/demo/DemoMap.svelte +104 -0
  36. package/dist/map-v3/demo/DemoMap.svelte.d.ts +6 -0
  37. package/dist/map-v3/demo/demo-cells.d.ts +13 -0
  38. package/dist/map-v3/demo/demo-cells.js +130 -0
  39. package/dist/map-v3/demo/demo-data.d.ts +8 -0
  40. package/dist/map-v3/demo/demo-data.js +104 -0
  41. package/dist/map-v3/demo/demo-repeaters.d.ts +13 -0
  42. package/dist/map-v3/demo/demo-repeaters.js +73 -0
  43. package/dist/map-v3/features/cells/components/CellFilterControl.svelte +208 -0
  44. package/dist/map-v3/features/cells/components/CellFilterControl.svelte.d.ts +12 -0
  45. package/dist/map-v3/features/cells/components/CellSettingsPanel.svelte +229 -0
  46. package/dist/map-v3/features/cells/components/CellSettingsPanel.svelte.d.ts +7 -0
  47. package/dist/map-v3/features/cells/constants.d.ts +18 -0
  48. package/dist/map-v3/features/cells/constants.js +37 -0
  49. package/dist/map-v3/features/cells/layers/CellLabelsLayer.svelte +230 -0
  50. package/dist/map-v3/features/cells/layers/CellLabelsLayer.svelte.d.ts +11 -0
  51. package/dist/map-v3/features/cells/layers/CellsLayer.svelte +194 -0
  52. package/dist/map-v3/features/cells/layers/CellsLayer.svelte.d.ts +11 -0
  53. package/dist/map-v3/features/cells/layers/index.d.ts +2 -0
  54. package/dist/map-v3/features/cells/layers/index.js +2 -0
  55. package/dist/map-v3/features/cells/logic/geometry.d.ts +12 -0
  56. package/dist/map-v3/features/cells/logic/geometry.js +35 -0
  57. package/dist/map-v3/features/cells/logic/grouping.d.ts +18 -0
  58. package/dist/map-v3/features/cells/logic/grouping.js +30 -0
  59. package/dist/map-v3/features/cells/logic/tree-adapter.d.ts +11 -0
  60. package/dist/map-v3/features/cells/logic/tree-adapter.js +53 -0
  61. package/dist/map-v3/features/cells/stores/cell.data.svelte.d.ts +9 -0
  62. package/dist/map-v3/features/cells/stores/cell.data.svelte.js +16 -0
  63. package/dist/map-v3/features/cells/stores/cell.display.svelte.d.ts +25 -0
  64. package/dist/map-v3/features/cells/stores/cell.display.svelte.js +67 -0
  65. package/dist/map-v3/features/cells/stores/cell.registry.svelte.d.ts +23 -0
  66. package/dist/map-v3/features/cells/stores/cell.registry.svelte.js +68 -0
  67. package/dist/map-v3/features/cells/types.d.ts +62 -0
  68. package/dist/map-v3/features/cells/types.js +6 -0
  69. package/dist/map-v3/features/repeaters/components/RepeaterFilterControl.svelte +148 -0
  70. package/dist/map-v3/features/repeaters/components/RepeaterFilterControl.svelte.d.ts +12 -0
  71. package/dist/map-v3/features/repeaters/components/RepeaterSettingsPanel.svelte +209 -0
  72. package/dist/map-v3/features/repeaters/components/RepeaterSettingsPanel.svelte.d.ts +7 -0
  73. package/dist/map-v3/features/repeaters/layers/RepeaterLabelsLayer.svelte +177 -0
  74. package/dist/map-v3/features/repeaters/layers/RepeaterLabelsLayer.svelte.d.ts +11 -0
  75. package/dist/map-v3/features/repeaters/layers/RepeatersLayer.svelte +163 -0
  76. package/dist/map-v3/features/repeaters/layers/RepeatersLayer.svelte.d.ts +11 -0
  77. package/dist/map-v3/features/repeaters/logic/geometry.d.ts +3 -0
  78. package/dist/map-v3/features/repeaters/logic/geometry.js +23 -0
  79. package/dist/map-v3/features/repeaters/logic/grouping.d.ts +8 -0
  80. package/dist/map-v3/features/repeaters/logic/grouping.js +20 -0
  81. package/dist/map-v3/features/repeaters/logic/tree-adapter.d.ts +8 -0
  82. package/dist/map-v3/features/repeaters/logic/tree-adapter.js +43 -0
  83. package/dist/map-v3/features/repeaters/stores/repeater.data.svelte.d.ts +8 -0
  84. package/dist/map-v3/features/repeaters/stores/repeater.data.svelte.js +13 -0
  85. package/dist/map-v3/features/repeaters/stores/repeater.display.svelte.d.ts +21 -0
  86. package/dist/map-v3/features/repeaters/stores/repeater.display.svelte.js +64 -0
  87. package/dist/map-v3/features/repeaters/stores/repeater.registry.svelte.d.ts +23 -0
  88. package/dist/map-v3/features/repeaters/stores/repeater.registry.svelte.js +68 -0
  89. package/dist/map-v3/features/repeaters/types.d.ts +18 -0
  90. package/dist/map-v3/features/repeaters/types.js +1 -0
  91. package/dist/map-v3/features/sites/components/SiteFilterControl.svelte +119 -0
  92. package/dist/map-v3/features/sites/components/SiteFilterControl.svelte.d.ts +12 -0
  93. package/dist/map-v3/features/sites/components/SiteSettingsPanel.svelte +241 -0
  94. package/dist/map-v3/features/sites/components/SiteSettingsPanel.svelte.d.ts +7 -0
  95. package/dist/map-v3/features/sites/layers/SiteLabelsLayer.svelte +152 -0
  96. package/dist/map-v3/features/sites/layers/SiteLabelsLayer.svelte.d.ts +11 -0
  97. package/dist/map-v3/features/sites/layers/SitesLayer.svelte +132 -0
  98. package/dist/map-v3/features/sites/layers/SitesLayer.svelte.d.ts +11 -0
  99. package/dist/map-v3/features/sites/logic/tree-adapter.d.ts +9 -0
  100. package/dist/map-v3/features/sites/logic/tree-adapter.js +75 -0
  101. package/dist/map-v3/features/sites/stores/site.data.svelte.d.ts +8 -0
  102. package/dist/map-v3/features/sites/stores/site.data.svelte.js +40 -0
  103. package/dist/map-v3/features/sites/stores/site.display.svelte.d.ts +20 -0
  104. package/dist/map-v3/features/sites/stores/site.display.svelte.js +63 -0
  105. package/dist/map-v3/features/sites/stores/site.registry.svelte.d.ts +13 -0
  106. package/dist/map-v3/features/sites/stores/site.registry.svelte.js +83 -0
  107. package/dist/map-v3/features/sites/types.d.ts +12 -0
  108. package/dist/map-v3/features/sites/types.js +1 -0
  109. package/dist/map-v3/index.d.ts +30 -0
  110. package/dist/map-v3/index.js +36 -0
  111. package/dist/map-v3/shared/controls/MapControl.svelte +242 -0
  112. package/dist/map-v3/shared/controls/MapControl.svelte.d.ts +27 -0
  113. package/dist/map-v3/shared/index.d.ts +1 -0
  114. package/dist/map-v3/shared/index.js +1 -0
  115. package/package.json +1 -1
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Demo Cell Data
3
+ *
4
+ * 100 sites across San Francisco Bay Area
5
+ * Each site has 3 sectors (azimuths: 0°, 120°, 240°)
6
+ * Each sector has 12 cells (all tech-band combinations)
7
+ * Total: 100 sites × 3 sectors × 12 tech-bands = 3,600 cells
8
+ */
9
+ const BASE_LAT = 47.4979;
10
+ const BASE_LNG = 19.0402;
11
+ // Grid parameters for distributing sites
12
+ const NUM_SITES = 1700;
13
+ const GRID_SIZE = 10; // 10×10 grid
14
+ const LAT_SPACING = 0.01; // ~1.1 km spacing
15
+ const LNG_SPACING = 0.015; // ~1.1 km spacing (adjusted for longitude)
16
+ // Standard beamwidth for sectors
17
+ const BEAMWIDTH = 65;
18
+ // Cell tech-band definitions with proper fband format
19
+ const TECH_BANDS = [
20
+ // 2G bands
21
+ { tech: '2G', band: '900', fband: 'GSM900' },
22
+ { tech: '2G', band: '1800', fband: 'GSM1800' },
23
+ // 4G bands
24
+ { tech: '4G', band: '700', fband: 'LTE700' },
25
+ { tech: '4G', band: '800', fband: 'LTE800' },
26
+ { tech: '4G', band: '900', fband: 'LTE900' },
27
+ { tech: '4G', band: '1800', fband: 'LTE1800' },
28
+ { tech: '4G', band: '2100', fband: 'LTE2100' },
29
+ { tech: '4G', band: '2600', fband: 'LTE2600' },
30
+ // 5G bands
31
+ { tech: '5G', band: '700', fband: '5G-700' },
32
+ { tech: '5G', band: '2100', fband: '5G-2100' },
33
+ { tech: '5G', band: '3500', fband: '5G-3500' }
34
+ ];
35
+ // Three sector azimuths
36
+ const AZIMUTHS = [0, 120, 240];
37
+ // Status rotation for variety
38
+ const STATUSES = [
39
+ 'On_Air',
40
+ 'On_Air',
41
+ 'On_Air',
42
+ 'On_Air',
43
+ 'On_Air_UNDER_CONSTRUCTION',
44
+ 'On_Air_Locked',
45
+ 'RF_Plan_Ready',
46
+ 'RF_Plan_Ready',
47
+ 'Re-Planned_RF_Plan_Ready',
48
+ 'Tavlati_RF_Plan_Ready',
49
+ 'On_Air',
50
+ 'On_Air'
51
+ ];
52
+ /**
53
+ * Generate demo cells: 100 sites × 3 sectors × 12 tech-bands = 3,600 cells
54
+ */
55
+ export const demoCells = [];
56
+ let cellCounter = 1;
57
+ // Generate sites in a grid pattern
58
+ for (let siteIndex = 0; siteIndex < NUM_SITES; siteIndex++) {
59
+ const row = Math.floor(siteIndex / GRID_SIZE);
60
+ const col = siteIndex % GRID_SIZE;
61
+ // Calculate site position (centered grid around base location)
62
+ const siteLat = BASE_LAT + (row - GRID_SIZE / 2) * LAT_SPACING;
63
+ const siteLng = BASE_LNG + (col - GRID_SIZE / 2) * LNG_SPACING;
64
+ const siteId = `DEMO-SITE-${String(siteIndex + 1).padStart(3, '0')}`;
65
+ // Generate 3 sectors per site
66
+ AZIMUTHS.forEach((azimuth, sectorIndex) => {
67
+ // Generate 12 tech-bands per sector
68
+ TECH_BANDS.forEach((techBand, techIndex) => {
69
+ const cellId = `CELL-${String(cellCounter).padStart(4, '0')}`;
70
+ const status = STATUSES[techIndex];
71
+ demoCells.push({
72
+ // Core properties
73
+ id: cellId,
74
+ txId: `TX-${String(cellCounter).padStart(4, '0')}`,
75
+ cellID: cellId,
76
+ cellID2G: techBand.tech === '2G' ? cellId : '',
77
+ cellName: `${siteId} ${techBand.tech}${techBand.band} S${sectorIndex + 1}`,
78
+ siteId: siteId,
79
+ tech: techBand.tech,
80
+ fband: techBand.fband,
81
+ frq: techBand.band,
82
+ type: 'MACRO',
83
+ status: status,
84
+ onAirDate: '2024-01-15',
85
+ // 2G specific
86
+ bcch: techBand.tech === '2G' ? 100 + techIndex : 0,
87
+ ctrlid: techBand.tech === '2G' ? `CTRL-${cellId}` : '',
88
+ // 4G specific
89
+ dlEarfn: techBand.tech === '4G' ? 6200 + techIndex * 100 : 0,
90
+ // Physical properties
91
+ antenna: 'DEMO-ANTENNA-MODEL',
92
+ azimuth: azimuth,
93
+ height: 30, // 30 meters antenna height
94
+ electricalTilt: '3',
95
+ beamwidth: BEAMWIDTH,
96
+ latitude: siteLat,
97
+ longitude: siteLng,
98
+ dx: 0,
99
+ dy: 0,
100
+ siteLatitude: siteLat,
101
+ siteLongitude: siteLng,
102
+ // Planning
103
+ comment: `Demo ${techBand.tech} ${techBand.band} cell at azimuth ${azimuth}°`,
104
+ planner: 'Demo User',
105
+ // Atoll properties
106
+ atollETP: 43.0,
107
+ atollPW: 20.0,
108
+ atollRS: 500.0 + (techBand.band === '700' ? 200 : 0), // Lower freq = longer range
109
+ atollBW: parseFloat(techBand.band) / 100, // Simplified bandwidth
110
+ // Network properties
111
+ cellId3: `${cellId}-3G`,
112
+ nwtP1: 20,
113
+ nwtP2: 40,
114
+ pci1: (cellCounter % 504), // Physical Cell ID for LTE
115
+ nwtRS: 450.0,
116
+ nwtBW: 10.0,
117
+ // Other
118
+ other: {
119
+ demoCell: true,
120
+ siteNumber: siteIndex + 1,
121
+ sector: sectorIndex + 1,
122
+ techBandKey: `${techBand.tech}_${techBand.band}`,
123
+ gridPosition: { row, col }
124
+ },
125
+ customSubgroup: `Sector-${sectorIndex + 1}`
126
+ });
127
+ cellCounter++;
128
+ });
129
+ });
130
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Demo cellular data for testing map-v2 site feature
3
+ */
4
+ import type { Site } from '../features/sites/types';
5
+ /**
6
+ * Sample cellular sites (San Francisco area)
7
+ */
8
+ export declare const demoSites: Site[];
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Demo cellular data for testing map-v2 site feature
3
+ */
4
+ /**
5
+ * Sample cellular sites (San Francisco area)
6
+ */
7
+ export const demoSites = [
8
+ {
9
+ siteId: 'site-001',
10
+ siteName: 'Downtown Tower',
11
+ longitude: -122.4194,
12
+ latitude: 37.7749,
13
+ fbands: ['2100', '1800'],
14
+ techs: ['5G'],
15
+ provider: 'Verizon',
16
+ level1: 'Urban Macro',
17
+ level2: 'San Francisco',
18
+ cellCount: 3
19
+ },
20
+ {
21
+ siteId: 'site-002',
22
+ siteName: 'Financial District',
23
+ longitude: -122.4008,
24
+ latitude: 37.7946,
25
+ fbands: ['1800', '850'],
26
+ techs: ['LTE'],
27
+ provider: 'AT&T',
28
+ level1: 'Urban Macro',
29
+ level2: 'San Francisco',
30
+ cellCount: 3
31
+ },
32
+ {
33
+ siteId: 'site-003',
34
+ siteName: 'Mission Bay',
35
+ longitude: -122.3912,
36
+ latitude: 37.7699,
37
+ fbands: ['2600'],
38
+ techs: ['5G'],
39
+ provider: 'T-Mobile',
40
+ level1: 'Small Cell',
41
+ level2: 'San Francisco',
42
+ cellCount: 2
43
+ },
44
+ {
45
+ siteId: 'site-004',
46
+ siteName: 'Golden Gate Park',
47
+ longitude: -122.4862,
48
+ latitude: 37.7694,
49
+ fbands: ['850', '700'],
50
+ techs: ['LTE'],
51
+ provider: 'Verizon',
52
+ level1: 'Rural Macro',
53
+ level2: 'San Francisco',
54
+ cellCount: 3
55
+ },
56
+ {
57
+ siteId: 'site-005',
58
+ siteName: 'Presidio Heights',
59
+ longitude: -122.4545,
60
+ latitude: 37.7881,
61
+ fbands: ['2100', '850'],
62
+ techs: ['5G'],
63
+ provider: 'AT&T',
64
+ level1: 'Suburban Macro',
65
+ level2: 'San Francisco',
66
+ cellCount: 3
67
+ },
68
+ {
69
+ siteId: 'site-006',
70
+ siteName: 'Castro District',
71
+ longitude: -122.4352,
72
+ latitude: 37.7609,
73
+ fbands: ['1800'],
74
+ techs: ['LTE'],
75
+ provider: 'T-Mobile',
76
+ level1: 'Small Cell',
77
+ level2: 'San Francisco',
78
+ cellCount: 2
79
+ },
80
+ {
81
+ siteId: 'site-007',
82
+ siteName: 'Embarcadero',
83
+ longitude: -122.3959,
84
+ latitude: 37.7956,
85
+ fbands: ['2600', '2100'],
86
+ techs: ['5G'],
87
+ provider: 'Verizon',
88
+ level1: 'Urban Macro',
89
+ level2: 'San Francisco',
90
+ cellCount: 3
91
+ },
92
+ {
93
+ siteId: 'site-008',
94
+ siteName: 'Sunset District',
95
+ longitude: -122.4944,
96
+ latitude: 37.7599,
97
+ fbands: ['850', '700'],
98
+ techs: ['LTE'],
99
+ provider: 'AT&T',
100
+ level1: 'Suburban Macro',
101
+ level2: 'San Francisco',
102
+ cellCount: 3
103
+ }
104
+ ];
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Demo Repeater Data
3
+ *
4
+ * 15 repeaters strategically placed near demo sites
5
+ * Mix of 2G, 4G, and 5G technologies across various frequency bands
6
+ * Fixed 30° beamwidth
7
+ */
8
+ import type { Repeater } from '../features/repeaters/types';
9
+ /**
10
+ * Generate demo repeater data
11
+ * Creates 15 repeaters with various tech:fband combinations
12
+ */
13
+ export declare const demoRepeaters: Repeater[];
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Demo Repeater Data
3
+ *
4
+ * 15 repeaters strategically placed near demo sites
5
+ * Mix of 2G, 4G, and 5G technologies across various frequency bands
6
+ * Fixed 30° beamwidth
7
+ */
8
+ const BASE_LAT = 47.4979;
9
+ const BASE_LNG = 19.0402;
10
+ // Repeater positions (distributed across demo area)
11
+ const REPEATER_POSITIONS = [
12
+ { lat: BASE_LAT + 0.015, lng: BASE_LNG - 0.020 },
13
+ { lat: BASE_LAT + 0.025, lng: BASE_LNG + 0.010 },
14
+ { lat: BASE_LAT - 0.010, lng: BASE_LNG + 0.025 },
15
+ { lat: BASE_LAT - 0.020, lng: BASE_LNG - 0.015 },
16
+ { lat: BASE_LAT + 0.005, lng: BASE_LNG + 0.030 },
17
+ { lat: BASE_LAT + 0.030, lng: BASE_LNG - 0.005 },
18
+ { lat: BASE_LAT - 0.025, lng: BASE_LNG + 0.015 },
19
+ { lat: BASE_LAT + 0.010, lng: BASE_LNG - 0.030 },
20
+ { lat: BASE_LAT - 0.015, lng: BASE_LNG - 0.025 },
21
+ { lat: BASE_LAT + 0.020, lng: BASE_LNG + 0.020 },
22
+ { lat: BASE_LAT - 0.005, lng: BASE_LNG - 0.010 },
23
+ { lat: BASE_LAT + 0.035, lng: BASE_LNG + 0.005 },
24
+ { lat: BASE_LAT - 0.030, lng: BASE_LNG + 0.020 },
25
+ { lat: BASE_LAT + 0.012, lng: BASE_LNG - 0.018 },
26
+ { lat: BASE_LAT - 0.018, lng: BASE_LNG + 0.012 }
27
+ ];
28
+ // Repeater tech-band combinations
29
+ const REPEATER_CONFIGS = [
30
+ // 2G repeaters
31
+ { tech: '2G', fband: 'GSM900', azimuth: 45 },
32
+ { tech: '2G', fband: 'GSM1800', azimuth: 180 },
33
+ { tech: '2G', fband: 'GSM900', azimuth: 270 },
34
+ // 4G repeaters
35
+ { tech: '4G', fband: 'LTE700', azimuth: 90 },
36
+ { tech: '4G', fband: 'LTE800', azimuth: 225 },
37
+ { tech: '4G', fband: 'LTE1800', azimuth: 135 },
38
+ { tech: '4G', fband: 'LTE2100', azimuth: 315 },
39
+ { tech: '4G', fband: 'LTE2600', azimuth: 60 },
40
+ // 5G repeaters
41
+ { tech: '5G', fband: '5G-700', azimuth: 150 },
42
+ { tech: '5G', fband: '5G-2100', azimuth: 210 },
43
+ { tech: '5G', fband: '5G-3500', azimuth: 330 },
44
+ { tech: '5G', fband: '5G-3500', azimuth: 0 },
45
+ { tech: '5G', fband: '5G-2100', azimuth: 120 },
46
+ { tech: '5G', fband: '5G-700', azimuth: 240 },
47
+ { tech: '5G', fband: '5G-3500', azimuth: 180 }
48
+ ];
49
+ /**
50
+ * Generate demo repeater data
51
+ * Creates 15 repeaters with various tech:fband combinations
52
+ */
53
+ export const demoRepeaters = REPEATER_POSITIONS.map((pos, idx) => {
54
+ const config = REPEATER_CONFIGS[idx];
55
+ const repeaterId = `REP-${String(idx + 1).padStart(3, '0')}`;
56
+ const donorCellId = `CELL-${String((idx * 7) % 100).padStart(3, '0')}`;
57
+ return {
58
+ repeaterId,
59
+ donorCellId,
60
+ donorCellName: `Cell-${config.tech}-${config.fband}-${idx + 1}`,
61
+ latitude: pos.lat,
62
+ longitude: pos.lng,
63
+ azimuth: config.azimuth,
64
+ beamwidth: 30, // Fixed 30° beamwidth
65
+ tech: config.tech,
66
+ fband: config.fband,
67
+ type: 'REPEATER',
68
+ height: 15 + (idx % 5) * 5, // Heights between 15-35m
69
+ factory_nbr: `FN-${String(1000 + idx).padStart(6, '0')}`,
70
+ provider: idx % 2 === 0 ? 'Operator-A' : 'Operator-B',
71
+ featureGroup: config.tech === '5G' ? 'High-Priority' : config.tech === '4G' ? 'Standard' : 'Legacy'
72
+ };
73
+ });
@@ -0,0 +1,208 @@
1
+ <script lang="ts">
2
+ import { onDestroy, untrack } from 'svelte';
3
+ import { MapControl } from '../../../shared';
4
+ import { createTreeStore } from '../../../../core/TreeView/tree.store';
5
+ import TreeView from '../../../../core/TreeView/TreeView.svelte';
6
+ import type { CellDataStore } from '../stores/cell.data.svelte';
7
+ import type { CellRegistry } from '../stores/cell.registry.svelte';
8
+ import type { CellDisplayStore } from '../stores/cell.display.svelte';
9
+ import type { CellGroupingField } from '../types';
10
+ import { buildCellTree } from '../logic/tree-adapter';
11
+
12
+ interface Props {
13
+ dataStore: CellDataStore;
14
+ registry: CellRegistry;
15
+ displayStore: CellDisplayStore;
16
+ position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
17
+ }
18
+
19
+ let { dataStore, registry, displayStore, position = 'top-left' }: Props = $props();
20
+
21
+ // Local State for Grouping
22
+ // let level1 = $state<CellGroupingField>('tech');
23
+ // let level2 = $state<CellGroupingField>('fband');
24
+ let includePlanned = $state(false);
25
+
26
+ const level1Options: CellGroupingField[] = ['tech', 'status', 'siteId'];
27
+ const level2Options: CellGroupingField[] = ['fband', 'frq', 'status', 'none'];
28
+
29
+ // Sync includePlanned with dataStore
30
+ $effect(() => {
31
+ // If includePlanned is true, we show ALL (filterOnAir = false)
32
+ // If includePlanned is false, we show ONLY On Air (filterOnAir = true)
33
+ dataStore.filterOnAir = !includePlanned;
34
+ });
35
+
36
+ // Create Tree Store
37
+ // We recreate it whenever the cell data changes significantly (e.g. new file loaded)
38
+ // or when grouping configuration changes.
39
+ // We use untrack() to avoid recreating the tree when the registry changes (visibility toggles),
40
+ // as the tree itself drives those changes.
41
+ let treeStore = $derived.by(() => {
42
+ const _cells = dataStore.filteredCells;
43
+ const _l1 = displayStore.level1;
44
+ const _l2 = displayStore.level2;
45
+
46
+ return untrack(() => {
47
+ const nodes = buildCellTree(_cells, registry, _l1, _l2);
48
+ return createTreeStore({
49
+ nodes,
50
+ namespace: `${registry.namespace}:tree:${_l1}:${_l2}`,
51
+ persistState: true,
52
+ defaultExpandAll: true
53
+ });
54
+ });
55
+ });
56
+
57
+ // Sync Tree Selection -> Cell Registry Visibility
58
+ $effect(() => {
59
+ const unsubscribe = treeStore.subscribe((val) => {
60
+ // Iterate all leaf nodes to sync visibility
61
+ // This is a bit heavy but ensures consistency.
62
+ // Optimization: Only update changed nodes if we had a diff.
63
+
64
+ // We only care about leaf nodes (bands)
65
+ let changes = 0;
66
+ val.state.nodes.forEach((nodeState) => {
67
+ if (nodeState.node.children && nodeState.node.children.length > 0) return; // Skip folders
68
+
69
+ const groupId = nodeState.node.id;
70
+ // IMPORTANT: Read from checkedPaths set, NOT nodeState.checked (which is static/stale)
71
+ const isVisible = val.state.checkedPaths.has(nodeState.path);
72
+
73
+ // Update registry if different
74
+ const currentStyle = registry.getStyle(groupId, '#000'); // Color doesn't matter here
75
+ if (currentStyle.visible !== isVisible) {
76
+ // console.log(`[CellFilterControl] Syncing ${groupId}: ${currentStyle.visible} -> ${isVisible}`);
77
+ registry.toggleVisibility(groupId);
78
+ changes++;
79
+ }
80
+ });
81
+ if (changes > 0) {
82
+ console.log(`[CellFilterControl] Synced ${changes} visibility changes to registry`);
83
+ }
84
+ });
85
+
86
+ return () => {
87
+ unsubscribe();
88
+ };
89
+ });
90
+
91
+ function handleColorChange(groupId: string, event: Event) {
92
+ const input = event.target as HTMLInputElement;
93
+ registry.setColor(groupId, input.value);
94
+ }
95
+
96
+ function getFieldLabel(field: string): string {
97
+ const labels: Record<string, string> = {
98
+ tech: 'Technology',
99
+ fband: 'Tech + Band',
100
+ frq: 'Frequency',
101
+ status: 'Status',
102
+ siteId: 'Site ID',
103
+ none: 'None'
104
+ };
105
+ return labels[field] || field;
106
+ }
107
+ </script>
108
+
109
+ <MapControl {position} title="Layers" icon="radioactive" controlWidth="320px">
110
+ <div class="cell-filter-control">
111
+ <!-- Status Filter Checkbox -->
112
+ <div class="mb-3 px-1">
113
+ <div class="form-check">
114
+ <input
115
+ type="checkbox"
116
+ class="form-check-input"
117
+ id="includePlanned"
118
+ bind:checked={includePlanned}
119
+ />
120
+ <label class="form-check-label small" for="includePlanned">
121
+ Include Planned Cells
122
+ <span class="text-muted ms-1">
123
+ ({includePlanned ? 'All cells' : 'On Air only'})
124
+ </span>
125
+ </label>
126
+ </div>
127
+ </div>
128
+
129
+ <!-- Grouping Configuration -->
130
+ <div class="grouping-config mb-3 pb-2 border-bottom">
131
+ <div class="row g-2">
132
+ <!-- Level 1 Grouping -->
133
+ <div class="col-6">
134
+ <label for="level1-select" class="form-label small mb-1 text-muted">Level 1</label>
135
+ <select
136
+ id="level1-select"
137
+ class="form-select form-select-sm"
138
+ bind:value={displayStore.level1}
139
+ >
140
+ {#each level1Options as option}
141
+ <option value={option}>{getFieldLabel(option)}</option>
142
+ {/each}
143
+ </select>
144
+ </div>
145
+
146
+ <!-- Level 2 Grouping -->
147
+ <div class="col-6">
148
+ <label for="level2-select" class="form-label small mb-1 text-muted">Level 2</label>
149
+ <select
150
+ id="level2-select"
151
+ class="form-select form-select-sm"
152
+ bind:value={displayStore.level2}
153
+ >
154
+ {#each level2Options as option}
155
+ <option value={option}>{getFieldLabel(option)}</option>
156
+ {/each}
157
+ </select>
158
+ </div>
159
+ </div>
160
+ </div>
161
+
162
+ <!-- Tree View -->
163
+ <div class="cell-filter-tree">
164
+ {#if dataStore.filteredCells.length === 0}
165
+ <div class="text-muted p-3 text-center small">
166
+ No cells loaded.
167
+ </div>
168
+ {:else}
169
+ <TreeView showControls={false} store={$treeStore} height="300px">
170
+ {#snippet children({ node, state })}
171
+ <!-- Color Picker (Only for leaves) -->
172
+ {#if !node.children || node.children.length === 0}
173
+ <div
174
+ class="d-flex align-items-center"
175
+ role="group"
176
+ onclick={(e) => e.stopPropagation()}
177
+ onkeydown={(e) => e.stopPropagation()}
178
+ >
179
+ <input
180
+ type="color"
181
+ class="form-control form-control-color form-control-sm border-0 p-0"
182
+ style="width: 16px; height: 16px; min-height: 0;"
183
+ value={node.metadata?.color}
184
+ oninput={(e) => handleColorChange(node.id, e)}
185
+ title="Change color"
186
+ />
187
+ </div>
188
+ {/if}
189
+ {/snippet}
190
+ </TreeView>
191
+ {/if}
192
+ </div>
193
+
194
+ <!-- Stats Footer -->
195
+ <!-- <div class="cell-filter-stats text-center pt-2 mt-2 border-top">
196
+ <small class="text-muted">
197
+ Showing {dataStore.filteredCells.length} of {dataStore.rawCells.length} cells
198
+ </small>
199
+ </div> -->
200
+ </div>
201
+ </MapControl>
202
+
203
+ <style>
204
+ .cell-filter-control {
205
+ background: #fff;
206
+ overflow: hidden;
207
+ }
208
+ </style>
@@ -0,0 +1,12 @@
1
+ import type { CellDataStore } from '../stores/cell.data.svelte';
2
+ import type { CellRegistry } from '../stores/cell.registry.svelte';
3
+ import type { CellDisplayStore } from '../stores/cell.display.svelte';
4
+ interface Props {
5
+ dataStore: CellDataStore;
6
+ registry: CellRegistry;
7
+ displayStore: CellDisplayStore;
8
+ position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
9
+ }
10
+ declare const CellFilterControl: import("svelte").Component<Props, {}, "">;
11
+ type CellFilterControl = ReturnType<typeof CellFilterControl>;
12
+ export default CellFilterControl;