feat : add right sidebar stating the height , depth

This commit is contained in:
lokeshramchand-ctrl 2026-01-30 17:58:50 +00:00
parent 8927422283
commit f051581171
3 changed files with 173 additions and 19 deletions

36
package-lock.json generated
View file

@ -1842,12 +1842,16 @@
"dev": true
},
"node_modules/cookie": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
"integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
"integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/cross-spawn": {
@ -2569,9 +2573,9 @@
}
},
"node_modules/lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
"version": "4.17.23",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz",
"integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==",
"license": "MIT"
},
"node_modules/lodash.assign": {
@ -3035,9 +3039,9 @@
}
},
"node_modules/react-router": {
"version": "7.6.2",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.2.tgz",
"integrity": "sha512-U7Nv3y+bMimgWjhlT5CRdzHPu2/KVmqPwKUCChW8en5P3znxUqwlYFlbmyj8Rgp1SF6zs5X4+77kBVknkg6a0w==",
"version": "7.13.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.13.0.tgz",
"integrity": "sha512-PZgus8ETambRT17BUm/LL8lX3Of+oiLaPuVTRH3l1eLvSPpKO3AvhAEb5N7ihAFZQrYDqkvvWfFh9p0z9VsjLw==",
"license": "MIT",
"dependencies": {
"cookie": "^1.0.1",
@ -3057,12 +3061,12 @@
}
},
"node_modules/react-router-dom": {
"version": "7.6.2",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.6.2.tgz",
"integrity": "sha512-Q8zb6VlTbdYKK5JJBLQEN06oTUa/RAbG/oQS1auK1I0TbJOXktqm+QENEVJU6QvWynlXPRBXI3fiOQcSEA78rA==",
"version": "7.13.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.13.0.tgz",
"integrity": "sha512-5CO/l5Yahi2SKC6rGZ+HDEjpjkGaG/ncEP7eWFTvFxbHP8yeeI0PxTDjimtpXYlR3b3i9/WIL4VJttPrESIf2g==",
"license": "MIT",
"dependencies": {
"react-router": "7.6.2"
"react-router": "7.13.0"
},
"engines": {
"node": ">=20.0.0"
@ -3199,9 +3203,9 @@
}
},
"node_modules/set-cookie-parser": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
"integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
"version": "2.7.2",
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
"integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
"license": "MIT"
},
"node_modules/shebang-command": {

View file

@ -1,4 +1,4 @@
import './App.css'
import './App.css';
import { useState } from 'react';
import OpenLayerMap from './components/OpenLayerMap';
import OpenLayerEsri from './components/OpenLayerEsri';

View file

@ -3,7 +3,7 @@ import { Map, View } from 'ol';
import 'ol/ol.css';
import TileLayer from 'ol/layer/Tile';
import XYZ from 'ol/source/XYZ';
import { fromLonLat } from 'ol/proj';
import { fromLonLat, toLonLat } from 'ol/proj';
import { Coordinate } from 'ol/coordinate';
import VectorLayer from 'ol/layer/Vector';
import { layerConfigs } from './Methods/Layers';
@ -11,6 +11,7 @@ import { loadAndRenderGeoJsonLayer } from './Methods/GeoJson_Load';
import { addMarker } from './Methods/Marker';
import gsap from 'gsap';
import { useNavigate } from 'react-router-dom';
import { easeOut } from 'ol/easing';
// --- THEME CONFIGURATION ---
const themes = {
@ -82,6 +83,12 @@ const MapView: React.FC<MapViewProps> = ({ initialAddressData, initialTerm }) =>
Object.fromEntries(layerConfigs.map(layer => [layer.topic, true]))
);
const [clickedCoordinate, setClickedCoordinate] = useState<Coordinate>();
const [mapStats, setMapStats] = useState({
zoom: 2,
center: [0, 0] as Coordinate,
rotation: 0,
size: { width: 0, height: 0 }
});
// -- REFS --
const mapDivRef = useRef<HTMLDivElement>(null);
@ -130,6 +137,20 @@ const MapView: React.FC<MapViewProps> = ({ initialAddressData, initialTerm }) =>
mapRef.current = map;
map.on('click', (e) => setClickedCoordinate(e.coordinate));
map.on('moveend', () => {
const view = map.getView();
const center = view.getCenter() ?? [0, 0];
const zoom = view.getZoom() ?? 0;
const rotation = view.getRotation() ?? 0;
const size = map.getSize();
setMapStats({
center,
zoom,
rotation,
size: { width: size?.[0] ?? 0, height: size?.[1] ?? 0 }
});
});
}
}, []);
@ -150,7 +171,7 @@ const MapView: React.FC<MapViewProps> = ({ initialAddressData, initialTerm }) =>
const lon = parseFloat(initialAddressData[0].lon);
const coords = fromLonLat([lon, lat]);
mapRef.current!.getView().animate({ center: coords, zoom: 14, duration: 2500, ease: (t) => Math.pow(t, 2) });
mapRef.current!.getView().animate({ center: coords, zoom: 14, duration: 2500, easing: easeOut });
markerLayerRef.current = addMarker(coords, mapRef.current!, markerLayerRef.current);
layerConfigs.forEach((layerConfig) => {
@ -170,6 +191,10 @@ const MapView: React.FC<MapViewProps> = ({ initialAddressData, initialTerm }) =>
};
const formatLayerName = (topic: string) => topic.replace(/_/g, ' ').replace(/SD$/, '').trim();
const formatNumber = (value: number, digits = 2) => Number.isFinite(value) ? value.toFixed(digits) : '—';
const centerLonLat = toLonLat(mapStats.center);
const clickedLonLat = clickedCoordinate ? toLonLat(clickedCoordinate) : null;
const activeLayerCount = Object.values(layerVisibility).filter(Boolean).length;
// -- STYLES --
const styles = {
@ -245,6 +270,75 @@ const MapView: React.FC<MapViewProps> = ({ initialAddressData, initialTerm }) =>
borderRadius: '8px', border: `1px solid ${currentTheme.border}`,
boxShadow: currentTheme.shadow,
transition: 'background 0.3s, color 0.3s'
},
rightSidebar: {
position: 'absolute' as const,
top: '92px',
right: '24px',
width: '320px',
maxHeight: 'calc(100% - 140px)',
display: 'flex',
flexDirection: 'column' as const,
gap: '12px',
pointerEvents: 'auto' as const,
zIndex: 1100
},
sidebarCard: {
background: currentTheme.glass,
backdropFilter: 'blur(20px)',
WebkitBackdropFilter: 'blur(20px)',
border: `1px solid ${currentTheme.border}`,
borderRadius: '16px',
padding: '16px',
boxShadow: currentTheme.shadow,
transition: 'all 0.3s ease'
},
sidebarTitle: {
fontSize: '11px',
fontWeight: 700,
letterSpacing: '1px',
color: currentTheme.textMuted,
textTransform: 'uppercase' as const,
marginBottom: '12px'
},
statRow: {
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: '6px 0',
borderBottom: `1px dashed ${currentTheme.border}`
},
statLabel: {
fontSize: '12px',
color: currentTheme.textMuted
},
statValue: {
fontSize: '12px',
fontWeight: 600,
color: currentTheme.textMain
},
layerMetaRow: {
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
gap: '12px',
padding: '8px 0',
borderBottom: `1px dashed ${currentTheme.border}`
},
layerMetaName: {
fontSize: '12px',
color: currentTheme.textMain,
display: 'flex',
alignItems: 'center',
gap: '8px'
},
layerMetaBadge: {
fontSize: '10px',
fontWeight: 600,
padding: '2px 8px',
borderRadius: '999px',
border: `1px solid ${currentTheme.border}`,
color: currentTheme.textMuted
}
};
@ -341,6 +435,62 @@ const MapView: React.FC<MapViewProps> = ({ initialAddressData, initialTerm }) =>
</div>
)}
{/* RIGHT SIDEBAR */}
<div className="floating-ui" style={styles.rightSidebar}>
<div style={styles.sidebarCard}>
<div style={styles.sidebarTitle}>General Values</div>
<div style={styles.statRow}>
<span style={styles.statLabel}>Height</span>
<span style={styles.statValue}>{mapStats.size.height ? `${mapStats.size.height}px` : '—'}</span>
</div>
<div style={styles.statRow}>
<span style={styles.statLabel}>Depth</span>
<span style={styles.statValue}>{mapStats.size.width ? `${mapStats.size.width}px` : '—'}</span>
</div>
<div style={styles.statRow}>
<span style={styles.statLabel}>Zoom</span>
<span style={styles.statValue}>{formatNumber(mapStats.zoom, 2)}</span>
</div>
<div style={styles.statRow}>
<span style={styles.statLabel}>Center</span>
<span style={styles.statValue}>{formatNumber(centerLonLat[1], 4)} N / {formatNumber(centerLonLat[0], 4)} E</span>
</div>
<div style={styles.statRow}>
<span style={styles.statLabel}>Last Click</span>
<span style={styles.statValue}>{clickedLonLat ? `${formatNumber(clickedLonLat[1], 4)} N / ${formatNumber(clickedLonLat[0], 4)} E` : '—'}</span>
</div>
<div style={{ ...styles.statRow, borderBottom: 'none' }}>
<span style={styles.statLabel}>Layers On</span>
<span style={styles.statValue}>{activeLayerCount}/{layerConfigs.length}</span>
</div>
</div>
<div style={styles.sidebarCard}>
<div style={styles.sidebarTitle}>Layer Values</div>
<div className="custom-scrollbar" style={{ maxHeight: '36vh', overflowY: 'auto', paddingRight: '6px' }}>
{layerConfigs.map((layer) => {
const isActive = layerVisibility[layer.topic];
const layerColor = extractColorFromStyle(layer.style);
return (
<div key={layer.topic} style={{ ...styles.layerMetaRow, borderBottom: `1px dashed ${currentTheme.border}` }}>
<div style={styles.layerMetaName}>
<span style={{ width: '8px', height: '8px', borderRadius: '50%', backgroundColor: layerColor, boxShadow: `0 0 8px ${layerColor}`, opacity: isActive ? 1 : 0.3 }} />
{formatLayerName(layer.topic)}
</div>
<div style={{ display: 'flex', gap: '8px' }}>
<span style={styles.layerMetaBadge}>{layer.radius}m</span>
<span style={{ ...styles.layerMetaBadge, color: isActive ? currentTheme.textMain : currentTheme.textMuted }}>
{isActive ? 'On' : 'Off'}
</span>
</div>
</div>
);
})}
</div>
</div>
</div>
</div>
</div>
);