@smartnet360/svelte-components 0.0.85 → 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 (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 +3 -0
  30. package/dist/map-v3/core/index.js +3 -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 +26 -0
  110. package/dist/map-v3/index.js +31 -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,271 @@
1
+ <script lang="ts">
2
+ /**
3
+ * MapStyleControl - Control for switching map styles
4
+ *
5
+ * Self-contained control that displays available map styles and allows switching.
6
+ * Persists selection to localStorage.
7
+ */
8
+ import { getContext } from 'svelte';
9
+ import { MapControl } from '../../shared';
10
+ import type { MapStore } from '../stores/map.store.svelte';
11
+
12
+ interface MapStyle {
13
+ id: string;
14
+ name: string;
15
+ url: string;
16
+ description?: string;
17
+ icon?: string;
18
+ }
19
+
20
+ const DEFAULT_STYLES: MapStyle[] = [
21
+ {
22
+ id: 'streets',
23
+ name: 'Streets',
24
+ url: 'mapbox://styles/mapbox/streets-v12',
25
+ description: 'General purpose street map',
26
+ icon: 'map'
27
+ },
28
+ {
29
+ id: 'outdoors',
30
+ name: 'Outdoors',
31
+ url: 'mapbox://styles/mapbox/outdoors-v12',
32
+ description: 'Hiking, biking, and terrain',
33
+ icon: 'tree'
34
+ },
35
+ {
36
+ id: 'light',
37
+ name: 'Light',
38
+ url: 'mapbox://styles/mapbox/light-v11',
39
+ description: 'Minimal light background',
40
+ icon: 'sun'
41
+ },
42
+ {
43
+ id: 'dark',
44
+ name: 'Dark',
45
+ url: 'mapbox://styles/mapbox/dark-v11',
46
+ description: 'Minimal dark background',
47
+ icon: 'moon-stars'
48
+ },
49
+ {
50
+ id: 'satellite',
51
+ name: 'Satellite',
52
+ url: 'mapbox://styles/mapbox/satellite-v9',
53
+ description: 'Satellite imagery',
54
+ icon: 'globe'
55
+ },
56
+ {
57
+ id: 'satellite-streets',
58
+ name: 'Satellite Streets',
59
+ url: 'mapbox://styles/mapbox/satellite-streets-v12',
60
+ description: 'Satellite with street overlay',
61
+ icon: 'globe-americas'
62
+ }
63
+ ];
64
+
65
+ interface Props {
66
+ /** Control position on map */
67
+ position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
68
+ /** Control title */
69
+ title?: string;
70
+ /** Optional header icon */
71
+ icon?: string;
72
+ /** Show icon when collapsed (default: true) */
73
+ iconOnlyWhenCollapsed?: boolean;
74
+ /** Initially collapsed? */
75
+ initiallyCollapsed?: boolean;
76
+ /** Storage namespace for persistence */
77
+ namespace?: string;
78
+ /** Custom available styles (default: built-in Mapbox styles) */
79
+ availableStyles?: MapStyle[];
80
+ /** Default style ID */
81
+ defaultStyleId?: string;
82
+ }
83
+
84
+ let {
85
+ position = 'top-right',
86
+ title = 'Map Style',
87
+ icon = 'layers',
88
+ iconOnlyWhenCollapsed = true,
89
+ initiallyCollapsed = true,
90
+ namespace = 'map',
91
+ availableStyles = DEFAULT_STYLES,
92
+ defaultStyleId = 'streets'
93
+ }: Props = $props();
94
+
95
+ const mapStore = getContext<MapStore>('MAP_CONTEXT');
96
+ const storageKey = `${namespace}:mapStyle`;
97
+
98
+ // Load persisted style or use default
99
+ const loadPersistedStyle = (): MapStyle => {
100
+ if (typeof window === 'undefined') {
101
+ return availableStyles.find(s => s.id === defaultStyleId) || availableStyles[0];
102
+ }
103
+
104
+ try {
105
+ const stored = localStorage.getItem(storageKey);
106
+ if (stored) {
107
+ const styleId = JSON.parse(stored);
108
+ const style = availableStyles.find(s => s.id === styleId);
109
+ if (style) return style;
110
+ }
111
+ } catch (e) {
112
+ console.warn('Failed to load persisted map style:', e);
113
+ }
114
+
115
+ return availableStyles.find(s => s.id === defaultStyleId) || availableStyles[0];
116
+ };
117
+
118
+ let currentStyle = $state<MapStyle>(loadPersistedStyle());
119
+
120
+ // Apply style when map becomes available
121
+ $effect(() => {
122
+ const map = mapStore.map;
123
+ if (map && currentStyle) {
124
+ const mapStyle = map.getStyle();
125
+ // Compare the style URL/source, not the name
126
+ // mapStyle.sprite contains the base URL we can check against
127
+ const currentMapStyleUrl = mapStyle?.sprite || '';
128
+ // Simple check: if the current style ID isn't in the sprite URL (approximate but usually works for Mapbox styles)
129
+ // Better check: store the last set style URL in a ref if needed, but this logic from V2 is okay for now.
130
+ // Actually, let's just set it if it's the first load or if we are sure.
131
+ // To avoid infinite loops or unnecessary sets, we can check if the map is loaded.
132
+
133
+ // For now, let's trust the user interaction to drive changes,
134
+ // but on initial load, we want to enforce the persisted style.
135
+ // We can't easily read the "URL" back from map.getStyle().
136
+
137
+ // Strategy: We just set it once when the map is first loaded if it differs from default?
138
+ // Or we can just set it. map.setStyle is relatively cheap if it's the same.
139
+ // However, mapbox might reload tiles.
140
+
141
+ // Let's stick to the V2 logic: check if sprite includes the ID.
142
+ const needsStyleChange = !currentMapStyleUrl.includes(currentStyle.id);
143
+
144
+ if (needsStyleChange) {
145
+ // console.log('Applying persisted style:', currentStyle.id);
146
+ map.setStyle(currentStyle.url);
147
+ }
148
+ }
149
+ });
150
+
151
+ function handleStyleChange(styleId: string): void {
152
+ const style = availableStyles.find(s => s.id === styleId);
153
+ if (!style) {
154
+ console.warn(`Style with id "${styleId}" not found`);
155
+ return;
156
+ }
157
+
158
+ // Update local state
159
+ currentStyle = style;
160
+
161
+ // Persist to localStorage
162
+ if (typeof window !== 'undefined') {
163
+ try {
164
+ localStorage.setItem(storageKey, JSON.stringify(styleId));
165
+ } catch (e) {
166
+ console.warn('Failed to persist map style:', e);
167
+ }
168
+ }
169
+
170
+ // Update map directly
171
+ if (mapStore.map) {
172
+ mapStore.map.setStyle(style.url);
173
+ }
174
+ }
175
+ </script>
176
+
177
+ <MapControl {position} {title} {icon} {iconOnlyWhenCollapsed} collapsible={true} {initiallyCollapsed}>
178
+ <div class="map-style-controls">
179
+ <div class="style-grid">
180
+ {#each availableStyles as style (style.id)}
181
+ <button
182
+ type="button"
183
+ class="btn style-option"
184
+ class:btn-primary={currentStyle.id === style.id}
185
+ class:btn-outline-secondary={currentStyle.id !== style.id}
186
+ class:active={currentStyle.id === style.id}
187
+ onclick={() => handleStyleChange(style.id)}
188
+ title={style.description}
189
+ >
190
+ <i class="bi bi-{style.icon || 'map'} style-icon"></i>
191
+ <span class="style-name">{style.name}</span>
192
+ </button>
193
+ {/each}
194
+ </div>
195
+ </div>
196
+ </MapControl>
197
+
198
+ <style>
199
+ .map-style-controls {
200
+ min-width: 240px;
201
+ }
202
+
203
+ .style-grid {
204
+ display: grid;
205
+ grid-template-columns: repeat(2, minmax(0, 1fr));
206
+ gap: 0.5rem;
207
+ margin-bottom: 0.75rem;
208
+ }
209
+
210
+ .style-option {
211
+ display: inline-flex;
212
+ flex-direction: column;
213
+ align-items: center;
214
+ justify-content: center;
215
+ gap: 0.4rem;
216
+ text-align: center;
217
+ font-weight: 500;
218
+ border: var(--bs-btn-border-width, 1px) solid var(
219
+ --bs-btn-border-color,
220
+ var(--bs-border-color, #dee2e6)
221
+ );
222
+ /* Normalise Bootstrap spacing so icons remain centred */
223
+ --bs-btn-padding-y: 0.75rem;
224
+ --bs-btn-padding-x: 0.75rem;
225
+ --bs-btn-line-height: 1.25;
226
+ width: 100%;
227
+ height: auto;
228
+ }
229
+
230
+ .style-option .style-icon {
231
+ display: block;
232
+ font-size: 1.2rem;
233
+ line-height: 1;
234
+ margin-top: 0.15rem;
235
+ }
236
+
237
+ .style-option .style-name {
238
+ display: block;
239
+ font-size: 0.5rem;
240
+ text-transform: uppercase;
241
+ letter-spacing: 0.04em;
242
+ }
243
+
244
+ /* Ensure Bootstrap button variables win inside Mapbox control */
245
+ .map-style-controls .btn {
246
+ background-color: var(--bs-btn-bg, transparent);
247
+ border-color: var(--bs-btn-border-color, currentColor);
248
+ color: var(--bs-btn-color, inherit);
249
+ transition: var(--bs-btn-transition, all 0.15s ease);
250
+ border-radius: var(--bs-btn-border-radius, var(--bs-border-radius, 0.375rem));
251
+ }
252
+
253
+ .map-style-controls .btn:hover,
254
+ .map-style-controls .btn:focus {
255
+ background-color: var(--bs-btn-hover-bg, var(--bs-btn-bg, transparent));
256
+ border-color: var(--bs-btn-hover-border-color, var(--bs-btn-border-color, currentColor));
257
+ color: var(--bs-btn-hover-color, var(--bs-btn-color, inherit));
258
+ }
259
+
260
+ .map-style-controls .btn:disabled,
261
+ .map-style-controls .btn:disabled:hover {
262
+ background-color: var(--bs-btn-disabled-bg, var(--bs-btn-bg, transparent));
263
+ border-color: var(--bs-btn-disabled-border-color, var(--bs-btn-border-color, currentColor));
264
+ color: var(--bs-btn-disabled-color, var(--bs-btn-color, inherit));
265
+ opacity: var(--bs-btn-disabled-opacity, 0.65);
266
+ }
267
+
268
+ .map-style-controls .btn-primary {
269
+ box-shadow: 0 4px 12px rgba(var(--bs-primary-rgb, 13, 110, 253), 0.18);
270
+ }
271
+ </style>
@@ -0,0 +1,28 @@
1
+ interface MapStyle {
2
+ id: string;
3
+ name: string;
4
+ url: string;
5
+ description?: string;
6
+ icon?: string;
7
+ }
8
+ interface Props {
9
+ /** Control position on map */
10
+ position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
11
+ /** Control title */
12
+ title?: string;
13
+ /** Optional header icon */
14
+ icon?: string;
15
+ /** Show icon when collapsed (default: true) */
16
+ iconOnlyWhenCollapsed?: boolean;
17
+ /** Initially collapsed? */
18
+ initiallyCollapsed?: boolean;
19
+ /** Storage namespace for persistence */
20
+ namespace?: string;
21
+ /** Custom available styles (default: built-in Mapbox styles) */
22
+ availableStyles?: MapStyle[];
23
+ /** Default style ID */
24
+ defaultStyleId?: string;
25
+ }
26
+ declare const MapStyleControl: import("svelte").Component<Props, {}, "">;
27
+ type MapStyleControl = ReturnType<typeof MapStyleControl>;
28
+ export default MapStyleControl;
@@ -0,0 +1,3 @@
1
+ export { MapStore, createMapStore } from './stores/map.store.svelte';
2
+ export { default as Map } from './components/Map.svelte';
3
+ export { default as MapStyleControl } from './controls/MapStyleControl.svelte';
@@ -0,0 +1,3 @@
1
+ export { MapStore, createMapStore } from './stores/map.store.svelte';
2
+ export { default as Map } from './components/Map.svelte';
3
+ export { default as MapStyleControl } from './controls/MapStyleControl.svelte';
@@ -0,0 +1,8 @@
1
+ export declare class MapStore {
2
+ map: import("mapbox-gl").Map | null;
3
+ loaded: boolean;
4
+ constructor();
5
+ init(container: HTMLElement, options: Omit<mapboxgl.MapboxOptions, 'container'>): void;
6
+ destroy(): void;
7
+ }
8
+ export declare function createMapStore(): MapStore;
@@ -0,0 +1,29 @@
1
+ import mapboxgl from 'mapbox-gl';
2
+ export class MapStore {
3
+ map = $state(null);
4
+ loaded = $state(false);
5
+ constructor() {
6
+ // No-op
7
+ }
8
+ init(container, options) {
9
+ if (this.map)
10
+ return;
11
+ this.map = new mapboxgl.Map({
12
+ container,
13
+ ...options
14
+ });
15
+ this.map.on('load', () => {
16
+ this.loaded = true;
17
+ });
18
+ }
19
+ destroy() {
20
+ if (this.map) {
21
+ this.map.remove();
22
+ this.map = null;
23
+ this.loaded = false;
24
+ }
25
+ }
26
+ }
27
+ export function createMapStore() {
28
+ return new MapStore();
29
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Viewport Store - Manages and persists map viewport state (center, zoom, pitch, bearing)
3
+ *
4
+ * Features:
5
+ * - Reactive state using Svelte 5 runes
6
+ * - Auto-persists to localStorage via $effect
7
+ * - Namespace isolation for multiple map instances
8
+ */
9
+ export interface ViewportState {
10
+ /** Map center [lng, lat] */
11
+ center: [number, number];
12
+ /** Zoom level */
13
+ zoom: number;
14
+ /** Map bearing (rotation) in degrees */
15
+ bearing: number;
16
+ /** Map pitch (tilt) in degrees */
17
+ pitch: number;
18
+ }
19
+ export interface ViewportStore {
20
+ readonly center: [number, number];
21
+ readonly zoom: number;
22
+ readonly bearing: number;
23
+ readonly pitch: number;
24
+ setCenter(center: [number, number]): void;
25
+ setZoom(zoom: number): void;
26
+ setBearing(bearing: number): void;
27
+ setPitch(pitch: number): void;
28
+ updateViewport(state: Partial<ViewportState>): void;
29
+ reset(): void;
30
+ }
31
+ /**
32
+ * Create a viewport store with persistence
33
+ *
34
+ * @param namespace - Unique identifier for localStorage key
35
+ * @param defaults - Default viewport values (used if no saved state exists)
36
+ * @returns ViewportStore instance
37
+ */
38
+ export declare function createViewportStore(namespace: string, defaults?: Partial<ViewportState>): ViewportStore;
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Viewport Store - Manages and persists map viewport state (center, zoom, pitch, bearing)
3
+ *
4
+ * Features:
5
+ * - Reactive state using Svelte 5 runes
6
+ * - Auto-persists to localStorage via $effect
7
+ * - Namespace isolation for multiple map instances
8
+ */
9
+ const DEFAULT_STATE = {
10
+ center: [0, 0],
11
+ zoom: 2,
12
+ bearing: 0,
13
+ pitch: 0
14
+ };
15
+ /**
16
+ * Load viewport state from localStorage
17
+ */
18
+ function loadViewportState(namespace, defaults) {
19
+ if (typeof window === 'undefined') {
20
+ return { ...DEFAULT_STATE, ...defaults };
21
+ }
22
+ try {
23
+ const key = `${namespace}:viewport`;
24
+ const stored = localStorage.getItem(key);
25
+ if (stored) {
26
+ const parsed = JSON.parse(stored);
27
+ return {
28
+ center: parsed.center || defaults.center || DEFAULT_STATE.center,
29
+ zoom: parsed.zoom ?? defaults.zoom ?? DEFAULT_STATE.zoom,
30
+ bearing: parsed.bearing ?? defaults.bearing ?? DEFAULT_STATE.bearing,
31
+ pitch: parsed.pitch ?? defaults.pitch ?? DEFAULT_STATE.pitch
32
+ };
33
+ }
34
+ }
35
+ catch (error) {
36
+ console.warn('Failed to load viewport state:', error);
37
+ }
38
+ return { ...DEFAULT_STATE, ...defaults };
39
+ }
40
+ /**
41
+ * Save viewport state to localStorage
42
+ */
43
+ function saveViewportState(namespace, state) {
44
+ if (typeof window === 'undefined')
45
+ return;
46
+ try {
47
+ const key = `${namespace}:viewport`;
48
+ localStorage.setItem(key, JSON.stringify(state));
49
+ }
50
+ catch (error) {
51
+ console.warn('Failed to save viewport state:', error);
52
+ }
53
+ }
54
+ /**
55
+ * Create a viewport store with persistence
56
+ *
57
+ * @param namespace - Unique identifier for localStorage key
58
+ * @param defaults - Default viewport values (used if no saved state exists)
59
+ * @returns ViewportStore instance
60
+ */
61
+ export function createViewportStore(namespace, defaults = {}) {
62
+ // Load initial state
63
+ let state = $state(loadViewportState(namespace, defaults));
64
+ // Auto-save to localStorage on any change
65
+ $effect(() => {
66
+ saveViewportState(namespace, state);
67
+ });
68
+ return {
69
+ // Getters
70
+ get center() { return state.center; },
71
+ get zoom() { return state.zoom; },
72
+ get bearing() { return state.bearing; },
73
+ get pitch() { return state.pitch; },
74
+ // Setters
75
+ setCenter(center) {
76
+ state.center = center;
77
+ },
78
+ setZoom(zoom) {
79
+ state.zoom = zoom;
80
+ },
81
+ setBearing(bearing) {
82
+ state.bearing = bearing;
83
+ },
84
+ setPitch(pitch) {
85
+ state.pitch = pitch;
86
+ },
87
+ // Bulk update (more efficient than individual setters)
88
+ updateViewport(updates) {
89
+ if (updates.center !== undefined)
90
+ state.center = updates.center;
91
+ if (updates.zoom !== undefined)
92
+ state.zoom = updates.zoom;
93
+ if (updates.bearing !== undefined)
94
+ state.bearing = updates.bearing;
95
+ if (updates.pitch !== undefined)
96
+ state.pitch = updates.pitch;
97
+ },
98
+ // Reset to defaults
99
+ reset() {
100
+ const resetState = { ...DEFAULT_STATE, ...defaults };
101
+ state.center = resetState.center;
102
+ state.zoom = resetState.zoom;
103
+ state.bearing = resetState.bearing;
104
+ state.pitch = resetState.pitch;
105
+ }
106
+ };
107
+ }
@@ -0,0 +1,104 @@
1
+ <script lang="ts">
2
+ import { onMount } from 'svelte';
3
+ import { Map, MapStyleControl } from '../core';
4
+ import { CellsLayer, CellLabelsLayer } from '../features/cells/layers';
5
+ import { createCellDataStore } from '../features/cells/stores/cell.data.svelte';
6
+ import { createCellRegistry } from '../features/cells/stores/cell.registry.svelte';
7
+ import { CellDisplayStore } from '../features/cells/stores/cell.display.svelte';
8
+ import FeatureSettingsControl from '../core/controls/FeatureSettingsControl.svelte';
9
+ import CellFilterControl from '../features/cells/components/CellFilterControl.svelte';
10
+ import { createRepeaterDataStore } from '../features/repeaters/stores/repeater.data.svelte';
11
+ import { createRepeaterRegistry } from '../features/repeaters/stores/repeater.registry.svelte';
12
+ import { RepeaterDisplayStore } from '../features/repeaters/stores/repeater.display.svelte';
13
+ import RepeaterFilterControl from '../features/repeaters/components/RepeaterFilterControl.svelte';
14
+ import RepeatersLayer from '../features/repeaters/layers/RepeatersLayer.svelte';
15
+ import RepeaterLabelsLayer from '../features/repeaters/layers/RepeaterLabelsLayer.svelte';
16
+ import { createSiteDataStore } from '../features/sites/stores/site.data.svelte';
17
+ import { createSiteDisplayStore } from '../features/sites/stores/site.display.svelte';
18
+ import SitesLayer from '../features/sites/layers/SitesLayer.svelte';
19
+ import SiteLabelsLayer from '../features/sites/layers/SiteLabelsLayer.svelte';
20
+ import SiteFilterControl from '../features/sites/components/SiteFilterControl.svelte';
21
+ import { createSiteRegistry } from '../features/sites/stores/site.registry.svelte';
22
+ import { demoCells } from './demo-cells';
23
+ import { demoRepeaters } from './demo-repeaters';
24
+
25
+ interface Props {
26
+ accessToken: string;
27
+ }
28
+
29
+ let { accessToken }: Props = $props();
30
+
31
+ // Initialize stores
32
+ const cellData = createCellDataStore();
33
+ const cellRegistry = createCellRegistry('demo-map');
34
+ const cellDisplay = new CellDisplayStore();
35
+
36
+ const siteData = createSiteDataStore(cellData);
37
+ const siteRegistry = createSiteRegistry('demo-map');
38
+ const siteDisplay = createSiteDisplayStore();
39
+
40
+ const repeaterData = createRepeaterDataStore();
41
+ const repeaterRegistry = createRepeaterRegistry('demo-map');
42
+ const repeaterDisplay = new RepeaterDisplayStore();
43
+
44
+ onMount(() => {
45
+ // Load dummy data
46
+ // Need to cast or map if types slightly differ, but they should match
47
+ cellData.setCells(demoCells as any);
48
+ repeaterData.setRepeaters(demoRepeaters);
49
+ });
50
+ </script>
51
+
52
+ <div class="map-wrapper">
53
+ <Map
54
+
55
+ {accessToken}
56
+ center={[19.0402, 47.4979]}
57
+ zoom={13}
58
+ namespace="demo-map"
59
+ >
60
+ <MapStyleControl position="top-right" namespace="demo-map" />
61
+
62
+ <CellFilterControl
63
+ position="top-left"
64
+ dataStore={cellData}
65
+ registry={cellRegistry}
66
+ displayStore={cellDisplay}
67
+ />
68
+
69
+ <SiteFilterControl
70
+ position="top-left"
71
+ dataStore={siteData}
72
+ registry={siteRegistry}
73
+ displayStore={siteDisplay}
74
+ />
75
+
76
+ <RepeaterFilterControl
77
+ position="top-left"
78
+ dataStore={repeaterData}
79
+ registry={repeaterRegistry}
80
+ displayStore={repeaterDisplay}
81
+ />
82
+
83
+ <FeatureSettingsControl
84
+ position="top-right"
85
+ cellDisplayStore={cellDisplay}
86
+ repeaterDisplayStore={repeaterDisplay}
87
+ siteDisplayStore={siteDisplay}
88
+ />
89
+
90
+ <SitesLayer dataStore={siteData} displayStore={siteDisplay} registry={siteRegistry} />
91
+ <SiteLabelsLayer dataStore={siteData} displayStore={siteDisplay} registry={siteRegistry} />
92
+ <CellsLayer dataStore={cellData} registry={cellRegistry} displayStore={cellDisplay} />
93
+ <CellLabelsLayer dataStore={cellData} registry={cellRegistry} displayStore={cellDisplay} />
94
+ <RepeatersLayer dataStore={repeaterData} registry={repeaterRegistry} displayStore={repeaterDisplay} />
95
+ <RepeaterLabelsLayer dataStore={repeaterData} registry={repeaterRegistry} displayStore={repeaterDisplay} />
96
+ </Map>
97
+ </div>
98
+
99
+ <style>
100
+ .map-wrapper {
101
+ width: 100%;
102
+ height: 100%;
103
+ }
104
+ </style>
@@ -0,0 +1,6 @@
1
+ interface Props {
2
+ accessToken: string;
3
+ }
4
+ declare const DemoMap: import("svelte").Component<Props, {}, "">;
5
+ type DemoMap = ReturnType<typeof DemoMap>;
6
+ export default DemoMap;
@@ -0,0 +1,13 @@
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
+ import type { Cell } from '../features/cells/types';
10
+ /**
11
+ * Generate demo cells: 100 sites × 3 sectors × 12 tech-bands = 3,600 cells
12
+ */
13
+ export declare const demoCells: Cell[];