@smartnet360/svelte-components 0.0.80 → 0.0.82
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.
- package/dist/map-v2/demo/DemoMap.svelte +3 -3
- package/dist/map-v2/features/cells/utils/cellGeoJSON.js +2 -0
- package/dist/map-v2/shared/controls/FeatureSelectionControl.svelte +80 -33
- package/dist/map-v2/shared/controls/MapControl.svelte +7 -2
- package/dist/map-v2/shared/controls/MapControl.svelte.d.ts +2 -0
- package/package.json +1 -1
|
@@ -232,11 +232,11 @@
|
|
|
232
232
|
<!-- Generic feature selection control - works with any layer -->
|
|
233
233
|
<FeatureSelectionControl
|
|
234
234
|
position="bottom-left"
|
|
235
|
-
title="
|
|
236
|
-
icon="
|
|
235
|
+
title="Cluster Tool"
|
|
236
|
+
icon="speedometer2"
|
|
237
237
|
iconOnlyWhenCollapsed={useIconHeaders}
|
|
238
238
|
onAction={handleProcessFeatures}
|
|
239
|
-
actionButtonLabel="Process
|
|
239
|
+
actionButtonLabel="Process Cluster"
|
|
240
240
|
featureIcon="pin-map-fill"
|
|
241
241
|
/>
|
|
242
242
|
|
|
@@ -51,6 +51,8 @@ export function cellsToGeoJSON(cells, currentZoom, baseRadius = 500, groupColorM
|
|
|
51
51
|
cellID: cell.cellID,
|
|
52
52
|
cellName: cell.cellName,
|
|
53
53
|
siteId: cell.siteId,
|
|
54
|
+
latitude: cell.latitude,
|
|
55
|
+
longitude: cell.longitude,
|
|
54
56
|
// Cell attributes
|
|
55
57
|
tech: cell.tech,
|
|
56
58
|
band: cell.frq,
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
import { onMount, onDestroy, getContext } from 'svelte';
|
|
16
16
|
import { get } from 'svelte/store';
|
|
17
|
+
import mapboxgl from 'mapbox-gl';
|
|
17
18
|
import MapControl from './MapControl.svelte';
|
|
18
19
|
import { createFeatureSelectionStore, type SelectedFeature } from './featureSelectionStore.svelte';
|
|
19
20
|
import { MAP_CONTEXT_KEY, type MapStore } from '../../core/types';
|
|
@@ -41,13 +42,13 @@
|
|
|
41
42
|
|
|
42
43
|
let {
|
|
43
44
|
position = 'top-left',
|
|
44
|
-
title = '
|
|
45
|
-
icon = '
|
|
45
|
+
title = 'Cluster Tool',
|
|
46
|
+
icon = 'speedometer2',
|
|
46
47
|
iconOnlyWhenCollapsed = true,
|
|
47
48
|
onAction,
|
|
48
|
-
actionButtonLabel = 'Process',
|
|
49
|
+
actionButtonLabel = 'Process Cluster',
|
|
49
50
|
featureIcon = 'geo-alt-fill',
|
|
50
|
-
idPropertyOptions = ['siteId', 'cellName','id'],
|
|
51
|
+
idPropertyOptions = ['siteId','sectorId', 'cellName','id'],
|
|
51
52
|
defaultIdProperty = 'siteId'
|
|
52
53
|
}: Props = $props();
|
|
53
54
|
|
|
@@ -66,6 +67,12 @@
|
|
|
66
67
|
let selectionCount = $derived(store.count);
|
|
67
68
|
let hasSelection = $derived(selectionCount > 0);
|
|
68
69
|
|
|
70
|
+
// Track collapsed state
|
|
71
|
+
let isCollapsed = $state(false);
|
|
72
|
+
|
|
73
|
+
// Track markers for selected features
|
|
74
|
+
let markers = new Map<string, mapboxgl.Marker>();
|
|
75
|
+
|
|
69
76
|
// Subscribe to map store and initialize when map becomes available
|
|
70
77
|
let unsubscribe: (() => void) | null = null;
|
|
71
78
|
|
|
@@ -78,6 +85,8 @@
|
|
|
78
85
|
if (map && !store['map']) {
|
|
79
86
|
console.log('[FeatureSelectionControl] Setting map on selection store');
|
|
80
87
|
store.setMap(map);
|
|
88
|
+
// Enable selection mode on mount (control starts collapsed=false by default)
|
|
89
|
+
store.enableSelectionMode();
|
|
81
90
|
}
|
|
82
91
|
});
|
|
83
92
|
});
|
|
@@ -87,12 +96,72 @@
|
|
|
87
96
|
if (unsubscribe) {
|
|
88
97
|
unsubscribe();
|
|
89
98
|
}
|
|
99
|
+
// Remove all markers
|
|
100
|
+
clearAllMarkers();
|
|
90
101
|
// Cleanup event handlers
|
|
91
102
|
store.destroy();
|
|
92
103
|
});
|
|
93
104
|
|
|
94
|
-
|
|
95
|
-
|
|
105
|
+
// Update markers when selected features change
|
|
106
|
+
$effect(() => {
|
|
107
|
+
updateMarkers(selectedFeatures);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
function updateMarkers(features: SelectedFeature[]) {
|
|
111
|
+
console.log('[FeatureSelectionControl] Updating markers for', features.length, 'features');
|
|
112
|
+
|
|
113
|
+
// Get current map
|
|
114
|
+
const map = get(mapStore);
|
|
115
|
+
if (!map) return;
|
|
116
|
+
|
|
117
|
+
// Remove markers that are no longer in the selection
|
|
118
|
+
const currentIds = new Set(features.map(f => f.id));
|
|
119
|
+
for (const [id, marker] of markers.entries()) {
|
|
120
|
+
if (!currentIds.has(id)) {
|
|
121
|
+
marker.remove();
|
|
122
|
+
markers.delete(id);
|
|
123
|
+
console.log('[FeatureSelectionControl] Removed marker for', id);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Add markers for new selections
|
|
128
|
+
for (const feature of features) {
|
|
129
|
+
if (!markers.has(feature.id)) {
|
|
130
|
+
// Try to extract coordinates from properties
|
|
131
|
+
const lat = feature.properties?.latitude || feature.properties?.lat;
|
|
132
|
+
const lon = feature.properties?.longitude || feature.properties?.lon || feature.properties?.lng;
|
|
133
|
+
|
|
134
|
+
if (lat && lon) {
|
|
135
|
+
const marker = new mapboxgl.Marker({ color: '#FF6B35' })
|
|
136
|
+
.setLngLat([lon, lat])
|
|
137
|
+
.addTo(map);
|
|
138
|
+
|
|
139
|
+
markers.set(feature.id, marker);
|
|
140
|
+
console.log('[FeatureSelectionControl] Added marker for', feature.id, 'at', [lon, lat]);
|
|
141
|
+
} else {
|
|
142
|
+
console.warn('[FeatureSelectionControl] No coordinates found for feature', feature.id);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function clearAllMarkers() {
|
|
149
|
+
for (const marker of markers.values()) {
|
|
150
|
+
marker.remove();
|
|
151
|
+
}
|
|
152
|
+
markers.clear();
|
|
153
|
+
console.log('[FeatureSelectionControl] All markers cleared');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function handleCollapseToggle(collapsed: boolean) {
|
|
157
|
+
isCollapsed = collapsed;
|
|
158
|
+
// When control is expanded, enable selection mode
|
|
159
|
+
// When control is collapsed, disable selection mode
|
|
160
|
+
if (collapsed) {
|
|
161
|
+
store.disableSelectionMode();
|
|
162
|
+
} else {
|
|
163
|
+
store.enableSelectionMode();
|
|
164
|
+
}
|
|
96
165
|
}
|
|
97
166
|
|
|
98
167
|
function handleIdPropertyChange(event: Event) {
|
|
@@ -107,6 +176,7 @@
|
|
|
107
176
|
|
|
108
177
|
function handleClearAll() {
|
|
109
178
|
store.clearSelection();
|
|
179
|
+
clearAllMarkers();
|
|
110
180
|
}
|
|
111
181
|
|
|
112
182
|
async function handleCopy() {
|
|
@@ -127,32 +197,8 @@
|
|
|
127
197
|
}
|
|
128
198
|
</script>
|
|
129
199
|
|
|
130
|
-
<MapControl {position} {title} {icon} {iconOnlyWhenCollapsed} collapsible={true}>
|
|
200
|
+
<MapControl {position} {title} {icon} {iconOnlyWhenCollapsed} collapsible={true} onCollapseToggle={handleCollapseToggle}>
|
|
131
201
|
<div class="feature-selection-control">
|
|
132
|
-
<!-- Selection Mode Toggle -->
|
|
133
|
-
<div class="selection-mode-toggle mb-3">
|
|
134
|
-
<div class="form-check form-switch">
|
|
135
|
-
<input
|
|
136
|
-
type="checkbox"
|
|
137
|
-
class="form-check-input"
|
|
138
|
-
id="feature-selection-mode-toggle"
|
|
139
|
-
checked={store.selectionMode}
|
|
140
|
-
onchange={handleToggleMode}
|
|
141
|
-
/>
|
|
142
|
-
<label class="form-check-label" for="feature-selection-mode-toggle">
|
|
143
|
-
Selection Mode
|
|
144
|
-
<span class="badge ms-2" class:bg-success={store.selectionMode} class:bg-secondary={!store.selectionMode}>
|
|
145
|
-
{store.selectionMode ? 'ON' : 'OFF'}
|
|
146
|
-
</span>
|
|
147
|
-
</label>
|
|
148
|
-
</div>
|
|
149
|
-
{#if store.selectionMode}
|
|
150
|
-
<small class="text-muted d-block mt-1">
|
|
151
|
-
Click any feature on the map to select
|
|
152
|
-
</small>
|
|
153
|
-
{/if}
|
|
154
|
-
</div>
|
|
155
|
-
|
|
156
202
|
<!-- ID Property Selector -->
|
|
157
203
|
<div class="mb-3">
|
|
158
204
|
<label for="id-property-select" class="form-label small">Select By</label>
|
|
@@ -171,7 +217,7 @@
|
|
|
171
217
|
<!-- Selection Stats -->
|
|
172
218
|
<div class="selection-stats mb-2">
|
|
173
219
|
<strong>{selectionCount}</strong>
|
|
174
|
-
{selectionCount === 1 ? '
|
|
220
|
+
{selectionCount === 1 ? 'item' : 'items'} selected
|
|
175
221
|
</div>
|
|
176
222
|
|
|
177
223
|
<!-- Action Buttons -->
|
|
@@ -225,7 +271,8 @@
|
|
|
225
271
|
{:else}
|
|
226
272
|
<div class="text-muted small text-center py-2">
|
|
227
273
|
<i class="bi bi-inbox"></i>
|
|
228
|
-
<div class="mt-1">No
|
|
274
|
+
<div class="mt-1">No items selected</div>
|
|
275
|
+
<div class="mt-1">Click on cells or sites</div>
|
|
229
276
|
</div>
|
|
230
277
|
{/if}
|
|
231
278
|
|
|
@@ -27,6 +27,8 @@
|
|
|
27
27
|
collapsible?: boolean;
|
|
28
28
|
/** Initial collapsed state */
|
|
29
29
|
initiallyCollapsed?: boolean;
|
|
30
|
+
/** Optional callback when collapse state changes */
|
|
31
|
+
onCollapseToggle?: (collapsed: boolean) => void;
|
|
30
32
|
/** Custom CSS class for the container */
|
|
31
33
|
className?: string;
|
|
32
34
|
/** Child content */
|
|
@@ -44,13 +46,12 @@
|
|
|
44
46
|
iconOnlyWhenCollapsed = true,
|
|
45
47
|
collapsible = true,
|
|
46
48
|
initiallyCollapsed = true,
|
|
49
|
+
onCollapseToggle,
|
|
47
50
|
className = '',
|
|
48
51
|
children,
|
|
49
52
|
edgeOffset = '12px',
|
|
50
53
|
controlWidth = '420px'
|
|
51
54
|
}: Props = $props();
|
|
52
|
-
|
|
53
|
-
|
|
54
55
|
const mapStore = tryUseMapbox();
|
|
55
56
|
|
|
56
57
|
if (!mapStore) {
|
|
@@ -113,6 +114,10 @@
|
|
|
113
114
|
|
|
114
115
|
function toggleCollapse() {
|
|
115
116
|
collapsed = !collapsed;
|
|
117
|
+
// Call the callback if provided
|
|
118
|
+
if (onCollapseToggle) {
|
|
119
|
+
onCollapseToggle(collapsed);
|
|
120
|
+
}
|
|
116
121
|
}
|
|
117
122
|
</script>
|
|
118
123
|
|
|
@@ -11,6 +11,8 @@ interface Props {
|
|
|
11
11
|
collapsible?: boolean;
|
|
12
12
|
/** Initial collapsed state */
|
|
13
13
|
initiallyCollapsed?: boolean;
|
|
14
|
+
/** Optional callback when collapse state changes */
|
|
15
|
+
onCollapseToggle?: (collapsed: boolean) => void;
|
|
14
16
|
/** Custom CSS class for the container */
|
|
15
17
|
className?: string;
|
|
16
18
|
/** Child content */
|