@smartnet360/svelte-components 0.0.84 → 0.0.86

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 (122) 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/SiteCheck.svelte +60 -80
  9. package/dist/apps/site-check/data-loader.d.ts +9 -6
  10. package/dist/apps/site-check/data-loader.js +2 -11
  11. package/dist/apps/site-check/helper.d.ts +3 -2
  12. package/dist/apps/site-check/helper.js +7 -5
  13. package/dist/apps/site-check/index.d.ts +1 -1
  14. package/dist/apps/site-check/transforms.d.ts +4 -2
  15. package/dist/apps/site-check/transforms.js +49 -10
  16. package/dist/core/Charts/GlobalControls.svelte +0 -4
  17. package/dist/core/Desktop/Grid/ResizeHandle.svelte +0 -7
  18. package/dist/core/Desktop/Grid/resizeStore.js +0 -1
  19. package/dist/index.d.ts +1 -0
  20. package/dist/index.js +2 -0
  21. package/dist/map-v2/demo/DemoMap.svelte +0 -2
  22. package/dist/map-v2/demo/demo-cells.js +0 -1
  23. package/dist/map-v2/features/cells/layers/CellsLayer.svelte +7 -26
  24. package/dist/map-v2/features/cells/utils/cellTree.js +0 -29
  25. package/dist/map-v2/features/repeaters/layers/RepeaterLabelsLayer.svelte +3 -27
  26. package/dist/map-v2/features/repeaters/layers/RepeatersLayer.svelte +8 -25
  27. package/dist/map-v2/features/repeaters/utils/repeaterTree.js +0 -6
  28. package/dist/map-v2/features/sites/controls/SiteFilterControl.svelte +0 -8
  29. package/dist/map-v2/features/sites/utils/siteTreeUtils.js +0 -6
  30. package/dist/map-v3/core/components/Map.svelte +89 -0
  31. package/dist/map-v3/core/components/Map.svelte.d.ts +13 -0
  32. package/dist/map-v3/core/controls/FeatureSettingsControl.svelte +103 -0
  33. package/dist/map-v3/core/controls/FeatureSettingsControl.svelte.d.ts +15 -0
  34. package/dist/map-v3/core/controls/MapStyleControl.svelte +271 -0
  35. package/dist/map-v3/core/controls/MapStyleControl.svelte.d.ts +28 -0
  36. package/dist/map-v3/core/index.d.ts +3 -0
  37. package/dist/map-v3/core/index.js +3 -0
  38. package/dist/map-v3/core/stores/map.store.svelte.d.ts +8 -0
  39. package/dist/map-v3/core/stores/map.store.svelte.js +29 -0
  40. package/dist/map-v3/core/stores/viewport.store.svelte.d.ts +38 -0
  41. package/dist/map-v3/core/stores/viewport.store.svelte.js +107 -0
  42. package/dist/map-v3/demo/DemoMap.svelte +104 -0
  43. package/dist/map-v3/demo/DemoMap.svelte.d.ts +6 -0
  44. package/dist/map-v3/demo/demo-cells.d.ts +13 -0
  45. package/dist/map-v3/demo/demo-cells.js +130 -0
  46. package/dist/map-v3/demo/demo-data.d.ts +8 -0
  47. package/dist/map-v3/demo/demo-data.js +104 -0
  48. package/dist/map-v3/demo/demo-repeaters.d.ts +13 -0
  49. package/dist/map-v3/demo/demo-repeaters.js +73 -0
  50. package/dist/map-v3/features/cells/components/CellFilterControl.svelte +208 -0
  51. package/dist/map-v3/features/cells/components/CellFilterControl.svelte.d.ts +12 -0
  52. package/dist/map-v3/features/cells/components/CellSettingsPanel.svelte +229 -0
  53. package/dist/map-v3/features/cells/components/CellSettingsPanel.svelte.d.ts +7 -0
  54. package/dist/map-v3/features/cells/constants.d.ts +18 -0
  55. package/dist/map-v3/features/cells/constants.js +37 -0
  56. package/dist/map-v3/features/cells/layers/CellLabelsLayer.svelte +230 -0
  57. package/dist/map-v3/features/cells/layers/CellLabelsLayer.svelte.d.ts +11 -0
  58. package/dist/map-v3/features/cells/layers/CellsLayer.svelte +194 -0
  59. package/dist/map-v3/features/cells/layers/CellsLayer.svelte.d.ts +11 -0
  60. package/dist/map-v3/features/cells/layers/index.d.ts +2 -0
  61. package/dist/map-v3/features/cells/layers/index.js +2 -0
  62. package/dist/map-v3/features/cells/logic/geometry.d.ts +12 -0
  63. package/dist/map-v3/features/cells/logic/geometry.js +35 -0
  64. package/dist/map-v3/features/cells/logic/grouping.d.ts +18 -0
  65. package/dist/map-v3/features/cells/logic/grouping.js +30 -0
  66. package/dist/map-v3/features/cells/logic/tree-adapter.d.ts +11 -0
  67. package/dist/map-v3/features/cells/logic/tree-adapter.js +53 -0
  68. package/dist/map-v3/features/cells/stores/cell.data.svelte.d.ts +9 -0
  69. package/dist/map-v3/features/cells/stores/cell.data.svelte.js +16 -0
  70. package/dist/map-v3/features/cells/stores/cell.display.svelte.d.ts +25 -0
  71. package/dist/map-v3/features/cells/stores/cell.display.svelte.js +67 -0
  72. package/dist/map-v3/features/cells/stores/cell.registry.svelte.d.ts +23 -0
  73. package/dist/map-v3/features/cells/stores/cell.registry.svelte.js +68 -0
  74. package/dist/map-v3/features/cells/types.d.ts +62 -0
  75. package/dist/map-v3/features/cells/types.js +6 -0
  76. package/dist/map-v3/features/repeaters/components/RepeaterFilterControl.svelte +148 -0
  77. package/dist/map-v3/features/repeaters/components/RepeaterFilterControl.svelte.d.ts +12 -0
  78. package/dist/map-v3/features/repeaters/components/RepeaterSettingsPanel.svelte +209 -0
  79. package/dist/map-v3/features/repeaters/components/RepeaterSettingsPanel.svelte.d.ts +7 -0
  80. package/dist/map-v3/features/repeaters/layers/RepeaterLabelsLayer.svelte +177 -0
  81. package/dist/map-v3/features/repeaters/layers/RepeaterLabelsLayer.svelte.d.ts +11 -0
  82. package/dist/map-v3/features/repeaters/layers/RepeatersLayer.svelte +163 -0
  83. package/dist/map-v3/features/repeaters/layers/RepeatersLayer.svelte.d.ts +11 -0
  84. package/dist/map-v3/features/repeaters/logic/geometry.d.ts +3 -0
  85. package/dist/map-v3/features/repeaters/logic/geometry.js +23 -0
  86. package/dist/map-v3/features/repeaters/logic/grouping.d.ts +8 -0
  87. package/dist/map-v3/features/repeaters/logic/grouping.js +20 -0
  88. package/dist/map-v3/features/repeaters/logic/tree-adapter.d.ts +8 -0
  89. package/dist/map-v3/features/repeaters/logic/tree-adapter.js +43 -0
  90. package/dist/map-v3/features/repeaters/stores/repeater.data.svelte.d.ts +8 -0
  91. package/dist/map-v3/features/repeaters/stores/repeater.data.svelte.js +13 -0
  92. package/dist/map-v3/features/repeaters/stores/repeater.display.svelte.d.ts +21 -0
  93. package/dist/map-v3/features/repeaters/stores/repeater.display.svelte.js +64 -0
  94. package/dist/map-v3/features/repeaters/stores/repeater.registry.svelte.d.ts +23 -0
  95. package/dist/map-v3/features/repeaters/stores/repeater.registry.svelte.js +68 -0
  96. package/dist/map-v3/features/repeaters/types.d.ts +18 -0
  97. package/dist/map-v3/features/repeaters/types.js +1 -0
  98. package/dist/map-v3/features/sites/components/SiteFilterControl.svelte +119 -0
  99. package/dist/map-v3/features/sites/components/SiteFilterControl.svelte.d.ts +12 -0
  100. package/dist/map-v3/features/sites/components/SiteSettingsPanel.svelte +241 -0
  101. package/dist/map-v3/features/sites/components/SiteSettingsPanel.svelte.d.ts +7 -0
  102. package/dist/map-v3/features/sites/layers/SiteLabelsLayer.svelte +152 -0
  103. package/dist/map-v3/features/sites/layers/SiteLabelsLayer.svelte.d.ts +11 -0
  104. package/dist/map-v3/features/sites/layers/SitesLayer.svelte +132 -0
  105. package/dist/map-v3/features/sites/layers/SitesLayer.svelte.d.ts +11 -0
  106. package/dist/map-v3/features/sites/logic/tree-adapter.d.ts +9 -0
  107. package/dist/map-v3/features/sites/logic/tree-adapter.js +75 -0
  108. package/dist/map-v3/features/sites/stores/site.data.svelte.d.ts +8 -0
  109. package/dist/map-v3/features/sites/stores/site.data.svelte.js +40 -0
  110. package/dist/map-v3/features/sites/stores/site.display.svelte.d.ts +20 -0
  111. package/dist/map-v3/features/sites/stores/site.display.svelte.js +63 -0
  112. package/dist/map-v3/features/sites/stores/site.registry.svelte.d.ts +13 -0
  113. package/dist/map-v3/features/sites/stores/site.registry.svelte.js +83 -0
  114. package/dist/map-v3/features/sites/types.d.ts +12 -0
  115. package/dist/map-v3/features/sites/types.js +1 -0
  116. package/dist/map-v3/index.d.ts +26 -0
  117. package/dist/map-v3/index.js +31 -0
  118. package/dist/map-v3/shared/controls/MapControl.svelte +242 -0
  119. package/dist/map-v3/shared/controls/MapControl.svelte.d.ts +27 -0
  120. package/dist/map-v3/shared/index.d.ts +1 -0
  121. package/dist/map-v3/shared/index.js +1 -0
  122. package/package.json +1 -1
