@smartnet360/svelte-components 0.0.22 → 0.0.23

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 (36) hide show
  1. package/dist/apps/antenna-pattern/utils/msi-parser.js +18 -1
  2. package/dist/cellular/CellularChartsView.svelte +293 -0
  3. package/dist/cellular/CellularChartsView.svelte.d.ts +7 -0
  4. package/dist/cellular/HierarchicalTree.svelte +469 -0
  5. package/dist/cellular/HierarchicalTree.svelte.d.ts +9 -0
  6. package/dist/cellular/SiteTree.svelte +286 -0
  7. package/dist/cellular/SiteTree.svelte.d.ts +11 -0
  8. package/dist/cellular/cellular-transforms.d.ts +25 -0
  9. package/dist/cellular/cellular-transforms.js +129 -0
  10. package/dist/cellular/cellular.model.d.ts +63 -0
  11. package/dist/cellular/cellular.model.js +6 -0
  12. package/dist/cellular/index.d.ts +11 -0
  13. package/dist/cellular/index.js +11 -0
  14. package/dist/cellular/mock-cellular-data.d.ts +13 -0
  15. package/dist/cellular/mock-cellular-data.js +241 -0
  16. package/dist/core/TreeChartView/TreeChartView.svelte +208 -0
  17. package/dist/core/TreeChartView/TreeChartView.svelte.d.ts +42 -0
  18. package/dist/core/TreeChartView/index.d.ts +7 -0
  19. package/dist/core/TreeChartView/index.js +7 -0
  20. package/dist/core/TreeView/TreeNode.svelte +173 -0
  21. package/dist/core/TreeView/TreeNode.svelte.d.ts +10 -0
  22. package/dist/core/TreeView/TreeView.svelte +163 -0
  23. package/dist/core/TreeView/TreeView.svelte.d.ts +10 -0
  24. package/dist/core/TreeView/index.d.ts +48 -0
  25. package/dist/core/TreeView/index.js +50 -0
  26. package/dist/core/TreeView/tree-utils.d.ts +56 -0
  27. package/dist/core/TreeView/tree-utils.js +194 -0
  28. package/dist/core/TreeView/tree.model.d.ts +104 -0
  29. package/dist/core/TreeView/tree.model.js +5 -0
  30. package/dist/core/TreeView/tree.store.d.ts +10 -0
  31. package/dist/core/TreeView/tree.store.js +225 -0
  32. package/dist/core/index.d.ts +2 -0
  33. package/dist/core/index.js +4 -0
  34. package/dist/index.d.ts +1 -0
  35. package/dist/index.js +3 -1
  36. package/package.json +1 -1
