@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,148 @@
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 { RepeaterDataStore } from '../stores/repeater.data.svelte';
7
+ import type { RepeaterRegistry } from '../stores/repeater.registry.svelte';
8
+ import type { RepeaterDisplayStore } from '../stores/repeater.display.svelte';
9
+ import type { RepeaterGroupingField } from '../types';
10
+ import { buildRepeaterTree } from '../logic/tree-adapter';
11
+
12
+ interface Props {
13
+ dataStore: RepeaterDataStore;
14
+ registry: RepeaterRegistry;
15
+ displayStore: RepeaterDisplayStore;
16
+ position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
17
+ }
18
+
19
+ let { dataStore, registry, displayStore, position = 'top-left' }: Props = $props();
20
+
21
+ const level1Options: RepeaterGroupingField[] = ['tech', 'provider', 'featureGroup'];
22
+ const level2Options: RepeaterGroupingField[] = ['fband', 'provider', 'featureGroup', 'none'];
23
+
24
+ let treeStore = $derived.by(() => {
25
+ const _repeaters = dataStore.filteredRepeaters;
26
+ const _l1 = displayStore.level1;
27
+ const _l2 = displayStore.level2;
28
+
29
+ return untrack(() => {
30
+ const nodes = buildRepeaterTree(_repeaters, registry, _l1, _l2);
31
+ return createTreeStore({
32
+ nodes,
33
+ namespace: `${registry.namespace}:repeater-tree:${_l1}:${_l2}`,
34
+ persistState: true,
35
+ defaultExpandAll: true
36
+ });
37
+ });
38
+ });
39
+
40
+ $effect(() => {
41
+ const unsubscribe = treeStore.subscribe((val) => {
42
+ let changes = 0;
43
+ val.state.nodes.forEach((nodeState) => {
44
+ if (nodeState.node.children && nodeState.node.children.length > 0) return;
45
+
46
+ const groupId = nodeState.node.id;
47
+ const isVisible = val.state.checkedPaths.has(nodeState.path);
48
+
49
+ const currentStyle = registry.getStyle(groupId, '#000');
50
+ if (currentStyle.visible !== isVisible) {
51
+ registry.toggleVisibility(groupId);
52
+ changes++;
53
+ }
54
+ });
55
+ });
56
+
57
+ return () => {
58
+ unsubscribe();
59
+ };
60
+ });
61
+
62
+ function handleColorChange(groupId: string, event: Event) {
63
+ const input = event.target as HTMLInputElement;
64
+ registry.setColor(groupId, input.value);
65
+ }
66
+
67
+ function getFieldLabel(field: string): string {
68
+ const labels: Record<string, string> = {
69
+ tech: 'Technology',
70
+ fband: 'Tech + Band',
71
+ provider: 'Provider',
72
+ featureGroup: 'Feature Group',
73
+ none: 'None'
74
+ };
75
+ return labels[field] || field;
76
+ }
77
+ </script>
78
+
79
+ <MapControl {position} title="Repeaters" icon="router" controlWidth="320px">
80
+ <div class="card shadow-sm border-0">
81
+ <!-- <div class="card-header bg-white py-2 d-flex justify-content-between align-items-center">
82
+ <h6 class="mb-0 fw-bold text-primary">
83
+ <i class="bi bi-broadcast me-2"></i>Repeaters
84
+ </h6>
85
+ <span class="badge bg-light text-dark border">
86
+ {dataStore.filteredRepeaters.length}
87
+ </span>
88
+ </div> -->
89
+
90
+ <div class="card-body p-2 bg-light border-bottom">
91
+ <div class="row g-2">
92
+ <div class="col-6">
93
+ <label class="form-label x-small text-muted text-uppercase mb-1">Group By</label>
94
+ <select class="form-select form-select-sm" bind:value={displayStore.level1}>
95
+ {#each level1Options as opt}
96
+ <option value={opt}>{getFieldLabel(opt)}</option>
97
+ {/each}
98
+ </select>
99
+ </div>
100
+ <div class="col-6">
101
+ <label class="form-label x-small text-muted text-uppercase mb-1">Then By</label>
102
+ <select class="form-select form-select-sm" bind:value={displayStore.level2}>
103
+ {#each level2Options as opt}
104
+ <option value={opt}>{getFieldLabel(opt)}</option>
105
+ {/each}
106
+ </select>
107
+ </div>
108
+ </div>
109
+ </div>
110
+
111
+ <div class="card-body p-0 overflow-auto" style="max-height: 400px;">
112
+ <div class="p-2">
113
+ <TreeView showControls={false} store={$treeStore}>
114
+ {#snippet children({ node })}
115
+ <div class="d-flex align-items-center justify-content-between w-100">
116
+
117
+
118
+ {#if !node.children || node.children.length === 0}
119
+ <div
120
+ class="d-flex align-items-center"
121
+ role="group"
122
+ onclick={(e) => e.stopPropagation()}
123
+ onkeydown={(e) => e.stopPropagation()}
124
+ >
125
+ <input
126
+ type="color"
127
+ class="form-control form-control-color form-control-sm border-0 p-0"
128
+ style="width: 16px; height: 16px; min-height: 0;"
129
+ value={node.metadata?.color}
130
+ oninput={(e) => handleColorChange(node.id, e)}
131
+ title="Change color"
132
+ />
133
+ </div>
134
+ {/if}
135
+ </div>
136
+ {/snippet}
137
+ </TreeView>
138
+ </div>
139
+ </div>
140
+ </div>
141
+ </MapControl>
142
+
143
+ <style>
144
+ .x-small {
145
+ font-size: 0.7rem;
146
+ letter-spacing: 0.5px;
147
+ }
148
+ </style>
@@ -0,0 +1,12 @@
1
+ import type { RepeaterDataStore } from '../stores/repeater.data.svelte';
2
+ import type { RepeaterRegistry } from '../stores/repeater.registry.svelte';
3
+ import type { RepeaterDisplayStore } from '../stores/repeater.display.svelte';
4
+ interface Props {
5
+ dataStore: RepeaterDataStore;
6
+ registry: RepeaterRegistry;
7
+ displayStore: RepeaterDisplayStore;
8
+ position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
9
+ }
10
+ declare const RepeaterFilterControl: import("svelte").Component<Props, {}, "">;
11
+ type RepeaterFilterControl = ReturnType<typeof RepeaterFilterControl>;
12
+ export default RepeaterFilterControl;
@@ -0,0 +1,209 @@
1
+ <script lang="ts">
2
+ import type { RepeaterDisplayStore } from '../stores/repeater.display.svelte';
3
+
4
+ interface Props {
5
+ displayStore: RepeaterDisplayStore;
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
+ <!-- Target Pixel Size (Radius) -->
14
+ <div class="row align-items-center g-2 mb-3">
15
+ <div class="col-4 text-secondary fw-semibold small text-uppercase">Pixel Size</div>
16
+ <div class="col-3 text-end">
17
+ <span class="badge bg-white text-muted border">{displayStore.targetPixelSize}px</span>
18
+ </div>
19
+ <div class="col-5">
20
+ <input
21
+ id="repeater-radius-slider"
22
+ type="range"
23
+ class="form-range w-100"
24
+ min="10"
25
+ max="200"
26
+ step="5"
27
+ bind:value={displayStore.targetPixelSize}
28
+ />
29
+ </div>
30
+ </div>
31
+
32
+ <!-- Line Width -->
33
+ <div class="row align-items-center g-2 mb-3">
34
+ <div class="col-4 text-secondary fw-semibold small text-uppercase">Line Width</div>
35
+ <div class="col-3 text-end">
36
+ <span class="badge bg-white text-muted border">{displayStore.lineWidth}px</span>
37
+ </div>
38
+ <div class="col-5">
39
+ <input
40
+ id="repeater-line-width-slider"
41
+ type="range"
42
+ class="form-range w-100"
43
+ min="0.5"
44
+ max="5"
45
+ step="0.5"
46
+ bind:value={displayStore.lineWidth}
47
+ />
48
+ </div>
49
+ </div>
50
+
51
+ <!-- Fill Opacity -->
52
+ <div class="row align-items-center g-2 mb-3">
53
+ <div class="col-4 text-secondary fw-semibold small text-uppercase">Fill Opacity</div>
54
+ <div class="col-3 text-end">
55
+ <span class="badge bg-white text-muted border">{Math.round(displayStore.fillOpacity * 100)}%</span>
56
+ </div>
57
+ <div class="col-5">
58
+ <input
59
+ id="repeater-opacity-slider"
60
+ type="range"
61
+ class="form-range w-100"
62
+ min="0"
63
+ max="1"
64
+ step="0.1"
65
+ bind:value={displayStore.fillOpacity}
66
+ />
67
+ </div>
68
+ </div>
69
+
70
+ <div class="border-top my-3"></div>
71
+
72
+ <!-- Show Labels -->
73
+ <div class="row align-items-center g-2 mb-3">
74
+ <div class="col-4 text-secondary fw-semibold small text-uppercase">Show Labels</div>
75
+ <div class="col-3"></div>
76
+ <div class="col-5">
77
+ <div class="form-check form-switch m-0 d-flex align-items-center justify-content-end">
78
+ <input
79
+ id="repeater-labels-toggle"
80
+ type="checkbox"
81
+ class="form-check-input"
82
+ role="switch"
83
+ bind:checked={displayStore.showLabels}
84
+ />
85
+ </div>
86
+ </div>
87
+ </div>
88
+
89
+ {#if displayStore.showLabels}
90
+ <div class="ps-3 border-start border-2 mb-3">
91
+ <!-- Labels Config -->
92
+ <div class="mb-2">
93
+ <div class="text-secondary fw-semibold small text-uppercase mb-1">Label Fields</div>
94
+ <div class="row g-1">
95
+ <div class="col-6">
96
+ <select class="form-select form-select-sm" bind:value={displayStore.labels.primary}>
97
+ <option value="repeaterId">ID</option>
98
+ <option value="tech">Tech</option>
99
+ <option value="fband">Band</option>
100
+ <option value="provider">Provider</option>
101
+ </select>
102
+ </div>
103
+ <div class="col-6">
104
+ <select class="form-select form-select-sm" bind:value={displayStore.labels.secondary}>
105
+ <option value="none">None</option>
106
+ <option value="repeaterId">ID</option>
107
+ <option value="tech">Tech</option>
108
+ <option value="fband">Band</option>
109
+ <option value="provider">Provider</option>
110
+ </select>
111
+ </div>
112
+ </div>
113
+ </div>
114
+
115
+ <!-- Label Settings -->
116
+ <div class="mt-3">
117
+ <!-- Label Distance -->
118
+ <div class="row align-items-center g-2 mb-2">
119
+ <div class="col-4 text-secondary small">Distance</div>
120
+ <div class="col-3 text-end">
121
+ <span class="badge bg-white text-muted border">{displayStore.labelPixelDistance}px</span>
122
+ </div>
123
+ <div class="col-5">
124
+ <input
125
+ type="range"
126
+ class="form-range w-100"
127
+ min="20"
128
+ max="150"
129
+ step="5"
130
+ bind:value={displayStore.labelPixelDistance}
131
+ />
132
+ </div>
133
+ </div>
134
+
135
+ <!-- Font Size -->
136
+ <div class="row align-items-center g-2 mb-3">
137
+ <div class="col-4 text-secondary small">Font Size</div>
138
+ <div class="col-3 text-end">
139
+ <span class="badge bg-white text-muted border">{displayStore.labelFontSize}px</span>
140
+ </div>
141
+ <div class="col-5">
142
+ <input
143
+ type="range"
144
+ class="form-range w-100"
145
+ min="8"
146
+ max="24"
147
+ step="1"
148
+ bind:value={displayStore.labelFontSize}
149
+ />
150
+ </div>
151
+ </div>
152
+
153
+ <!-- Label Colors -->
154
+ <div class="row align-items-center g-2 mb-2">
155
+ <div class="col-4 text-secondary small">Color</div>
156
+ <div class="col-8">
157
+ <div class="input-group input-group-sm">
158
+ <input
159
+ type="color"
160
+ class="form-control form-control-color"
161
+ bind:value={displayStore.labelColor}
162
+ title="Label Color"
163
+ />
164
+ <span class="input-group-text bg-white text-muted small flex-grow-1 font-monospace">
165
+ {displayStore.labelColor}
166
+ </span>
167
+ </div>
168
+ </div>
169
+ </div>
170
+
171
+ <!-- Halo Settings -->
172
+ <div class="row align-items-center g-2 mb-2">
173
+ <div class="col-4 text-secondary small">Halo</div>
174
+ <div class="col-8">
175
+ <div class="input-group input-group-sm">
176
+ <input
177
+ type="color"
178
+ class="form-control form-control-color"
179
+ bind:value={displayStore.labelHaloColor}
180
+ title="Halo Color"
181
+ />
182
+ <span class="input-group-text bg-white text-muted small flex-grow-1 font-monospace">
183
+ {displayStore.labelHaloColor}
184
+ </span>
185
+ </div>
186
+ </div>
187
+ </div>
188
+
189
+ <div class="row align-items-center g-2 mb-3">
190
+ <div class="col-4 text-secondary small">Halo Width</div>
191
+ <div class="col-3 text-end">
192
+ <span class="badge bg-white text-muted border">{displayStore.labelHaloWidth}px</span>
193
+ </div>
194
+ <div class="col-5">
195
+ <input
196
+ type="range"
197
+ class="form-range w-100"
198
+ min="0"
199
+ max="5"
200
+ step="0.5"
201
+ bind:value={displayStore.labelHaloWidth}
202
+ />
203
+ </div>
204
+ </div>
205
+ </div>
206
+ </div>
207
+ {/if}
208
+ </div>
209
+ </div>
@@ -0,0 +1,7 @@
1
+ import type { RepeaterDisplayStore } from '../stores/repeater.display.svelte';
2
+ interface Props {
3
+ displayStore: RepeaterDisplayStore;
4
+ }
5
+ declare const RepeaterSettingsPanel: import("svelte").Component<Props, {}, "">;
6
+ type RepeaterSettingsPanel = ReturnType<typeof RepeaterSettingsPanel>;
7
+ export default RepeaterSettingsPanel;
@@ -0,0 +1,177 @@
1
+ <script lang="ts">
2
+ import { getContext } from 'svelte';
3
+ import type { MapStore } from '../../../core/stores/map.store.svelte';
4
+ import type { RepeaterDataStore } from '../stores/repeater.data.svelte';
5
+ import type { RepeaterDisplayStore } from '../stores/repeater.display.svelte';
6
+ import type { RepeaterRegistry } from '../stores/repeater.registry.svelte';
7
+ import { groupRepeaters, getColorForGroup } from '../logic/grouping';
8
+ import type { Repeater } from '../types';
9
+ import type mapboxgl from 'mapbox-gl';
10
+
11
+ interface Props {
12
+ dataStore: RepeaterDataStore;
13
+ displayStore: RepeaterDisplayStore;
14
+ registry: RepeaterRegistry;
15
+ }
16
+
17
+ let { dataStore, displayStore, registry }: Props = $props();
18
+
19
+ const mapStore = getContext<MapStore>('MAP_CONTEXT');
20
+ let sourceId = 'repeater-labels-source';
21
+ let layerId = 'repeater-labels-layer';
22
+ let updateTimeout: any;
23
+
24
+ // Helper to get label text
25
+ function getLabelText(repeater: Repeater, config: { primary: string, secondary: string }): string {
26
+ const pVal = repeater[config.primary as keyof Repeater];
27
+ const sVal = config.secondary !== 'none' ? repeater[config.secondary as keyof Repeater] : null;
28
+
29
+ let text = pVal != null ? String(pVal) : '';
30
+ if (sVal != null) {
31
+ text += ` | ${sVal}`;
32
+ }
33
+ return text;
34
+ }
35
+
36
+ // React to changes
37
+ $effect(() => {
38
+ // Read dependencies
39
+ const _repeaters = dataStore.filteredRepeaters;
40
+ const _show = displayStore.showLabels;
41
+ const _dist = displayStore.labelPixelDistance;
42
+ const _size = displayStore.labelFontSize;
43
+ const _tol = displayStore.labelAzimuthTolerance;
44
+ // Track specific label properties for reactivity
45
+ const _lPrim = displayStore.labels.primary;
46
+ const _lSec = displayStore.labels.secondary;
47
+ const _color = displayStore.labelColor;
48
+ const _haloColor = displayStore.labelHaloColor;
49
+ const _haloWidth = displayStore.labelHaloWidth;
50
+ const _registryVersion = registry.version;
51
+
52
+ const map = mapStore.map;
53
+ if (map && map.getLayer(layerId)) {
54
+ map.setPaintProperty(layerId, 'text-color', displayStore.labelColor);
55
+ map.setPaintProperty(layerId, 'text-halo-color', displayStore.labelHaloColor);
56
+ map.setPaintProperty(layerId, 'text-halo-width', displayStore.labelHaloWidth);
57
+ }
58
+
59
+ updateLayer();
60
+ });
61
+
62
+ function updateLayer() {
63
+ const map = mapStore.map;
64
+ if (!map) return;
65
+
66
+ clearTimeout(updateTimeout);
67
+ updateTimeout = setTimeout(() => {
68
+ renderLabels(map);
69
+ }, 150);
70
+ }
71
+
72
+ function renderLabels(map: mapboxgl.Map) {
73
+ if (!displayStore.showLabels) {
74
+ if (map.getLayer(layerId)) map.setLayoutProperty(layerId, 'visibility', 'none');
75
+ return;
76
+ }
77
+
78
+ if (map.getLayer(layerId)) map.setLayoutProperty(layerId, 'visibility', 'visible');
79
+
80
+ // Update text size if changed
81
+ if (map.getLayer(layerId)) {
82
+ map.setLayoutProperty(layerId, 'text-size', displayStore.labelFontSize);
83
+ }
84
+
85
+ const bounds = map.getBounds();
86
+ if (!bounds) return;
87
+
88
+ // 1. Filter visible repeaters (Bounds + Registry Visibility)
89
+ const visibleRepeaters: Repeater[] = [];
90
+
91
+ // Group repeaters using the same logic as RepeatersLayer to determine visibility
92
+ const repeaterGroups = groupRepeaters(dataStore.filteredRepeaters, displayStore.level1, displayStore.level2);
93
+ let groupIndex = 0;
94
+
95
+ for (const [groupId, repeaters] of repeaterGroups) {
96
+ const defaultColor = getColorForGroup(groupIndex++);
97
+ const style = registry.getStyle(groupId, defaultColor);
98
+
99
+ if (style.visible) {
100
+ // Check bounds
101
+ for (const r of repeaters) {
102
+ if (bounds.contains([r.longitude, r.latitude])) {
103
+ visibleRepeaters.push(r);
104
+ }
105
+ }
106
+ }
107
+ }
108
+
109
+ // 2. Generate Label Features
110
+ const features: GeoJSON.Feature[] = [];
111
+
112
+ // Convert pixel distance to ems (approx 1em = 16px or labelFontSize)
113
+ const distEm = displayStore.labelPixelDistance / displayStore.labelFontSize;
114
+
115
+ for (const r of visibleRepeaters) {
116
+ const labelText = getLabelText(r, displayStore.labels);
117
+ if (!labelText) continue;
118
+
119
+ // Calculate offset based on azimuth
120
+ // Mapbox text-offset: [x, y]
121
+ // x = sin(az) * dist
122
+ // y = -cos(az) * dist (negative because y is down)
123
+ const az = r.azimuth ?? 0;
124
+ const azRad = (az * Math.PI) / 180;
125
+ const offsetX = Math.sin(azRad) * distEm;
126
+ const offsetY = -Math.cos(azRad) * distEm;
127
+
128
+ features.push({
129
+ type: 'Feature',
130
+ geometry: {
131
+ type: 'Point',
132
+ coordinates: [r.longitude, r.latitude]
133
+ },
134
+ properties: {
135
+ label: labelText,
136
+ offset: [offsetX, offsetY]
137
+ }
138
+ });
139
+ }
140
+
141
+ const geojson: GeoJSON.FeatureCollection = {
142
+ type: 'FeatureCollection',
143
+ features
144
+ };
145
+
146
+ const source = map.getSource(sourceId) as mapboxgl.GeoJSONSource;
147
+ if (source) {
148
+ source.setData(geojson);
149
+ } else {
150
+ map.addSource(sourceId, {
151
+ type: 'geojson',
152
+ data: geojson
153
+ });
154
+
155
+ map.addLayer({
156
+ id: layerId,
157
+ type: 'symbol',
158
+ source: sourceId,
159
+ layout: {
160
+ 'text-field': ['get', 'label'],
161
+ 'text-font': ['Open Sans Semibold', 'Arial Unicode MS Bold'],
162
+ 'text-size': displayStore.labelFontSize,
163
+ 'text-offset': ['get', 'offset'],
164
+ 'text-anchor': 'center',
165
+ 'text-justify': 'auto',
166
+ 'text-allow-overlap': false,
167
+ 'text-ignore-placement': false
168
+ },
169
+ paint: {
170
+ 'text-color': displayStore.labelColor,
171
+ 'text-halo-color': displayStore.labelHaloColor,
172
+ 'text-halo-width': displayStore.labelHaloWidth
173
+ }
174
+ });
175
+ }
176
+ }
177
+ </script>
@@ -0,0 +1,11 @@
1
+ import type { RepeaterDataStore } from '../stores/repeater.data.svelte';
2
+ import type { RepeaterDisplayStore } from '../stores/repeater.display.svelte';
3
+ import type { RepeaterRegistry } from '../stores/repeater.registry.svelte';
4
+ interface Props {
5
+ dataStore: RepeaterDataStore;
6
+ displayStore: RepeaterDisplayStore;
7
+ registry: RepeaterRegistry;
8
+ }
9
+ declare const RepeaterLabelsLayer: import("svelte").Component<Props, {}, "">;
10
+ type RepeaterLabelsLayer = ReturnType<typeof RepeaterLabelsLayer>;
11
+ export default RepeaterLabelsLayer;