@@ -0,0 +1,18 @@
1
+ export interface Repeater {
2
+ repeaterId: string;
3
+ donorCellId: string;
4
+ donorCellName: string;
5
+ latitude: number;
6
+ longitude: number;
7
+ azimuth: number;
8
+ beamwidth: number;
9
+ tech: string;
10
+ fband: string;
11
+ type: 'REPEATER';
12
+ height: number;
13
+ factory_nbr: string;
14
+ provider: string;
15
+ featureGroup: string;
16
+ [key: string]: any;
17
+ }
18
+ export type RepeaterGroupingField = 'tech' | 'fband' | 'provider' | 'featureGroup' | 'none';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,119 @@
1
+ <script lang="ts">
2
+ import { 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 { SiteDataStore } from '../stores/site.data.svelte';
7
+ import type { SiteRegistry } from '../stores/site.registry.svelte';
8
+ import type { SiteDisplayStore } from '../stores/site.display.svelte';
9
+ import { buildSiteTree } from '../logic/tree-adapter';
10
+
11
+ interface Props {
12
+ dataStore: SiteDataStore;
13
+ registry: SiteRegistry;
14
+ displayStore: SiteDisplayStore;
15
+ position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
16
+ }
17
+
18
+ let { dataStore, registry, displayStore, position = 'top-left' }: Props = $props();
19
+
20
+ // Create Tree Store
21
+ let treeStore = $derived.by(() => {
22
+ const _sites = dataStore.sites;
23
+
24
+ return untrack(() => {
25
+ const nodes = buildSiteTree(_sites);
26
+ return createTreeStore({
27
+ nodes,
28
+ namespace: `${registry.namespace}:site-tree`,
29
+ persistState: true,
30
+ defaultExpandAll: false
31
+ });
32
+ });
33
+ });
34
+
35
+ // Sync Tree Selection -> Site Registry Visibility
36
+ $effect(() => {
37
+ const unsubscribe = treeStore.subscribe((val) => {
38
+ // Iterate all leaf nodes to sync visibility
39
+
40
+ // Collect all site IDs from the tree
41
+ const allSiteIds: string[] = [];
42
+ const visibleSiteIds: string[] = [];
43
+
44
+ val.state.nodes.forEach((nodeState) => {
45
+ // Check if it's a leaf node (Level 2 group)
46
+ if (nodeState.node.children && nodeState.node.children.length > 0) return;
47
+
48
+ // It's a leaf (Level 2 group)
49
+ const siteIds = nodeState.node.metadata?.siteIds || [];
50
+ allSiteIds.push(...siteIds);
51
+
52
+ if (val.state.checkedPaths.has(nodeState.path)) {
53
+ visibleSiteIds.push(...siteIds);
54
+ }
55
+ });
56
+
57
+ // Update registry
58
+ const hiddenIds = allSiteIds.filter(id => !visibleSiteIds.includes(id));
59
+
60
+ // Optimization: Check if anything actually changed
61
+ let needsUpdate = false;
62
+ // Simple check: if counts match, assume mostly correct, but better to check content
63
+ // For now, just update. Registry handles diffing somewhat.
64
+ registry.setVisible(visibleSiteIds, true);
65
+ registry.setVisible(hiddenIds, false);
66
+ });
67
+
68
+ return () => {
69
+ unsubscribe();
70
+ };
71
+ });
72
+
73
+ function handleColorChange(node: any, event: Event) {
74
+ const input = event.target as HTMLInputElement;
75
+ const color = input.value;
76
+ if (node.metadata?.level1 && node.metadata?.level2) {
77
+ registry.setGroupColor(node.metadata.level1, node.metadata.level2, color);
78
+ }
79
+ }
80
+
81
+ function getGroupColor(node: any): string {
82
+ if (node.metadata?.level1 && node.metadata?.level2) {
83
+ return registry.getColor(node.metadata.level1, node.metadata.level2, node.metadata.color);
84
+ }
85
+ return '#3388ff';
86
+ }
87
+ </script>
88
+
89
+ <MapControl {position} title="Sites" icon="broadcast-pin" controlWidth="300px">
90
+ <div class="p-2">
91
+ <!-- <div class="mb-2 text-muted small">
92
+ {dataStore.sites.length} Sites
93
+ </div> -->
94
+ <div class="border rounded bg-white" style="max-height: 400px; overflow-y: auto;">
95
+ <TreeView showControls={false} store={$treeStore}>
96
+ {#snippet children({ node, state })}
97
+ <!-- Color Picker (Only for leaves / Level 2) -->
98
+ {#if !node.children || node.children.length === 0}
99
+ <div
100
+ class="d-flex align-items-center"
101
+ role="group"
102
+ onclick={(e) => e.stopPropagation()}
103
+ onkeydown={(e) => e.stopPropagation()}
104
+ >
105
+ <input
106
+ type="color"
107
+ class="form-control form-control-color form-control-sm border-0 p-0"
108
+ style="width: 16px; height: 16px; min-height: 0;"
109
+ value={getGroupColor(node)}
110
+ oninput={(e) => handleColorChange(node, e)}
111
+ title="Change color"
112
+ />
113
+ </div>
114
+ {/if}
115
+ {/snippet}
116
+ </TreeView>
117
+ </div>
118
+ </div>
119
+ </MapControl>
@@ -0,0 +1,12 @@
1
+ import type { SiteDataStore } from '../stores/site.data.svelte';
2
+ import type { SiteRegistry } from '../stores/site.registry.svelte';
3
+ import type { SiteDisplayStore } from '../stores/site.display.svelte';
4
+ interface Props {
5
+ dataStore: SiteDataStore;
6
+ registry: SiteRegistry;
7
+ displayStore: SiteDisplayStore;
8
+ position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
9
+ }
10
+ declare const SiteFilterControl: import("svelte").Component<Props, {}, "">;
11
+ type SiteFilterControl = ReturnType<typeof SiteFilterControl>;
12
+ export default SiteFilterControl;
@@ -0,0 +1,241 @@
1
+ <script lang="ts">
2
+ import type { SiteDisplayStore } from '../stores/site.display.svelte';
3
+
4
+ interface Props {
5
+ displayStore: SiteDisplayStore;
6
+ }
7
+
8
+ let { displayStore }: Props = $props();
9
+ </script>
10
+
11
+ <div class="card border-0 shadow-sm rounded-2">
12
+ <div class="card-body bg-light p-3">
13
+ <!-- Radius -->
14
+ <div class="row align-items-center g-2 mb-3">
15
+ <div class="col-4 text-secondary fw-semibold small text-uppercase">Radius</div>
16
+ <div class="col-3 text-end">
17
+ <span class="badge bg-white text-muted border">{displayStore.radius}px</span>
18
+ </div>
19
+ <div class="col-5">
20
+ <input
21
+ type="range"
22
+ class="form-range w-100"
23
+ min="2"
24
+ max="20"
25
+ step="1"
26
+ bind:value={displayStore.radius}
27
+ />
28
+ </div>
29
+ </div>
30
+
31
+ <!-- Fill Color -->
32
+ <div class="row align-items-center g-2 mb-2">
33
+ <div class="col-4 text-secondary fw-semibold small text-uppercase">Fill Color</div>
34
+ <div class="col-8">
35
+ <div class="input-group input-group-sm">
36
+ <input
37
+ type="color"
38
+ class="form-control form-control-color"
39
+ bind:value={displayStore.color}
40
+ title="Fill Color"
41
+ />
42
+ <span class="input-group-text bg-white text-muted small flex-grow-1 font-monospace">
43
+ {displayStore.color}
44
+ </span>
45
+ </div>
46
+ </div>
47
+ </div>
48
+
49
+ <!-- Fill Opacity -->
50
+ <div class="row align-items-center g-2 mb-3">
51
+ <div class="col-4 text-secondary fw-semibold small text-uppercase">Opacity</div>
52
+ <div class="col-3 text-end">
53
+ <span class="badge bg-white text-muted border">{Math.round(displayStore.opacity * 100)}%</span>
54
+ </div>
55
+ <div class="col-5">
56
+ <input
57
+ type="range"
58
+ class="form-range w-100"
59
+ min="0"
60
+ max="1"
61
+ step="0.1"
62
+ bind:value={displayStore.opacity}
63
+ />
64
+ </div>
65
+ </div>
66
+
67
+ <!-- Stroke Color -->
68
+ <div class="row align-items-center g-2 mb-2">
69
+ <div class="col-4 text-secondary fw-semibold small text-uppercase">Stroke</div>
70
+ <div class="col-8">
71
+ <div class="input-group input-group-sm">
72
+ <input
73
+ type="color"
74
+ class="form-control form-control-color"
75
+ bind:value={displayStore.strokeColor}
76
+ title="Stroke Color"
77
+ />
78
+ <span class="input-group-text bg-white text-muted small flex-grow-1 font-monospace">
79
+ {displayStore.strokeColor}
80
+ </span>
81
+ </div>
82
+ </div>
83
+ </div>
84
+
85
+ <!-- Stroke Width -->
86
+ <div class="row align-items-center g-2 mb-3">
87
+ <div class="col-4 text-secondary fw-semibold small text-uppercase">Stroke Width</div>
88
+ <div class="col-3 text-end">
89
+ <span class="badge bg-white text-muted border">{displayStore.strokeWidth}px</span>
90
+ </div>
91
+ <div class="col-5">
92
+ <input
93
+ type="range"
94
+ class="form-range w-100"
95
+ min="0"
96
+ max="5"
97
+ step="0.5"
98
+ bind:value={displayStore.strokeWidth}
99
+ />
100
+ </div>
101
+ </div>
102
+
103
+ <div class="border-top my-3"></div>
104
+
105
+ <!-- Show Labels -->
106
+ <div class="row align-items-center g-2 mb-3">
107
+ <div class="col-4 text-secondary fw-semibold small text-uppercase">Show Labels</div>
108
+ <div class="col-3"></div>
109
+ <div class="col-5">
110
+ <div class="form-check form-switch m-0 d-flex align-items-center justify-content-end">
111
+ <input
112
+ type="checkbox"
113
+ class="form-check-input"
114
+ role="switch"
115
+ bind:checked={displayStore.showLabels}
116
+ />
117
+ </div>
118
+ </div>
119
+ </div>
120
+
121
+ {#if displayStore.showLabels}
122
+ <div class="ps-3 border-start border-2 mb-3">
123
+ <!-- Label Fields -->
124
+ <div class="mb-2">
125
+ <div class="text-secondary fw-semibold small text-uppercase mb-1">Label Fields</div>
126
+ <div class="row g-1">
127
+ <div class="col-6">
128
+ <select class="form-select form-select-sm" bind:value={displayStore.labels.primary}>
129
+ <option value="siteId">Site ID</option>
130
+ <option value="siteName">Name</option>
131
+ <option value="provider">Provider</option>
132
+ <option value="cellCount">Cell Count</option>
133
+ </select>
134
+ </div>
135
+ <div class="col-6">
136
+ <select class="form-select form-select-sm" bind:value={displayStore.labels.secondary}>
137
+ <option value="none">None</option>
138
+ <option value="siteId">Site ID</option>
139
+ <option value="siteName">Name</option>
140
+ <option value="provider">Provider</option>
141
+ <option value="cellCount">Cell Count</option>
142
+ </select>
143
+ </div>
144
+ </div>
145
+ </div>
146
+
147
+ <!-- Label Settings -->
148
+ <div class="mt-3">
149
+ <!-- Label Distance -->
150
+ <div class="row align-items-center g-2 mb-2">
151
+ <div class="col-4 text-secondary small">Distance</div>
152
+ <div class="col-3 text-end">
153
+ <span class="badge bg-white text-muted border">{displayStore.labelPixelDistance}px</span>
154
+ </div>
155
+ <div class="col-5">
156
+ <input
157
+ type="range"
158
+ class="form-range w-100"
159
+ min="5"
160
+ max="100"
161
+ step="5"
162
+ bind:value={displayStore.labelPixelDistance}
163
+ />
164
+ </div>
165
+ </div>
166
+
167
+ <!-- Font Size -->
168
+ <div class="row align-items-center g-2 mb-3">
169
+ <div class="col-4 text-secondary small">Font Size</div>
170
+ <div class="col-3 text-end">
171
+ <span class="badge bg-white text-muted border">{displayStore.labelFontSize}px</span>
172
+ </div>
173
+ <div class="col-5">
174
+ <input
175
+ type="range"
176
+ class="form-range w-100"
177
+ min="8"
178
+ max="24"
179
+ step="1"
180
+ bind:value={displayStore.labelFontSize}
181
+ />
182
+ </div>
183
+ </div>
184
+
185
+ <!-- Label Colors -->
186
+ <div class="row align-items-center g-2 mb-2">
187
+ <div class="col-4 text-secondary small">Color</div>
188
+ <div class="col-8">
189
+ <div class="input-group input-group-sm">
190
+ <input
191
+ type="color"
192
+ class="form-control form-control-color"
193
+ bind:value={displayStore.labelColor}
194
+ title="Label Color"
195
+ />
196
+ <span class="input-group-text bg-white text-muted small flex-grow-1 font-monospace">
197
+ {displayStore.labelColor}
198
+ </span>
199
+ </div>
200
+ </div>
201
+ </div>
202
+
203
+ <!-- Halo Settings -->
204
+ <div class="row align-items-center g-2 mb-2">
205
+ <div class="col-4 text-secondary small">Halo</div>
206
+ <div class="col-8">
207
+ <div class="input-group input-group-sm">
208
+ <input
209
+ type="color"
210
+ class="form-control form-control-color"
211
+ bind:value={displayStore.labelHaloColor}
212
+ title="Halo Color"
213
+ />
214
+ <span class="input-group-text bg-white text-muted small flex-grow-1 font-monospace">
215
+ {displayStore.labelHaloColor}
216
+ </span>
217
+ </div>
218
+ </div>
219
+ </div>
220
+
221
+ <div class="row align-items-center g-2 mb-3">
222
+ <div class="col-4 text-secondary small">Halo Width</div>
223
+ <div class="col-3 text-end">
224
+ <span class="badge bg-white text-muted border">{displayStore.labelHaloWidth}px</span>
225
+ </div>
226
+ <div class="col-5">
227
+ <input
228
+ type="range"
229
+ class="form-range w-100"
230
+ min="0"
231
+ max="5"
232
+ step="0.5"
233
+ bind:value={displayStore.labelHaloWidth}
234
+ />
235
+ </div>
236
+ </div>
237
+ </div>
238
+ </div>
239
+ {/if}
240
+ </div>
241
+ </div>
@@ -0,0 +1,7 @@
1
+ import type { SiteDisplayStore } from '../stores/site.display.svelte';
2
+ interface Props {
3
+ displayStore: SiteDisplayStore;
4
+ }
5
+ declare const SiteSettingsPanel: import("svelte").Component<Props, {}, "">;
6
+ type SiteSettingsPanel = ReturnType<typeof SiteSettingsPanel>;
7
+ export default SiteSettingsPanel;
@@ -0,0 +1,152 @@
1
+ <script lang="ts">
2
+ import { getContext } from 'svelte';
3
+ import type { MapStore } from '../../../core/stores/map.store.svelte';
4
+ import type { SiteDataStore } from '../stores/site.data.svelte';
5
+ import type { SiteDisplayStore } from '../stores/site.display.svelte';
6
+ import type { SiteRegistry } from '../stores/site.registry.svelte';
7
+ import type { Site } from '../types';
8
+ import type mapboxgl from 'mapbox-gl';
9
+
10
+ interface Props {
11
+ dataStore: SiteDataStore;
12
+ displayStore: SiteDisplayStore;
13
+ registry: SiteRegistry;
14
+ }
15
+
16
+ let { dataStore, displayStore, registry }: Props = $props();
17
+
18
+ const mapStore = getContext<MapStore>('MAP_CONTEXT');
19
+ let sourceId = 'site-labels-source';
20
+ let layerId = 'site-labels-layer';
21
+ let updateTimeout: any;
22
+
23
+ // Helper to get label text
24
+ function getLabelText(site: Site, config: { primary: string, secondary: string }): string {
25
+ const pVal = site[config.primary as keyof Site];
26
+ const sVal = config.secondary !== 'none' ? site[config.secondary as keyof Site] : null;
27
+
28
+ let text = pVal != null ? String(pVal) : '';
29
+ if (sVal != null) {
30
+ text += ` | ${sVal}`;
31
+ }
32
+ return text;
33
+ }
34
+
35
+ // React to changes
36
+ $effect(() => {
37
+ // Read dependencies
38
+ const _sites = dataStore.sites;
39
+ const _show = displayStore.showLabels;
40
+ const _dist = displayStore.labelPixelDistance;
41
+ const _size = displayStore.labelFontSize;
42
+ const _lPrim = displayStore.labels.primary;
43
+ const _lSec = displayStore.labels.secondary;
44
+ const _color = displayStore.labelColor;
45
+ const _haloColor = displayStore.labelHaloColor;
46
+ const _haloWidth = displayStore.labelHaloWidth;
47
+ const _version = registry.version;
48
+
49
+ const map = mapStore.map;
50
+ if (map && map.getLayer(layerId)) {
51
+ map.setPaintProperty(layerId, 'text-color', displayStore.labelColor);
52
+ map.setPaintProperty(layerId, 'text-halo-color', displayStore.labelHaloColor);
53
+ map.setPaintProperty(layerId, 'text-halo-width', displayStore.labelHaloWidth);
54
+ }
55
+
56
+ updateLayer();
57
+ });
58
+
59
+ function updateLayer() {
60
+ const map = mapStore.map;
61
+ if (!map) return;
62
+
63
+ clearTimeout(updateTimeout);
64
+ updateTimeout = setTimeout(() => {
65
+ renderLabels(map);
66
+ }, 150);
67
+ }
68
+
69
+ function renderLabels(map: mapboxgl.Map) {
70
+ if (!displayStore.showLabels) {
71
+ if (map.getLayer(layerId)) map.setLayoutProperty(layerId, 'visibility', 'none');
72
+ return;
73
+ }
74
+
75
+ if (map.getLayer(layerId)) map.setLayoutProperty(layerId, 'visibility', 'visible');
76
+
77
+ // Update text size if changed
78
+ if (map.getLayer(layerId)) {
79
+ map.setLayoutProperty(layerId, 'text-size', displayStore.labelFontSize);
80
+ }
81
+
82
+ const bounds = map.getBounds();
83
+ if (!bounds) return;
84
+
85
+ const sites = dataStore.sites;
86
+ const features: GeoJSON.Feature[] = [];
87
+
88
+ // Convert pixel distance to ems (approx 1em = 16px or labelFontSize)
89
+ const distEm = displayStore.labelPixelDistance / displayStore.labelFontSize;
90
+
91
+ for (const site of sites) {
92
+ if (!registry.isVisible(site.siteId)) continue;
93
+ if (!bounds.contains([site.longitude, site.latitude])) continue;
94
+
95
+ const labelText = getLabelText(site, displayStore.labels);
96
+ if (!labelText) continue;
97
+
98
+ // For sites (circles), we usually place label on top or bottom
99
+ // Let's place it on top (offset Y negative)
100
+ const offsetX = 0;
101
+ const offsetY = -distEm;
102
+
103
+ features.push({
104
+ type: 'Feature',
105
+ geometry: {
106
+ type: 'Point',
107
+ coordinates: [site.longitude, site.latitude]
108
+ },
109
+ properties: {
110
+ label: labelText,
111
+ offset: [offsetX, offsetY]
112
+ }
113
+ });
114
+ }
115
+
116
+ const geojson: GeoJSON.FeatureCollection = {
117
+ type: 'FeatureCollection',
118
+ features
119
+ };
120
+
121
+ const source = map.getSource(sourceId) as mapboxgl.GeoJSONSource;
122
+ if (source) {
123
+ source.setData(geojson);
124
+ } else {
125
+ map.addSource(sourceId, {
126
+ type: 'geojson',
127
+ data: geojson
128
+ });
129
+
130
+ map.addLayer({
131
+ id: layerId,
132
+ type: 'symbol',
133
+ source: sourceId,
134
+ layout: {
135
+ 'text-field': ['get', 'label'],
136
+ 'text-font': ['Open Sans Semibold', 'Arial Unicode MS Bold'],
137
+ 'text-size': displayStore.labelFontSize,
138
+ 'text-offset': ['get', 'offset'],
139
+ 'text-anchor': 'center',
140
+ 'text-justify': 'auto',
141
+ 'text-allow-overlap': false,
142
+ 'text-ignore-placement': false
143
+ },
144
+ paint: {
145
+ 'text-color': displayStore.labelColor,
146
+ 'text-halo-color': displayStore.labelHaloColor,
147
+ 'text-halo-width': displayStore.labelHaloWidth
148
+ }
149
+ });
150
+ }
151
+ }
152
+ </script>
@@ -0,0 +1,11 @@
1
+ import type { SiteDataStore } from '../stores/site.data.svelte';
2
+ import type { SiteDisplayStore } from '../stores/site.display.svelte';
3
+ import type { SiteRegistry } from '../stores/site.registry.svelte';
4
+ interface Props {
5
+ dataStore: SiteDataStore;
6
+ displayStore: SiteDisplayStore;
7
+ registry: SiteRegistry;
8
+ }
9
+ declare const SiteLabelsLayer: import("svelte").Component<Props, {}, "">;
10
+ type SiteLabelsLayer = ReturnType<typeof SiteLabelsLayer>;
11
+ export default SiteLabelsLayer;