diff --git a/index.html b/index.html
index 7372f09..6a90f53 100644
--- a/index.html
+++ b/index.html
@@ -116,7 +116,10 @@
diff --git a/script.js b/script.js
index 21a7bd4..49be7a6 100644
--- a/script.js
+++ b/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 ---
function renderTree() {
@@ -490,3 +509,78 @@ function renderHistory() {
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();
+});
diff --git a/style.css b/style.css
index 0bd033d..27ad68b 100644
--- a/style.css
+++ b/style.css
@@ -376,4 +376,25 @@ textarea:focus {
input[type="radio"] {
width: auto;
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%;
}
\ No newline at end of file