feat: Add geocoding functionality and implement pan/zoom for the family tree view.
This commit is contained in:
parent
216385b638
commit
bf5a0876b6
3 changed files with 119 additions and 1 deletions
|
|
@ -116,7 +116,10 @@
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Sted</label>
|
<label>Sted</label>
|
||||||
<input type="text" id="location" placeholder="F.eks. Kristiansand">
|
<div style="display: flex; gap: 10px;">
|
||||||
|
<input type="text" id="location" placeholder="F.eks. Kristiansand">
|
||||||
|
<button type="button" id="geocodeBtn" class="icon-btn" title="Finn koordinater">📍</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
|
|
|
||||||
94
script.js
94
script.js
|
|
@ -196,6 +196,25 @@ memberForm.addEventListener('submit', (e) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Geocoding
|
||||||
|
document.getElementById('geocodeBtn').addEventListener('click', () => {
|
||||||
|
const location = document.getElementById('location').value;
|
||||||
|
if (!location) return;
|
||||||
|
|
||||||
|
const geocoder = new google.maps.Geocoder();
|
||||||
|
geocoder.geocode({ 'address': location }, (results, status) => {
|
||||||
|
if (status === 'OK') {
|
||||||
|
const lat = results[0].geometry.location.lat();
|
||||||
|
const lng = results[0].geometry.location.lng();
|
||||||
|
|
||||||
|
document.getElementById('lat').value = lat.toFixed(6);
|
||||||
|
document.getElementById('lng').value = lng.toFixed(6);
|
||||||
|
} else {
|
||||||
|
alert('Kunne ikke finne koordinater for dette stedet: ' + status);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// --- Feature: Family Tree ---
|
// --- Feature: Family Tree ---
|
||||||
|
|
||||||
function renderTree() {
|
function renderTree() {
|
||||||
|
|
@ -490,3 +509,78 @@ function renderHistory() {
|
||||||
list.appendChild(item);
|
list.appendChild(item);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Feature: Family Tree Navigation (Pan/Zoom) ---
|
||||||
|
|
||||||
|
let treeState = {
|
||||||
|
scale: 1,
|
||||||
|
panning: false,
|
||||||
|
pointX: 0,
|
||||||
|
pointY: 0,
|
||||||
|
startX: 0,
|
||||||
|
startY: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
function setupPanZoom() {
|
||||||
|
const treeTab = document.getElementById('tree-tab');
|
||||||
|
const treeContainer = document.getElementById('tree-container');
|
||||||
|
|
||||||
|
// Zoom (Wheel)
|
||||||
|
treeTab.addEventListener('wheel', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const xs = (e.clientX - treeState.pointX) / treeState.scale;
|
||||||
|
const ys = (e.clientY - treeState.pointY) / treeState.scale;
|
||||||
|
|
||||||
|
const delta = -e.deltaY;
|
||||||
|
(delta > 0) ? (treeState.scale *= 1.1) : (treeState.scale /= 1.1);
|
||||||
|
|
||||||
|
// Limit zoom
|
||||||
|
treeState.scale = Math.min(Math.max(0.1, treeState.scale), 5);
|
||||||
|
|
||||||
|
treeState.pointX = e.clientX - xs * treeState.scale;
|
||||||
|
treeState.pointY = e.clientY - ys * treeState.scale;
|
||||||
|
|
||||||
|
setTransform();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Pan (Mouse)
|
||||||
|
treeTab.addEventListener('mousedown', (e) => {
|
||||||
|
if (e.target.closest('.family-card') || e.target.closest('.fab')) return; // Don't drag if clicking card/button
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
treeState.startX = e.clientX - treeState.pointX;
|
||||||
|
treeState.startY = e.clientY - treeState.pointY;
|
||||||
|
treeState.panning = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
treeTab.addEventListener('mousemove', (e) => {
|
||||||
|
if (!treeState.panning) return;
|
||||||
|
e.preventDefault();
|
||||||
|
treeState.pointX = e.clientX - treeState.startX;
|
||||||
|
treeState.pointY = e.clientY - treeState.startY;
|
||||||
|
setTransform();
|
||||||
|
});
|
||||||
|
|
||||||
|
treeTab.addEventListener('mouseup', () => {
|
||||||
|
treeState.panning = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
treeTab.addEventListener('mouseleave', () => {
|
||||||
|
treeState.panning = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
function setTransform() {
|
||||||
|
treeContainer.style.transform = `translate(${treeState.pointX}px, ${treeState.pointY}px) scale(${treeState.scale})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial center (approx)
|
||||||
|
treeState.pointX = (window.innerWidth - 1000) / 2; // Assuming 1000px content width
|
||||||
|
treeState.pointY = 50;
|
||||||
|
setTransform();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize PanZoom when DOM is ready
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
setupPanZoom();
|
||||||
|
});
|
||||||
|
|
|
||||||
21
style.css
21
style.css
|
|
@ -377,3 +377,24 @@ input[type="radio"] {
|
||||||
width: auto;
|
width: auto;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Pan and Zoom Styles */
|
||||||
|
#tree-tab {
|
||||||
|
overflow: hidden; /* Hide scrollbars, we use custom pan */
|
||||||
|
position: relative;
|
||||||
|
cursor: grab;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tree-tab:active {
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
|
||||||
|
#tree-container {
|
||||||
|
transform-origin: 0 0; /* Zoom from top-left */
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
/* Ensure it has size */
|
||||||
|
min-width: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue