datastake-daf 0.6.280 β†’ 0.6.281

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.
@@ -0,0 +1,106 @@
1
+ # SimpleGlobe Component
2
+
3
+ A simplified wrapper for the Globe component that makes it easy to display project data as pins on a 3D globe.
4
+
5
+ ## Features
6
+
7
+ - πŸ—ΊοΈ **3D Globe Visualization**: Interactive 3D globe using Mapbox GL JS
8
+ - πŸ“ **Pin-based Display**: Projects are displayed as clickable pins on the globe
9
+ - 🎯 **Simple Integration**: Just pass your project data and it works
10
+ - πŸ“Š **Rich Tooltips**: Hover over pins to see project details
11
+ - 🎨 **Customizable**: Support for custom map configurations
12
+
13
+ ## Quick Start
14
+
15
+ ```jsx
16
+ import SimpleGlobe from './SimpleGlobe';
17
+
18
+ const MyComponent = () => {
19
+ const projects = [
20
+ {
21
+ name: "Solar Project",
22
+ latitude: 14.7167,
23
+ longitude: -17.4677,
24
+ percentageCompletion: 75,
25
+ projectDescription: "Large-scale solar energy project"
26
+ }
27
+ ];
28
+
29
+ return (
30
+ <div style={{ width: '100%', height: '600px' }}>
31
+ <SimpleGlobe
32
+ projects={projects}
33
+ onProjectClick={(project) => console.log('Clicked:', project)}
34
+ />
35
+ </div>
36
+ );
37
+ };
38
+ ```
39
+
40
+ ## Props
41
+
42
+ | Prop | Type | Default | Description |
43
+ |------|------|---------|-------------|
44
+ | `projects` | `Array` | `[]` | Array of project objects |
45
+ | `mapConfig` | `Object` | `{}` | Map configuration options |
46
+ | `showSider` | `boolean` | `false` | Whether to show the sidebar |
47
+ | `onProjectClick` | `Function` | `() => {}` | Callback when a project pin is clicked |
48
+
49
+ ## Project Data Format
50
+
51
+ Your project objects should have the following structure:
52
+
53
+ ```javascript
54
+ {
55
+ _id: "unique-id", // Optional: Unique identifier
56
+ name: "Project Name", // Required: Project name
57
+ latitude: 14.7167, // Required: Latitude coordinate
58
+ longitude: -17.4677, // Required: Longitude coordinate
59
+ projectDescription: "Description", // Optional: Project description
60
+ country: "SN", // Optional: Country code
61
+ sectoralScope: "energy", // Optional: Project sector
62
+ percentageCompletion: 75, // Optional: Completion percentage
63
+ author: { name: "Author Name" } // Optional: Author information
64
+ }
65
+ ```
66
+
67
+ ## Examples
68
+
69
+ ### Basic Usage
70
+ ```jsx
71
+ <SimpleGlobe projects={myProjects} />
72
+ ```
73
+
74
+ ### With Sidebar
75
+ ```jsx
76
+ <SimpleGlobe
77
+ projects={myProjects}
78
+ showSider={true}
79
+ />
80
+ ```
81
+
82
+ ### With Custom Configuration
83
+ ```jsx
84
+ <SimpleGlobe
85
+ projects={myProjects}
86
+ mapConfig={{
87
+ maxZoom: 8,
88
+ minZoom: 2
89
+ }}
90
+ onProjectClick={(project) => {
91
+ // Handle project click
92
+ console.log('Project clicked:', project);
93
+ }}
94
+ />
95
+ ```
96
+
97
+ ## Storybook
98
+
99
+ You can see all examples in Storybook under "Dashboard/Globe/SimpleGlobe".
100
+
101
+ ## Notes
102
+
103
+ - The component automatically transforms your project data to the format expected by the underlying Globe component
104
+ - Pins are displayed as circular markers with project completion percentage
105
+ - Tooltips show project details when hovering over pins
106
+ - The globe is fully interactive - you can zoom, pan, and rotate
@@ -0,0 +1,331 @@
1
+ import React, { useEffect, useRef } from 'react';
2
+ import mapboxgl from 'mapbox-gl';
3
+ import 'mapbox-gl/dist/mapbox-gl.css';
4
+ import Style from './style';
5
+ import { renderTooltip as renderTooltipHtml } from '../../../../utils/tooltip';
6
+
7
+ /**
8
+ * SimpleGlobe - A simplified wrapper for the Globe component
9
+ *
10
+ * This component makes it easy to display project data as pins on a 3D globe.
11
+ * Just pass your project data and it will automatically create pins at the specified locations.
12
+ *
13
+ * @param {Array} projects - Array of project objects with location data
14
+ * @param {string} projects[].name - Project name
15
+ * @param {number} projects[].latitude - Latitude coordinate
16
+ * @param {number} projects[].longitude - Longitude coordinate
17
+ * @param {string} projects[].description - Project description (optional)
18
+ * @param {string} projects[].country - Country code (optional)
19
+ * @param {string} projects[].sectoralScope - Project sector (optional)
20
+ * @param {number} projects[].percentageCompletion - Completion percentage (optional)
21
+ * @param {Object} projects[].author - Author information (optional)
22
+ * @param {Object} mapConfig - Map configuration options (optional)
23
+ * @param {boolean} showSider - Whether to show the sidebar (default: false)
24
+ * @param {Function} onProjectClick - Callback when a project pin is clicked
25
+ * @param {string} type - Marker type: "default" for circular markers, "location" for map pin markers
26
+ * @param {string} color - Marker color (default: "var(--color-primary-60)")
27
+ * @param {Function} renderTooltip - Custom tooltip renderer function (optional)
28
+ */
29
+ const SimpleGlobe = ({
30
+ projects = [],
31
+ mapConfig = {},
32
+ showSider = false,
33
+ onProjectClick = () => {},
34
+ type = "default",
35
+ color = "var(--color-primary-60)",
36
+ renderTooltip = null
37
+ }) => {
38
+ const mapContainer = useRef(null);
39
+ const map = useRef(null);
40
+ const boundsRef = useRef(null);
41
+
42
+ useEffect(() => {
43
+ if (map.current) return; // Initialize map only once
44
+
45
+ console.log('πŸ—ΊοΈ [SIMPLE GLOBE] Creating map...');
46
+
47
+ // Set Mapbox access token
48
+ mapboxgl.accessToken = 'pk.eyJ1IjoicmVkaXM5OTkiLCJhIjoiY2x4YWV5MzA5MmtuZzJpcXM5Y201Z2E2YiJ9.m5bwPg-Tj4Akesl1yQUa3w';
49
+
50
+ // Create map with custom Straatos style
51
+ map.current = new mapboxgl.Map({
52
+ container: mapContainer.current,
53
+ style: 'mapbox://styles/pietragottardo/cm9tt9zfi00d101pg1vdx26si',
54
+ center: [0, 0],
55
+ zoom: mapConfig.maxZoom || 3,
56
+ projection: 'globe',
57
+ attributionControl: false
58
+ });
59
+
60
+ // Add markers when map loads
61
+ map.current.on('load', () => {
62
+ console.log('πŸ—ΊοΈ [SIMPLE GLOBE] Map loaded, adding markers...');
63
+
64
+ // Hide Mapbox logo and attribution completely
65
+ const mapContainer = map.current.getContainer();
66
+ const style = document.createElement('style');
67
+ style.textContent = `
68
+ .mapboxgl-ctrl-logo,
69
+ .mapboxgl-ctrl-attrib,
70
+ .mapboxgl-ctrl-bottom-left,
71
+ .mapboxgl-ctrl-bottom-right {
72
+ display: none !important;
73
+ }
74
+ .mapboxgl-canvas-container {
75
+ overflow: hidden !important;
76
+ }
77
+ .mapboxgl-canvas {
78
+ overflow: hidden !important;
79
+ }
80
+ /* CRITICAL: Override Leaflet CSS interference with Mapbox */
81
+ .daf-simple-globe-container .mapboxgl-canvas-container {
82
+ position: relative !important;
83
+ left: auto !important;
84
+ top: auto !important;
85
+ }
86
+ .daf-simple-globe-container .mapboxgl-canvas-container canvas {
87
+ position: relative !important;
88
+ left: auto !important;
89
+ top: auto !important;
90
+ transform: none !important;
91
+ }
92
+ /* Prevent Leaflet styles from affecting Mapbox markers */
93
+ .daf-simple-globe-container .daf-globe-marker {
94
+ position: relative !important;
95
+ left: auto !important;
96
+ top: auto !important;
97
+ }
98
+ `;
99
+ document.head.appendChild(style);
100
+
101
+ // Set the space background with stars
102
+ try {
103
+ map.current.setFog({
104
+ 'color': 'rgb(0, 0, 0)',
105
+ 'high-color': 'rgb(0, 0, 0)',
106
+ 'horizon-blend': 0.1,
107
+ 'space-color': 'rgb(0, 0, 0)',
108
+ 'star-intensity': 0.6
109
+ });
110
+ console.log('✨ [SIMPLE GLOBE] Space background with stars set');
111
+ } catch (e) {
112
+ console.log('⚠️ [SIMPLE GLOBE] Could not set fog, trying alternative...');
113
+ // Fallback: try simpler fog configuration
114
+ try {
115
+ map.current.setFog({
116
+ 'color': 'rgb(0, 0, 0)',
117
+ 'high-color': 'rgb(0, 0, 0)',
118
+ 'horizon-blend': 0.1
119
+ });
120
+ console.log('✨ [SIMPLE GLOBE] Alternative space background set');
121
+ } catch (e2) {
122
+ console.log('⚠️ [SIMPLE GLOBE] Could not set any fog configuration');
123
+ }
124
+ }
125
+
126
+ // Calculate bounds to fit all markers
127
+ const bounds = new mapboxgl.LngLatBounds();
128
+ let hasValidCoordinates = false;
129
+
130
+ projects.forEach((project, index) => {
131
+ console.log(`πŸ“ [SIMPLE GLOBE] Adding marker ${index}:`, project);
132
+
133
+ // Create marker element based on type
134
+ const el = document.createElement('div');
135
+ // Use a scoped class to avoid picking up broad styles like `.map-marker { width:100% }`
136
+ el.className = 'daf-globe-marker';
137
+ el.style.cursor = 'pointer';
138
+ el.style.boxShadow = '0px 3.45px 3.45px 0px #00000029';
139
+ el.style.display = 'flex';
140
+ el.style.alignItems = 'center';
141
+ el.style.justifyContent = 'center';
142
+
143
+ if (type === "location") {
144
+ // Location marker - SVG map pin style
145
+ el.style.width = '28px';
146
+ el.style.height = '33px';
147
+ el.innerHTML = `
148
+ <svg
149
+ width="28"
150
+ height="33"
151
+ viewBox="0 0 28 33"
152
+ fill="none"
153
+ xmlns="http://www.w3.org/2000/svg"
154
+ >
155
+ <path
156
+ d="M5.14346 4.87419C10.0688 -0.15896 18.0528 -0.162058 22.9757 4.86861C27.6563 9.65161 27.8841 17.2616 23.6622 22.3255H23.6608C23.427 22.6141 23.1808 22.894 22.9211 23.1623L14.0671 32.2101L5.44057 23.3948L5.13868 23.096C0.215857 18.0655 0.218422 9.90737 5.14346 4.87419Z"
157
+ fill="${color}"
158
+ stroke="white"
159
+ />
160
+ </svg>
161
+ `;
162
+ } else {
163
+ // Default circular marker style
164
+ el.style.width = '30px';
165
+ el.style.height = '30px';
166
+ el.style.backgroundColor = color;
167
+ el.style.borderRadius = '50%';
168
+ el.style.border = '2px solid white';
169
+ el.style.color = 'white';
170
+ el.style.fontWeight = 'bold';
171
+ el.style.fontSize = '14px';
172
+ el.style.textAlign = 'center';
173
+ el.style.lineHeight = '1';
174
+ el.innerHTML = `<span style="display: block; line-height: 1;">${project.percentageCompletion || 0}</span>`;
175
+ }
176
+
177
+ // Create popup content using custom renderTooltip or default
178
+ let popupContent;
179
+
180
+ if (renderTooltip && typeof renderTooltip === 'function') {
181
+ // Use custom renderTooltip function (same as MineSiteMap)
182
+ const tooltipData = renderTooltip(project);
183
+ popupContent = renderTooltipHtml({
184
+ title: project.name,
185
+ subTitle: project.sectoralScope || 'Project',
186
+ items: tooltipData,
187
+ link: false
188
+ });
189
+ } else {
190
+ // Use default tooltip structure
191
+ const tooltipData = [
192
+
193
+ {
194
+ label: "Country",
195
+ value: project.country || 'N/A'
196
+ },
197
+ {
198
+ label: "Sectoral Scope",
199
+ value: project.sectoralScope || 'Project'
200
+ },
201
+
202
+
203
+ ];
204
+
205
+ popupContent = renderTooltipHtml({
206
+ title: project.name,
207
+ subTitle: project.sectoralScope || 'Project',
208
+ items: tooltipData,
209
+ link: false
210
+ });
211
+ }
212
+
213
+ // Create popup
214
+ const popup = new mapboxgl.Popup({
215
+ offset: 25,
216
+ closeButton: true,
217
+ closeOnClick: false
218
+ }).setHTML(popupContent);
219
+
220
+ // Ensure coordinates are valid numbers
221
+ const lng = Number(project.longitude);
222
+ const lat = Number(project.latitude);
223
+
224
+ console.log(`πŸ“ [SIMPLE GLOBE] Marker ${index} coordinates:`, { lng, lat });
225
+
226
+ // Validate coordinates
227
+ if (isNaN(lng) || isNaN(lat) || lng < -180 || lng > 180 || lat < -90 || lat > 90) {
228
+ console.error(`❌ [SIMPLE GLOBE] Invalid coordinates for project ${index}:`, { lng, lat });
229
+ return;
230
+ }
231
+
232
+ // Add coordinates to bounds
233
+ bounds.extend([lng, lat]);
234
+ hasValidCoordinates = true;
235
+
236
+ // Add marker to map with proper coordinate order [lng, lat]
237
+ const marker = new mapboxgl.Marker(el)
238
+ .setLngLat([lng, lat])
239
+ .setPopup(popup)
240
+ .addTo(map.current);
241
+
242
+ // Add click handler
243
+ el.addEventListener('click', () => {
244
+ console.log('πŸ“ [SIMPLE GLOBE] Marker clicked:', project);
245
+ onProjectClick(project);
246
+ });
247
+
248
+ console.log(`βœ… [SIMPLE GLOBE] Marker ${index} added at:`, [lng, lat]);
249
+ });
250
+
251
+ // Fit map to show all markers if we have valid coordinates
252
+ if (hasValidCoordinates && !bounds.isEmpty()) {
253
+ console.log('πŸ—ΊοΈ [SIMPLE GLOBE] Fitting map to bounds:', bounds);
254
+ map.current.fitBounds(bounds, {
255
+ padding: { top: 20, bottom: 20, left: 20, right: 20 },
256
+ maxZoom: 6,
257
+ duration: 1000
258
+ });
259
+ boundsRef.current = bounds;
260
+ } else {
261
+ boundsRef.current = null;
262
+ }
263
+ });
264
+
265
+ return () => {
266
+ if (map.current) {
267
+ map.current.remove();
268
+ map.current = null;
269
+ }
270
+ };
271
+ }, [projects, onProjectClick, mapConfig]);
272
+
273
+ const zoomIn = () => map.current && map.current.zoomIn();
274
+ const zoomOut = () => map.current && map.current.zoomOut();
275
+ const recenter = () => {
276
+ if (!map.current) return;
277
+ if (boundsRef.current && !boundsRef.current.isEmpty()) {
278
+ map.current.fitBounds(boundsRef.current, {
279
+ padding: { top: 20, bottom: 20, left: 20, right: 20 },
280
+ maxZoom: 6,
281
+ duration: 800,
282
+ });
283
+ return;
284
+ }
285
+ map.current.easeTo({ center: [0, 0], zoom: 3, duration: 800 });
286
+ };
287
+ const pitchUp = () => {
288
+ if (!map.current) return;
289
+ const next = Math.min((map.current.getPitch?.() || 0) + 10, 85);
290
+ map.current.setPitch(next);
291
+ };
292
+ const pitchDown = () => {
293
+ if (!map.current) return;
294
+ const next = Math.max((map.current.getPitch?.() || 0) - 10, 0);
295
+ map.current.setPitch(next);
296
+ };
297
+
298
+ return (
299
+ <Style>
300
+ <div
301
+ className="daf-simple-globe-container"
302
+ style={{
303
+ width: '100%',
304
+ height: '100%',
305
+ overflow: 'hidden',
306
+ position: 'relative'
307
+ }}
308
+ >
309
+ <div
310
+ ref={mapContainer}
311
+ className="daf-globe-map-container"
312
+ style={{
313
+ width: '100%',
314
+ height: '100%',
315
+ overflow: 'hidden',
316
+ position: 'relative'
317
+ }}
318
+ />
319
+ <div className="map-control-buttons">
320
+ <button type="button" aria-label="Zoom in" onClick={zoomIn}>+</button>
321
+ <button type="button" aria-label="Zoom out" onClick={zoomOut}>βˆ’</button>
322
+ <button type="button" aria-label="Recenter" onClick={recenter}>β—Ž</button>
323
+ <button type="button" aria-label="Pitch up" onClick={pitchUp}>^</button>
324
+ <button type="button" aria-label="Pitch down" onClick={pitchDown}>Λ…</button>
325
+ </div>
326
+ </div>
327
+ </Style>
328
+ );
329
+ };
330
+
331
+ export default SimpleGlobe;