@@ -0,0 +1,286 @@
1
+ <svelte:options runes={true} />
2
+
3
+ <script lang="ts">
4
+ import type { CellularSite, VisibilityState } from './cellular.model.js';
5
+ import { getBandColor, getBandLabel } from './mock-cellular-data.js';
6
+
7
+ interface Props {
8
+ sites: CellularSite[];
9
+ visibility: VisibilityState;
10
+ onToggleSite: (siteId: string) => void;
11
+ onToggleSector: (siteId: string, sectorId: string) => void;
12
+ onToggleCell: (siteId: string, sectorId: string, band: number) => void;
13
+ }
14
+
15
+ let { sites, visibility, onToggleSite, onToggleSector, onToggleCell }: Props = $props();
16
+
17
+ // Track expanded/collapsed state for UI only
18
+ let expandedSites = $state<Set<string>>(new Set(sites.map(s => s.siteId)));
19
+ let expandedSectors = $state<Set<string>>(new Set());
20
+
21
+ function toggleSiteExpand(siteId: string) {
22
+ if (expandedSites.has(siteId)) {
23
+ expandedSites.delete(siteId);
24
+ } else {
25
+ expandedSites.add(siteId);
26
+ }
27
+ expandedSites = new Set(expandedSites); // Trigger reactivity
28
+ }
29
+
30
+ function toggleSectorExpand(siteId: string, sectorId: string) {
31
+ const key = `${siteId}:${sectorId}`;
32
+ if (expandedSectors.has(key)) {
33
+ expandedSectors.delete(key);
34
+ } else {
35
+ expandedSectors.add(key);
36
+ }
37
+ expandedSectors = new Set(expandedSectors); // Trigger reactivity
38
+ }
39
+
40
+ function getSectorKey(siteId: string, sectorId: string): string {
41
+ return `${siteId}:${sectorId}`;
42
+ }
43
+
44
+ function getCellKey(siteId: string, sectorId: string, band: number): string {
45
+ return `${siteId}:${sectorId}:${band}`;
46
+ }
47
+ </script>
48
+
49
+ <div class="site-tree">
50
+ <div class="tree-header">
51
+ <h6 class="mb-0">Site Selection</h6>
52
+ </div>
53
+
54
+ <div class="tree-content">
55
+ {#each sites as site}
56
+ {@const isSiteVisible = visibility.sites[site.siteId] ?? true}
57
+ {@const isSiteExpanded = expandedSites.has(site.siteId)}
58
+
59
+ <div class="tree-site">
60
+ <!-- Site Level -->
61
+ <div class="tree-item site-item">
62
+ <button
63
+ class="expand-btn"
64
+ onclick={() => toggleSiteExpand(site.siteId)}
65
+ aria-label={isSiteExpanded ? 'Collapse' : 'Expand'}
66
+ >
67
+ <svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
68
+ {#if isSiteExpanded}
69
+ <path d="M2 4l4 4 4-4z" />
70
+ {:else}
71
+ <path d="M4 2l4 4-4 4z" />
72
+ {/if}
73
+ </svg>
74
+ </button>
75
+
76
+ <input
77
+ type="checkbox"
78
+ class="form-check-input"
79
+ id="site-{site.siteId}"
80
+ checked={isSiteVisible}
81
+ onchange={() => onToggleSite(site.siteId)}
82
+ />
83
+
84
+ <label class="tree-label" for="site-{site.siteId}">
85
+ <strong>{site.siteName}</strong>
86
+ </label>
87
+ </div>
88
+
89
+ <!-- Sectors -->
90
+ {#if isSiteExpanded}
91
+ <div class="tree-children">
92
+ {#each site.sectors as sector}
93
+ {@const sectorKey = getSectorKey(site.siteId, sector.sectorId)}
94
+ {@const isSectorVisible = visibility.sectors[sectorKey] ?? true}
95
+ {@const isSectorExpanded = expandedSectors.has(sectorKey)}
96
+
97
+ <div class="tree-sector">
98
+ <!-- Sector Level -->
99
+ <div class="tree-item sector-item">
100
+ <button
101
+ class="expand-btn"
102
+ onclick={() => toggleSectorExpand(site.siteId, sector.sectorId)}
103
+ aria-label={isSectorExpanded ? 'Collapse' : 'Expand'}
104
+ >
105
+ <svg width="12" height="12" viewBox="0 0 12 12" fill="currentColor">
106
+ {#if isSectorExpanded}
107
+ <path d="M2 4l4 4 4-4z" />
108
+ {:else}
109
+ <path d="M4 2l4 4-4 4z" />
110
+ {/if}
111
+ </svg>
112
+ </button>
113
+
114
+ <input
115
+ type="checkbox"
116
+ class="form-check-input"
117
+ id="sector-{sectorKey}"
118
+ checked={isSectorVisible}
119
+ onchange={() => onToggleSector(site.siteId, sector.sectorId)}
120
+ />
121
+
122
+ <label class="tree-label" for="sector-{sectorKey}">
123
+ {sector.sectorName}
124
+ </label>
125
+ </div>
126
+
127
+ <!-- Cells (Frequency Bands) -->
128
+ {#if isSectorExpanded}
129
+ <div class="tree-children">
130
+ {#each sector.cells as cell}
131
+ {@const cellKey = getCellKey(site.siteId, sector.sectorId, cell.band)}
132
+ {@const isCellVisible = visibility.cells[cellKey] ?? true}
133
+
134
+ <div class="tree-item cell-item">
135
+ <span class="expand-spacer"></span>
136
+
137
+ <input
138
+ type="checkbox"
139
+ class="form-check-input"
140
+ id="cell-{cellKey}"
141
+ checked={isCellVisible}
142
+ onchange={() => onToggleCell(site.siteId, sector.sectorId, cell.band)}
143
+ />
144
+
145
+ <label class="tree-label" for="cell-{cellKey}">
146
+ <span
147
+ class="band-indicator"
148
+ style:background-color={getBandColor(cell.band)}
149
+ ></span>
150
+ {getBandLabel(cell.band)}
151
+ </label>
152
+ </div>
153
+ {/each}
154
+ </div>
155
+ {/if}
156
+ </div>
157
+ {/each}
158
+ </div>
159
+ {/if}
160
+ </div>
161
+ {/each}
162
+ </div>
163
+ </div>
164
+
165
+ <style>
166
+ .site-tree {
167
+ height: 100%;
168
+ display: flex;
169
+ flex-direction: column;
170
+ background-color: #f8f9fa;
171
+ border-right: 1px solid #dee2e6;
172
+ }
173
+
174
+ .tree-header {
175
+ padding: 0.75rem 1rem;
176
+ border-bottom: 1px solid #dee2e6;
177
+ background-color: #fff;
178
+ }
179
+
180
+ .tree-content {
181
+ flex: 1;
182
+ overflow-y: auto;
183
+ padding: 0.5rem;
184
+ }
185
+
186
+ .tree-site {
187
+ margin-bottom: 0.5rem;
188
+ }
189
+
190
+ .tree-item {
191
+ display: flex;
192
+ align-items: center;
193
+ gap: 0.5rem;
194
+ padding: 0.375rem 0.5rem;
195
+ border-radius: 0.25rem;
196
+ transition: background-color 0.15s ease;
197
+ }
198
+
199
+ .tree-item:hover {
200
+ background-color: rgba(0, 0, 0, 0.05);
201
+ }
202
+
203
+ .site-item {
204
+ font-size: 0.95rem;
205
+ }
206
+
207
+ .sector-item {
208
+ font-size: 0.9rem;
209
+ margin-left: 1rem;
210
+ }
211
+
212
+ .cell-item {
213
+ font-size: 0.85rem;
214
+ margin-left: 2rem;
215
+ }
216
+
217
+ .expand-btn {
218
+ background: none;
219
+ border: none;
220
+ padding: 0;
221
+ width: 1rem;
222
+ height: 1rem;
223
+ display: flex;
224
+ align-items: center;
225
+ justify-content: center;
226
+ cursor: pointer;
227
+ color: #6c757d;
228
+ flex-shrink: 0;
229
+ }
230
+
231
+ .expand-btn:hover {
232
+ color: #495057;
233
+ }
234
+
235
+ .expand-spacer {
236
+ width: 1rem;
237
+ flex-shrink: 0;
238
+ }
239
+
240
+ .form-check-input {
241
+ cursor: pointer;
242
+ flex-shrink: 0;
243
+ margin: 0;
244
+ }
245
+
246
+ .tree-label {
247
+ cursor: pointer;
248
+ margin: 0;
249
+ user-select: none;
250
+ flex: 1;
251
+ display: flex;
252
+ align-items: center;
253
+ gap: 0.5rem;
254
+ }
255
+
256
+ .tree-children {
257
+ margin-top: 0.25rem;
258
+ }
259
+
260
+ .band-indicator {
261
+ display: inline-block;
262
+ width: 0.75rem;
263
+ height: 0.75rem;
264
+ border-radius: 50%;
265
+ flex-shrink: 0;
266
+ border: 1px solid rgba(0, 0, 0, 0.1);
267
+ }
268
+
269
+ /* Scrollbar styling */
270
+ .tree-content::-webkit-scrollbar {
271
+ width: 0.5rem;
272
+ }
273
+
274
+ .tree-content::-webkit-scrollbar-track {
275
+ background: #f1f1f1;
276
+ }
277
+
278
+ .tree-content::-webkit-scrollbar-thumb {
279
+ background: #888;
280
+ border-radius: 0.25rem;
281
+ }
282
+
283
+ .tree-content::-webkit-scrollbar-thumb:hover {
284
+ background: #555;
285
+ }
286
+ </style>
@@ -0,0 +1,11 @@
1
+ import type { CellularSite, VisibilityState } from './cellular.model.js';
2
+ interface Props {
3
+ sites: CellularSite[];
4
+ visibility: VisibilityState;
5
+ onToggleSite: (siteId: string) => void;
6
+ onToggleSector: (siteId: string, sectorId: string) => void;
7
+ onToggleCell: (siteId: string, sectorId: string, band: number) => void;
8
+ }
9
+ declare const SiteTree: import("svelte").Component<Props, {}, "">;
10
+ type SiteTree = ReturnType<typeof SiteTree>;
11
+ export default SiteTree;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Cellular Data Transformation Utilities
3
+ *
4
+ * These functions transform cellular site data into formats needed by TreeChartView.
5
+ * This is consumer-specific logic, not part of the generic library.
6
+ */
7
+ import type { CellularSite, CellLine } from './cellular.model';
8
+ import type { TreeNode } from '../core/TreeView/tree.model';
9
+ import type { Layout } from '../core/Charts/charts.model';
10
+ /**
11
+ * Transform cellular sites into TreeNode structure
12
+ */
13
+ export declare function cellularSitesToTreeNodes(sites: CellularSite[]): TreeNode[];
14
+ /**
15
+ * Flatten cellular sites into cell lines with visibility
16
+ */
17
+ export declare function flattenCellularSites(sites: CellularSite[], checkedPaths: Set<string>): CellLine[];
18
+ /**
19
+ * Transform cell lines into chart data format
20
+ */
21
+ export declare function cellLinesToChartData(cellLines: CellLine[]): any[];
22
+ /**
23
+ * Build chart layout from cell lines
24
+ */
25
+ export declare function buildCellularChartLayout(cellLines: CellLine[]): Layout;
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Cellular Data Transformation Utilities
3
+ *
4
+ * These functions transform cellular site data into formats needed by TreeChartView.
5
+ * This is consumer-specific logic, not part of the generic library.
6
+ */
7
+ import { getBandColor, getBandLabel } from './mock-cellular-data';
8
+ /**
9
+ * Transform cellular sites into TreeNode structure
10
+ */
11
+ export function cellularSitesToTreeNodes(sites) {
12
+ return sites.map(site => ({
13
+ id: site.siteId,
14
+ label: site.siteName,
15
+ icon: '📡',
16
+ defaultExpanded: false,
17
+ defaultChecked: true,
18
+ children: site.sectors.map(sector => ({
19
+ id: sector.sectorId,
20
+ label: sector.sectorName,
21
+ icon: '📶',
22
+ defaultExpanded: false,
23
+ defaultChecked: true,
24
+ children: sector.cells.map(cell => ({
25
+ id: String(cell.band),
26
+ label: getBandLabel(cell.band),
27
+ icon: '📻',
28
+ defaultChecked: true,
29
+ metadata: {
30
+ band: cell.band,
31
+ color: getBandColor(cell.band)
32
+ }
33
+ }))
34
+ }))
35
+ }));
36
+ }
37
+ /**
38
+ * Flatten cellular sites into cell lines with visibility
39
+ */
40
+ export function flattenCellularSites(sites, checkedPaths) {
41
+ const lines = [];
42
+ sites.forEach(site => {
43
+ site.sectors.forEach(sector => {
44
+ sector.cells.forEach(cell => {
45
+ // Path construction matches tree-utils.ts flattenTree logic
46
+ const cellKey = `${site.siteId}:${sector.sectorId}:${cell.band}`;
47
+ const isVisible = checkedPaths.has(cellKey);
48
+ lines.push({
49
+ siteId: site.siteId,
50
+ siteName: site.siteName,
51
+ sectorId: sector.sectorId,
52
+ sectorName: sector.sectorName,
53
+ cellId: cell.cellId,
54
+ band: cell.band,
55
+ label: `${site.siteName} - ${sector.sectorName} - ${getBandLabel(cell.band)}`,
56
+ color: getBandColor(cell.band),
57
+ kpis: cell.kpis,
58
+ visible: isVisible
59
+ });
60
+ });
61
+ });
62
+ });
63
+ return lines;
64
+ }
65
+ /**
66
+ * Transform cell lines into chart data format
67
+ */
68
+ export function cellLinesToChartData(cellLines) {
69
+ const data = [];
70
+ cellLines.forEach(line => {
71
+ const timestamps = new Set(line.kpis.throughput.map(kpi => kpi.timestamp));
72
+ timestamps.forEach(timestamp => {
73
+ const throughputPoint = line.kpis.throughput.find(kpi => kpi.timestamp === timestamp);
74
+ const taPoint = line.kpis.timingAdvance.find(kpi => kpi.timestamp === timestamp);
75
+ if (throughputPoint && taPoint) {
76
+ data.push({
77
+ TIMESTAMP: timestamp,
78
+ [`DL_THROUGHPUT_${line.cellId}`]: throughputPoint.value,
79
+ [`AVG_TA_${line.cellId}`]: taPoint.value,
80
+ _cellId: line.cellId,
81
+ _label: line.label,
82
+ _color: line.color
83
+ });
84
+ }
85
+ });
86
+ });
87
+ return data;
88
+ }
89
+ /**
90
+ * Build chart layout from cell lines
91
+ */
92
+ export function buildCellularChartLayout(cellLines) {
93
+ const throughputKPIs = cellLines.map(line => ({
94
+ rawName: `DL_THROUGHPUT_${line.cellId}`,
95
+ name: line.label,
96
+ scale: 'absolute',
97
+ unit: 'Mbps',
98
+ color: line.color
99
+ }));
100
+ const taKPIs = cellLines.map(line => ({
101
+ rawName: `AVG_TA_${line.cellId}`,
102
+ name: line.label,
103
+ scale: 'absolute',
104
+ unit: 'μs',
105
+ color: line.color
106
+ }));
107
+ return {
108
+ layoutName: 'Cellular Network KPIs',
109
+ sections: [
110
+ {
111
+ id: 'cellular-kpis',
112
+ title: 'Network Performance',
113
+ grid: '1x2',
114
+ charts: [
115
+ {
116
+ title: 'Downlink Throughput',
117
+ yLeft: throughputKPIs,
118
+ yRight: []
119
+ },
120
+ {
121
+ title: 'Average Timing Advance',
122
+ yLeft: taKPIs,
123
+ yRight: []
124
+ }
125
+ ]
126
+ }
127
+ ]
128
+ };
129
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Cellular Network Data Models
3
+ *
4
+ * Represents hierarchical structure: Site → Sector → Cell (per frequency band)
5
+ */
6
+ export type FrequencyBand = 700 | 800 | 900 | 1800 | 2100 | 2600 | 3500;
7
+ export interface KPIData {
8
+ timestamp: string;
9
+ value: number;
10
+ }
11
+ export interface CellKPIs {
12
+ throughput: KPIData[];
13
+ timingAdvance: KPIData[];
14
+ [key: string]: KPIData[];
15
+ }
16
+ export interface Cell {
17
+ cellId: string;
18
+ band: FrequencyBand;
19
+ kpis: CellKPIs;
20
+ }
21
+ export interface Sector {
22
+ sectorId: string;
23
+ sectorName: string;
24
+ azimuth?: number;
25
+ cells: Cell[];
26
+ }
27
+ export interface CellularSite {
28
+ siteId: string;
29
+ siteName: string;
30
+ sectors: Sector[];
31
+ }
32
+ /**
33
+ * Flattened view of a cell for chart rendering
34
+ * Includes full hierarchy path and visibility state
35
+ */
36
+ export interface CellLine {
37
+ siteId: string;
38
+ siteName: string;
39
+ sectorId: string;
40
+ sectorName: string;
41
+ cellId: string;
42
+ band: FrequencyBand;
43
+ label: string;
44
+ color?: string;
45
+ kpis: CellKPIs;
46
+ visible: boolean;
47
+ }
48
+ /**
49
+ * Visibility state for hierarchical control
50
+ */
51
+ export interface VisibilityState {
52
+ sites: Record<string, boolean>;
53
+ sectors: Record<string, boolean>;
54
+ cells: Record<string, boolean>;
55
+ }
56
+ /**
57
+ * Configuration for cellular charts view
58
+ */
59
+ export interface CellularChartsConfig {
60
+ sites: CellularSite[];
61
+ kpis: string[];
62
+ colorScheme?: 'by-band' | 'by-site' | 'unique';
63
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Cellular Network Data Models
3
+ *
4
+ * Represents hierarchical structure: Site → Sector → Cell (per frequency band)
5
+ */
6
+ export {};
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Cellular Network Visualization Library
3
+ *
4
+ * Provides components for visualizing cellular site data with hierarchical
5
+ * site/sector/cell selection and KPI charting.
6
+ */
7
+ export { default as CellularChartsView } from './CellularChartsView.svelte';
8
+ export { default as SiteTree } from './SiteTree.svelte';
9
+ export * from './cellular.model.js';
10
+ export * from './mock-cellular-data.js';
11
+ export * from './cellular-transforms.js';
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Cellular Network Visualization Library
3
+ *
4
+ * Provides components for visualizing cellular site data with hierarchical
5
+ * site/sector/cell selection and KPI charting.
6
+ */
7
+ export { default as CellularChartsView } from './CellularChartsView.svelte';
8
+ export { default as SiteTree } from './SiteTree.svelte';
9
+ export * from './cellular.model.js';
10
+ export * from './mock-cellular-data.js';
11
+ export * from './cellular-transforms.js';
@@ -0,0 +1,13 @@
1
+ import type { CellularSite, FrequencyBand } from './cellular.model.js';
2
+ /**
3
+ * Generate mock cellular sites for testing
4
+ */
5
+ export declare function generateMockCellularData(): CellularSite[];
6
+ /**
7
+ * Get color for frequency band (consistent color scheme)
8
+ */
9
+ export declare function getBandColor(band: FrequencyBand): string;
10
+ /**
11
+ * Get label for frequency band
12
+ */
13
+ export declare function getBandLabel(band: FrequencyBand): string;