@smartnet360/svelte-components 0.0.55 → 0.0.57
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 +1 -0
- package/dist/map-v2/features/sites/controls/SiteFilterControl.svelte +37 -2
- package/dist/map-v2/features/sites/controls/SiteSizeSlider.svelte +53 -2
- package/dist/map-v2/features/sites/layers/SitesLayer.svelte +24 -7
- package/dist/map-v2/features/sites/stores/siteStoreContext.svelte.d.ts +7 -0
- package/dist/map-v2/features/sites/stores/siteStoreContext.svelte.js +90 -8
- package/dist/map-v2/features/sites/types.d.ts +3 -0
- package/dist/map-v2/features/sites/utils/siteGeoJSON.d.ts +3 -1
- package/dist/map-v2/features/sites/utils/siteGeoJSON.js +18 -9
- package/dist/map-v2/features/sites/utils/siteTreeUtils.d.ts +3 -1
- package/dist/map-v2/features/sites/utils/siteTreeUtils.js +6 -2
- package/dist/map-v2/index.d.ts +1 -1
- package/dist/map-v2/index.js +1 -1
- package/package.json +1 -1
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
|
|
44
44
|
if (store.allSites.length > 0) {
|
|
45
45
|
console.log('SiteFilterControl: Building tree...');
|
|
46
|
-
const treeNodes = buildSiteTree(store.allSites);
|
|
46
|
+
const treeNodes = buildSiteTree(store.allSites, store.groupColorMap);
|
|
47
47
|
|
|
48
48
|
treeStore = createTreeStore({
|
|
49
49
|
nodes: [treeNodes],
|
|
@@ -71,12 +71,30 @@
|
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
});
|
|
74
|
+
|
|
75
|
+
function handleColorChange(groupKey: string, color: string) {
|
|
76
|
+
store.setGroupColor(groupKey, color);
|
|
77
|
+
}
|
|
74
78
|
</script>
|
|
75
79
|
|
|
76
80
|
<MapControl {position} {title} collapsible={true} {initiallyCollapsed}>
|
|
77
81
|
{#if treeStore && $treeStore}
|
|
78
82
|
<div class="site-filter-tree">
|
|
79
|
-
<TreeView store={$treeStore} showControls={false}
|
|
83
|
+
<TreeView store={$treeStore} showControls={false}>
|
|
84
|
+
{#snippet children({ node, state })}
|
|
85
|
+
<!-- Custom node rendering with color picker for leaf nodes -->
|
|
86
|
+
{#if node.metadata?.type === 'featureGroup'}
|
|
87
|
+
<input
|
|
88
|
+
type="color"
|
|
89
|
+
class="color-picker"
|
|
90
|
+
value={node.metadata.color || store.color}
|
|
91
|
+
oninput={(e) => handleColorChange(node.id, e.currentTarget.value)}
|
|
92
|
+
onclick={(e) => e.stopPropagation()}
|
|
93
|
+
title="Choose color for this group"
|
|
94
|
+
/>
|
|
95
|
+
{/if}
|
|
96
|
+
{/snippet}
|
|
97
|
+
</TreeView>
|
|
80
98
|
|
|
81
99
|
<div class="site-filter-stats">
|
|
82
100
|
<small class="text-muted">
|
|
@@ -102,8 +120,25 @@
|
|
|
102
120
|
text-align: center;
|
|
103
121
|
}
|
|
104
122
|
|
|
123
|
+
.color-picker {
|
|
124
|
+
width: 24px;
|
|
125
|
+
height: 24px;
|
|
126
|
+
padding: 0;
|
|
127
|
+
border: 1px solid #ccc;
|
|
128
|
+
border-radius: 4px;
|
|
129
|
+
cursor: pointer;
|
|
130
|
+
flex-shrink: 0;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.color-picker:hover {
|
|
134
|
+
border-color: #0d6efd;
|
|
135
|
+
}
|
|
136
|
+
|
|
105
137
|
:global(.site-filter-tree .tree-node-label) {
|
|
106
138
|
font-size: 13px;
|
|
139
|
+
display: flex;
|
|
140
|
+
align-items: center;
|
|
141
|
+
flex: 1;
|
|
107
142
|
}
|
|
108
143
|
|
|
109
144
|
:global(.site-filter-tree .tree-node-checkbox) {
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
<!-- Size slider -->
|
|
44
44
|
<div class="control-row">
|
|
45
45
|
<label for="site-size-slider" class="control-label">
|
|
46
|
-
|
|
46
|
+
Site Radius: <strong>{store.size}px</strong>
|
|
47
47
|
</label>
|
|
48
48
|
<input
|
|
49
49
|
id="site-size-slider"
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
<!-- Circle Color -->
|
|
76
76
|
<div class="control-row">
|
|
77
77
|
<label for="site-color-picker" class="control-label">
|
|
78
|
-
|
|
78
|
+
Base Color
|
|
79
79
|
</label>
|
|
80
80
|
<input
|
|
81
81
|
id="site-color-picker"
|
|
@@ -129,6 +129,40 @@
|
|
|
129
129
|
bind:value={store.labelColor}
|
|
130
130
|
/>
|
|
131
131
|
</div>
|
|
132
|
+
|
|
133
|
+
<!-- Label offset slider -->
|
|
134
|
+
<div class="control-row">
|
|
135
|
+
<label for="label-offset-slider" class="control-label">
|
|
136
|
+
Label Offset: <strong>{store.labelOffset.toFixed(1)}</strong>
|
|
137
|
+
</label>
|
|
138
|
+
<input
|
|
139
|
+
id="label-offset-slider"
|
|
140
|
+
type="range"
|
|
141
|
+
class="form-range"
|
|
142
|
+
min="-3"
|
|
143
|
+
max="3"
|
|
144
|
+
step="0.1"
|
|
145
|
+
bind:value={store.labelOffset}
|
|
146
|
+
/>
|
|
147
|
+
</div>
|
|
148
|
+
|
|
149
|
+
<!-- Label property dropdown -->
|
|
150
|
+
<div class="control-row">
|
|
151
|
+
<label for="label-property-select" class="control-label">
|
|
152
|
+
Label Field
|
|
153
|
+
</label>
|
|
154
|
+
<select
|
|
155
|
+
id="label-property-select"
|
|
156
|
+
class="form-select"
|
|
157
|
+
bind:value={store.labelProperty}
|
|
158
|
+
>
|
|
159
|
+
<option value="name">Name</option>
|
|
160
|
+
<option value="id">ID</option>
|
|
161
|
+
<option value="provider">Provider</option>
|
|
162
|
+
<option value="technology">Technology</option>
|
|
163
|
+
<option value="featureGroup">Feature Group</option>
|
|
164
|
+
</select>
|
|
165
|
+
</div>
|
|
132
166
|
{/if}
|
|
133
167
|
</div>
|
|
134
168
|
</MapControl>
|
|
@@ -182,4 +216,21 @@
|
|
|
182
216
|
border-radius: 4px;
|
|
183
217
|
cursor: pointer;
|
|
184
218
|
}
|
|
219
|
+
|
|
220
|
+
.form-select {
|
|
221
|
+
flex: 1;
|
|
222
|
+
min-width: 0;
|
|
223
|
+
font-size: 12px;
|
|
224
|
+
padding: 4px 8px;
|
|
225
|
+
border: 1px solid #dee2e6;
|
|
226
|
+
border-radius: 4px;
|
|
227
|
+
background-color: white;
|
|
228
|
+
cursor: pointer;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.form-select:focus {
|
|
232
|
+
border-color: #0d6efd;
|
|
233
|
+
outline: none;
|
|
234
|
+
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
|
|
235
|
+
}
|
|
185
236
|
</style>
|
|
@@ -60,6 +60,9 @@
|
|
|
60
60
|
const showLabels = store.showLabels;
|
|
61
61
|
const labelSize = store.labelSize;
|
|
62
62
|
const labelColor = store.labelColor;
|
|
63
|
+
const labelOffset = store.labelOffset;
|
|
64
|
+
const labelProperty = store.labelProperty;
|
|
65
|
+
const groupColorMap = store.groupColorMap; // Track color map changes
|
|
63
66
|
|
|
64
67
|
if (map) {
|
|
65
68
|
updateLayer();
|
|
@@ -107,10 +110,18 @@
|
|
|
107
110
|
source: sourceId,
|
|
108
111
|
paint: {
|
|
109
112
|
'circle-radius': store.size,
|
|
110
|
-
'circle-color':
|
|
113
|
+
'circle-color': [
|
|
114
|
+
'coalesce',
|
|
115
|
+
['get', 'groupColor'], // Use group-specific color if available
|
|
116
|
+
store.color // Fallback to global color
|
|
117
|
+
],
|
|
111
118
|
'circle-opacity': store.opacity,
|
|
112
119
|
'circle-stroke-width': 2,
|
|
113
|
-
'circle-stroke-color':
|
|
120
|
+
'circle-stroke-color': [
|
|
121
|
+
'coalesce',
|
|
122
|
+
['get', 'groupColor'], // Use group-specific color if available
|
|
123
|
+
store.color // Fallback to global color
|
|
124
|
+
],
|
|
114
125
|
'circle-stroke-opacity': store.opacity
|
|
115
126
|
}
|
|
116
127
|
});
|
|
@@ -121,9 +132,9 @@
|
|
|
121
132
|
type: 'symbol',
|
|
122
133
|
source: sourceId,
|
|
123
134
|
layout: {
|
|
124
|
-
'text-field': ['get',
|
|
135
|
+
'text-field': ['get', store.labelProperty],
|
|
125
136
|
'text-size': store.labelSize,
|
|
126
|
-
'text-offset': [0,
|
|
137
|
+
'text-offset': [0, store.labelOffset],
|
|
127
138
|
'text-anchor': 'top',
|
|
128
139
|
'text-font': ['Open Sans Regular', 'Arial Unicode MS Regular']
|
|
129
140
|
},
|
|
@@ -151,18 +162,24 @@
|
|
|
151
162
|
function updateLayer(): void {
|
|
152
163
|
if (!map) return;
|
|
153
164
|
|
|
154
|
-
// Update data
|
|
155
|
-
const geojson = sitesToGeoJSON(store.filteredSites);
|
|
165
|
+
// Update data with color information
|
|
166
|
+
const geojson = sitesToGeoJSON(store.filteredSites, store.groupColorMap);
|
|
156
167
|
updateGeoJSONSource(map, sourceId, geojson);
|
|
157
168
|
|
|
158
169
|
// Update circle visual properties
|
|
159
170
|
map.setPaintProperty(layerId, 'circle-radius', store.size);
|
|
160
|
-
map.setPaintProperty(layerId, 'circle-color',
|
|
171
|
+
map.setPaintProperty(layerId, 'circle-color', [
|
|
172
|
+
'coalesce',
|
|
173
|
+
['get', 'groupColor'],
|
|
174
|
+
store.color
|
|
175
|
+
]);
|
|
161
176
|
map.setPaintProperty(layerId, 'circle-opacity', store.opacity);
|
|
162
177
|
map.setPaintProperty(layerId, 'circle-stroke-opacity', store.opacity);
|
|
163
178
|
|
|
164
179
|
// Update label properties
|
|
180
|
+
map.setLayoutProperty(labelLayerId, 'text-field', ['get', store.labelProperty]);
|
|
165
181
|
map.setLayoutProperty(labelLayerId, 'text-size', store.labelSize);
|
|
182
|
+
map.setLayoutProperty(labelLayerId, 'text-offset', [0, store.labelOffset]);
|
|
166
183
|
map.setPaintProperty(labelLayerId, 'text-color', store.labelColor);
|
|
167
184
|
map.setLayoutProperty(labelLayerId, 'visibility', store.showLabels ? 'visible' : 'none');
|
|
168
185
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Uses Svelte 5 runes ($state) to create a directly bindable reactive object.
|
|
5
5
|
* This allows components to use bind:value instead of manual event handlers.
|
|
6
|
+
* Persists visual settings to localStorage.
|
|
6
7
|
*/
|
|
7
8
|
import type { Site, SiteStoreValue } from '../types';
|
|
8
9
|
export declare function createSiteStoreContext(initialSites?: Site[]): {
|
|
@@ -15,14 +16,20 @@ export declare function createSiteStoreContext(initialSites?: Site[]): {
|
|
|
15
16
|
showOnHover: boolean;
|
|
16
17
|
labelSize: number;
|
|
17
18
|
labelColor: string;
|
|
19
|
+
labelOffset: number;
|
|
20
|
+
labelProperty: string;
|
|
18
21
|
strokeWidth: number;
|
|
19
22
|
strokeColor: string;
|
|
23
|
+
groupColorMap: Map<string, string>;
|
|
20
24
|
setAllSites(sites: Site[]): void;
|
|
21
25
|
setFilteredSites(sites: Site[]): void;
|
|
22
26
|
setSize(size: number): void;
|
|
23
27
|
setColor(color: string): void;
|
|
24
28
|
setOpacity(opacity: number): void;
|
|
25
29
|
setShowLabels(show: boolean): void;
|
|
30
|
+
getGroupColor(groupKey: string): string | undefined;
|
|
31
|
+
setGroupColor(groupKey: string, color: string): void;
|
|
32
|
+
clearGroupColor(groupKey: string): void;
|
|
26
33
|
reset(): void;
|
|
27
34
|
getState(): SiteStoreValue;
|
|
28
35
|
};
|
|
@@ -3,21 +3,81 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Uses Svelte 5 runes ($state) to create a directly bindable reactive object.
|
|
5
5
|
* This allows components to use bind:value instead of manual event handlers.
|
|
6
|
+
* Persists visual settings to localStorage.
|
|
6
7
|
*/
|
|
8
|
+
const STORAGE_KEY = 'siteVisualSettings';
|
|
9
|
+
function loadSettings() {
|
|
10
|
+
if (typeof window === 'undefined')
|
|
11
|
+
return {};
|
|
12
|
+
try {
|
|
13
|
+
const saved = localStorage.getItem(STORAGE_KEY);
|
|
14
|
+
if (saved) {
|
|
15
|
+
return JSON.parse(saved);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
console.warn('Failed to load site settings from localStorage:', error);
|
|
20
|
+
}
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
function saveSettings(settings) {
|
|
24
|
+
if (typeof window === 'undefined')
|
|
25
|
+
return;
|
|
26
|
+
try {
|
|
27
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
console.warn('Failed to save site settings to localStorage:', error);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
7
33
|
export function createSiteStoreContext(initialSites = []) {
|
|
34
|
+
// Load persisted settings
|
|
35
|
+
const persistedSettings = loadSettings();
|
|
36
|
+
// Convert persisted groupColors object back to Map
|
|
37
|
+
const initialColorMap = new Map();
|
|
38
|
+
if (persistedSettings.groupColors) {
|
|
39
|
+
Object.entries(persistedSettings.groupColors).forEach(([key, value]) => {
|
|
40
|
+
initialColorMap.set(key, value);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
8
43
|
// Internal reactive state
|
|
9
44
|
let state = $state({
|
|
10
45
|
allSites: initialSites,
|
|
11
46
|
filteredSites: initialSites,
|
|
12
|
-
size: 10,
|
|
13
|
-
color: '#3b82f6',
|
|
14
|
-
opacity: 1.0,
|
|
15
|
-
showLabels: false,
|
|
47
|
+
size: persistedSettings.size ?? 10,
|
|
48
|
+
color: persistedSettings.color ?? '#3b82f6',
|
|
49
|
+
opacity: persistedSettings.opacity ?? 1.0,
|
|
50
|
+
showLabels: persistedSettings.showLabels ?? false,
|
|
16
51
|
showOnHover: true,
|
|
17
|
-
labelSize: 12,
|
|
18
|
-
labelColor: '#000000',
|
|
19
|
-
|
|
20
|
-
|
|
52
|
+
labelSize: persistedSettings.labelSize ?? 12,
|
|
53
|
+
labelColor: persistedSettings.labelColor ?? '#000000',
|
|
54
|
+
labelOffset: persistedSettings.labelOffset ?? 1.5,
|
|
55
|
+
labelProperty: persistedSettings.labelProperty ?? 'name',
|
|
56
|
+
groupColorMap: initialColorMap,
|
|
57
|
+
strokeWidth: persistedSettings.strokeWidth ?? 2,
|
|
58
|
+
strokeColor: persistedSettings.strokeColor ?? '#ffffff'
|
|
59
|
+
});
|
|
60
|
+
// Auto-save settings when they change
|
|
61
|
+
$effect(() => {
|
|
62
|
+
// Convert Map to plain object for serialization
|
|
63
|
+
const groupColorsObj = {};
|
|
64
|
+
state.groupColorMap.forEach((color, key) => {
|
|
65
|
+
groupColorsObj[key] = color;
|
|
66
|
+
});
|
|
67
|
+
const settings = {
|
|
68
|
+
size: state.size,
|
|
69
|
+
color: state.color,
|
|
70
|
+
opacity: state.opacity,
|
|
71
|
+
showLabels: state.showLabels,
|
|
72
|
+
labelSize: state.labelSize,
|
|
73
|
+
labelColor: state.labelColor,
|
|
74
|
+
labelOffset: state.labelOffset,
|
|
75
|
+
labelProperty: state.labelProperty,
|
|
76
|
+
strokeWidth: state.strokeWidth ?? 2,
|
|
77
|
+
strokeColor: state.strokeColor ?? '#ffffff',
|
|
78
|
+
groupColors: groupColorsObj
|
|
79
|
+
};
|
|
80
|
+
saveSettings(settings);
|
|
21
81
|
});
|
|
22
82
|
// Return object with getters/setters for direct binding
|
|
23
83
|
return {
|
|
@@ -40,10 +100,16 @@ export function createSiteStoreContext(initialSites = []) {
|
|
|
40
100
|
set labelSize(value) { state.labelSize = value; },
|
|
41
101
|
get labelColor() { return state.labelColor; },
|
|
42
102
|
set labelColor(value) { state.labelColor = value; },
|
|
103
|
+
get labelOffset() { return state.labelOffset; },
|
|
104
|
+
set labelOffset(value) { state.labelOffset = value; },
|
|
105
|
+
get labelProperty() { return state.labelProperty; },
|
|
106
|
+
set labelProperty(value) { state.labelProperty = value; },
|
|
43
107
|
get strokeWidth() { return state.strokeWidth ?? 2; },
|
|
44
108
|
set strokeWidth(value) { state.strokeWidth = value; },
|
|
45
109
|
get strokeColor() { return state.strokeColor ?? '#ffffff'; },
|
|
46
110
|
set strokeColor(value) { state.strokeColor = value; },
|
|
111
|
+
get groupColorMap() { return state.groupColorMap; },
|
|
112
|
+
set groupColorMap(value) { state.groupColorMap = value; },
|
|
47
113
|
// Convenience methods (optional, but nice to have)
|
|
48
114
|
setAllSites(sites) { state.allSites = sites; },
|
|
49
115
|
setFilteredSites(sites) { state.filteredSites = sites; },
|
|
@@ -51,6 +117,19 @@ export function createSiteStoreContext(initialSites = []) {
|
|
|
51
117
|
setColor(color) { state.color = color; },
|
|
52
118
|
setOpacity(opacity) { state.opacity = opacity; },
|
|
53
119
|
setShowLabels(show) { state.showLabels = show; },
|
|
120
|
+
// Group color methods
|
|
121
|
+
getGroupColor(groupKey) {
|
|
122
|
+
return state.groupColorMap.get(groupKey);
|
|
123
|
+
},
|
|
124
|
+
setGroupColor(groupKey, color) {
|
|
125
|
+
state.groupColorMap.set(groupKey, color);
|
|
126
|
+
// Trigger reactivity by reassigning
|
|
127
|
+
state.groupColorMap = new Map(state.groupColorMap);
|
|
128
|
+
},
|
|
129
|
+
clearGroupColor(groupKey) {
|
|
130
|
+
state.groupColorMap.delete(groupKey);
|
|
131
|
+
state.groupColorMap = new Map(state.groupColorMap);
|
|
132
|
+
},
|
|
54
133
|
// Reset to defaults
|
|
55
134
|
reset() {
|
|
56
135
|
state.allSites = initialSites;
|
|
@@ -62,8 +141,11 @@ export function createSiteStoreContext(initialSites = []) {
|
|
|
62
141
|
state.showOnHover = true;
|
|
63
142
|
state.labelSize = 12;
|
|
64
143
|
state.labelColor = '#000000';
|
|
144
|
+
state.labelOffset = 1.5;
|
|
145
|
+
state.labelProperty = 'name';
|
|
65
146
|
state.strokeWidth = 2;
|
|
66
147
|
state.strokeColor = '#ffffff';
|
|
148
|
+
state.groupColorMap = new Map();
|
|
67
149
|
},
|
|
68
150
|
// Get snapshot of current state (useful for debugging)
|
|
69
151
|
getState() {
|
|
@@ -29,6 +29,9 @@ export interface SiteStoreValue {
|
|
|
29
29
|
showOnHover: boolean;
|
|
30
30
|
labelSize: number;
|
|
31
31
|
labelColor: string;
|
|
32
|
+
labelOffset: number;
|
|
33
|
+
labelProperty: string;
|
|
34
|
+
groupColorMap: Map<string, string>;
|
|
32
35
|
hoverColor?: string;
|
|
33
36
|
selectedColor?: string;
|
|
34
37
|
strokeWidth?: number;
|
|
@@ -23,8 +23,10 @@ export interface FeatureCollection<T> {
|
|
|
23
23
|
/**
|
|
24
24
|
* Converts an array of sites to a GeoJSON FeatureCollection
|
|
25
25
|
* Sites are represented as Point features (circles will be drawn by Mapbox)
|
|
26
|
+
* @param sites - Array of sites to convert
|
|
27
|
+
* @param colorMap - Optional map of group keys (provider:featureGroup) to colors
|
|
26
28
|
*/
|
|
27
|
-
export declare function sitesToGeoJSON(sites: Site[]): FeatureCollection<SiteFeature>;
|
|
29
|
+
export declare function sitesToGeoJSON(sites: Site[], colorMap?: Map<string, string>): FeatureCollection<SiteFeature>;
|
|
28
30
|
/**
|
|
29
31
|
* Converts a single site to a GeoJSON Feature
|
|
30
32
|
*/
|
|
@@ -4,16 +4,25 @@
|
|
|
4
4
|
/**
|
|
5
5
|
* Converts an array of sites to a GeoJSON FeatureCollection
|
|
6
6
|
* Sites are represented as Point features (circles will be drawn by Mapbox)
|
|
7
|
+
* @param sites - Array of sites to convert
|
|
8
|
+
* @param colorMap - Optional map of group keys (provider:featureGroup) to colors
|
|
7
9
|
*/
|
|
8
|
-
export function sitesToGeoJSON(sites) {
|
|
9
|
-
const features = sites.map((site) =>
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
export function sitesToGeoJSON(sites, colorMap) {
|
|
11
|
+
const features = sites.map((site) => {
|
|
12
|
+
const groupKey = `${site.provider}:${site.featureGroup}`;
|
|
13
|
+
const groupColor = colorMap?.get(groupKey);
|
|
14
|
+
return {
|
|
15
|
+
type: 'Feature',
|
|
16
|
+
geometry: {
|
|
17
|
+
type: 'Point',
|
|
18
|
+
coordinates: [site.longitude, site.latitude]
|
|
19
|
+
},
|
|
20
|
+
properties: {
|
|
21
|
+
...site,
|
|
22
|
+
groupColor // Add groupColor property for Mapbox styling
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
});
|
|
17
26
|
return {
|
|
18
27
|
type: 'FeatureCollection',
|
|
19
28
|
features
|
|
@@ -6,8 +6,10 @@ import type { TreeNode } from '../../../../core/TreeView/tree.model';
|
|
|
6
6
|
/**
|
|
7
7
|
* Builds a hierarchical tree from flat site array
|
|
8
8
|
* Structure: All Sites -> Provider -> Feature Group
|
|
9
|
+
* @param sites - Array of sites to build tree from
|
|
10
|
+
* @param colorMap - Optional map of group keys (provider:featureGroup) to colors
|
|
9
11
|
*/
|
|
10
|
-
export declare function buildSiteTree(sites: Site[]): TreeNode;
|
|
12
|
+
export declare function buildSiteTree(sites: Site[], colorMap?: Map<string, string>): TreeNode;
|
|
11
13
|
/**
|
|
12
14
|
* Filters sites based on checked tree paths
|
|
13
15
|
*/
|
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
/**
|
|
5
5
|
* Builds a hierarchical tree from flat site array
|
|
6
6
|
* Structure: All Sites -> Provider -> Feature Group
|
|
7
|
+
* @param sites - Array of sites to build tree from
|
|
8
|
+
* @param colorMap - Optional map of group keys (provider:featureGroup) to colors
|
|
7
9
|
*/
|
|
8
|
-
export function buildSiteTree(sites) {
|
|
10
|
+
export function buildSiteTree(sites, colorMap) {
|
|
9
11
|
// Group sites by provider, then by feature group
|
|
10
12
|
const providerGroups = new Map();
|
|
11
13
|
sites.forEach((site) => {
|
|
@@ -30,6 +32,7 @@ export function buildSiteTree(sites) {
|
|
|
30
32
|
sortedFeatureGroups.forEach((featureGroup) => {
|
|
31
33
|
const groupSites = featureGroups.get(featureGroup);
|
|
32
34
|
const nodeId = `${provider}:${featureGroup}`;
|
|
35
|
+
const groupKey = nodeId; // Use same key format for color lookup
|
|
33
36
|
providerChildren.push({
|
|
34
37
|
id: nodeId,
|
|
35
38
|
label: `${featureGroup} (${groupSites.length})`,
|
|
@@ -39,7 +42,8 @@ export function buildSiteTree(sites) {
|
|
|
39
42
|
type: 'featureGroup',
|
|
40
43
|
provider,
|
|
41
44
|
featureGroup,
|
|
42
|
-
siteIds: groupSites.map((s) => s.id)
|
|
45
|
+
siteIds: groupSites.map((s) => s.id),
|
|
46
|
+
color: colorMap?.get(groupKey) // Add color from map if available
|
|
43
47
|
}
|
|
44
48
|
});
|
|
45
49
|
});
|
package/dist/map-v2/index.d.ts
CHANGED
|
@@ -6,5 +6,5 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export { type MapStore, MAP_CONTEXT_KEY, MapboxProvider, MapStyleControl, createMapStore, useMapbox, tryUseMapbox } from './core';
|
|
8
8
|
export { MapControl, addSourceIfMissing, removeSourceIfExists, addLayerIfMissing, removeLayerIfExists, updateGeoJSONSource, removeLayerAndSource, isStyleLoaded, waitForStyleLoad, setFeatureState, removeFeatureState, generateLayerId, generateSourceId } from './shared';
|
|
9
|
-
export { type Site, type SiteStoreValue, createSiteStore, SitesLayer, SiteFilterControl, SiteSizeSlider, sitesToGeoJSON, siteToFeature, buildSiteTree, getFilteredSites } from './features/sites';
|
|
9
|
+
export { type Site, type SiteStoreValue, type SiteStoreContext, createSiteStore, createSiteStoreContext, SitesLayer, SiteFilterControl, SiteSizeSlider, sitesToGeoJSON, siteToFeature, buildSiteTree, getFilteredSites } from './features/sites';
|
|
10
10
|
export { DemoMap, demoSites } from './demo';
|
package/dist/map-v2/index.js
CHANGED
|
@@ -15,7 +15,7 @@ export { MapControl, addSourceIfMissing, removeSourceIfExists, addLayerIfMissing
|
|
|
15
15
|
// ============================================================================
|
|
16
16
|
// SITE FEATURE
|
|
17
17
|
// ============================================================================
|
|
18
|
-
export { createSiteStore, SitesLayer, SiteFilterControl, SiteSizeSlider, sitesToGeoJSON, siteToFeature, buildSiteTree, getFilteredSites } from './features/sites';
|
|
18
|
+
export { createSiteStore, createSiteStoreContext, SitesLayer, SiteFilterControl, SiteSizeSlider, sitesToGeoJSON, siteToFeature, buildSiteTree, getFilteredSites } from './features/sites';
|
|
19
19
|
// ============================================================================
|
|
20
20
|
// DEMO
|
|
21
21
|
// ============================================================================
|