Behebe Dashboard-, Loesch- und Infrastruktur-Issues
closes #20 closes #19 closes #18 closes #17
This commit is contained in:
@@ -29,8 +29,14 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
let markerX = 0;
|
||||
let markerY = 0;
|
||||
let dragging = false;
|
||||
let panning = false;
|
||||
let panStart = null;
|
||||
let dragOffsetX = 0;
|
||||
let dragOffsetY = 0;
|
||||
let viewX = 0;
|
||||
let viewY = 0;
|
||||
let viewWidth = DEFAULT_PLAN_SIZE.width;
|
||||
let viewHeight = DEFAULT_PLAN_SIZE.height;
|
||||
|
||||
const activeMarker = document.createElementNS(SVG_NS, 'rect');
|
||||
activeMarker.classList.add('active-marker');
|
||||
@@ -45,7 +51,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
const planSize = { ...DEFAULT_PLAN_SIZE };
|
||||
const updateOverlayViewBox = () => {
|
||||
overlay.setAttribute('viewBox', `0 0 ${planSize.width} ${planSize.height}`);
|
||||
overlay.setAttribute('viewBox', `${viewX} ${viewY} ${viewWidth} ${viewHeight}`);
|
||||
};
|
||||
|
||||
const updatePositionLabel = (x, y) => {
|
||||
@@ -72,17 +78,57 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
};
|
||||
|
||||
const toOverlayPoint = (clientX, clientY) => {
|
||||
const pt = overlay.createSVGPoint();
|
||||
pt.x = clientX;
|
||||
pt.y = clientY;
|
||||
const ctm = overlay.getScreenCTM();
|
||||
if (!ctm) {
|
||||
const rect = overlay.getBoundingClientRect();
|
||||
if (rect.width <= 0 || rect.height <= 0) {
|
||||
return null;
|
||||
}
|
||||
const transformed = pt.matrixTransform(ctm.inverse());
|
||||
const ratioX = (clientX - rect.left) / rect.width;
|
||||
const ratioY = (clientY - rect.top) / rect.height;
|
||||
const transformed = {
|
||||
x: viewX + (ratioX * viewWidth),
|
||||
y: viewY + (ratioY * viewHeight)
|
||||
};
|
||||
return { x: transformed.x, y: transformed.y };
|
||||
};
|
||||
|
||||
const clampView = () => {
|
||||
const minWidth = Math.max(30, planSize.width * 0.1);
|
||||
const minHeight = Math.max(30, planSize.height * 0.1);
|
||||
viewWidth = clamp(viewWidth, minWidth, planSize.width);
|
||||
viewHeight = clamp(viewHeight, minHeight, planSize.height);
|
||||
viewX = clamp(viewX, 0, Math.max(0, planSize.width - viewWidth));
|
||||
viewY = clamp(viewY, 0, Math.max(0, planSize.height - viewHeight));
|
||||
};
|
||||
|
||||
const applyView = () => {
|
||||
clampView();
|
||||
updateOverlayViewBox();
|
||||
};
|
||||
|
||||
const zoomAt = (clientX, clientY, factor) => {
|
||||
const point = toOverlayPoint(clientX, clientY);
|
||||
if (!point) {
|
||||
return;
|
||||
}
|
||||
const ratioX = (point.x - viewX) / viewWidth;
|
||||
const ratioY = (point.y - viewY) / viewHeight;
|
||||
const nextWidth = viewWidth * factor;
|
||||
const nextHeight = viewHeight * factor;
|
||||
viewX = point.x - (ratioX * nextWidth);
|
||||
viewY = point.y - (ratioY * nextHeight);
|
||||
viewWidth = nextWidth;
|
||||
viewHeight = nextHeight;
|
||||
applyView();
|
||||
};
|
||||
|
||||
const resetView = () => {
|
||||
viewX = 0;
|
||||
viewY = 0;
|
||||
viewWidth = planSize.width;
|
||||
viewHeight = planSize.height;
|
||||
applyView();
|
||||
};
|
||||
|
||||
const updateFromInputs = () => {
|
||||
setMarkerPosition(Number(xField.value) || 0, Number(yField.value) || 0);
|
||||
};
|
||||
@@ -195,6 +241,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const panelPlacementFields = document.getElementById('panel-placement-fields');
|
||||
const panelFloorPlanGroup = document.getElementById('panel-floor-plan-group');
|
||||
const panelFloorMissingHint = document.getElementById('panel-floor-missing-hint');
|
||||
const outletBindPatchpanelSelect = document.getElementById('outlet-bind-patchpanel-port-id');
|
||||
|
||||
const buildingOptions = panelBuildingSelect ? Array.from(panelBuildingSelect.options).filter((option) => option.value !== '') : [];
|
||||
const floorOptions = panelFloorSelect ? Array.from(panelFloorSelect.options).filter((option) => option.value !== '') : [];
|
||||
@@ -208,6 +255,32 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
return Number(roomOption?.dataset?.floorId || 0);
|
||||
};
|
||||
|
||||
const filterPatchpanelBindOptions = () => {
|
||||
if (!outletBindPatchpanelSelect) {
|
||||
return;
|
||||
}
|
||||
const currentFloorId = getCurrentFloorId();
|
||||
const options = Array.from(outletBindPatchpanelSelect.options).filter((option) => option.value !== '');
|
||||
let firstMatch = '';
|
||||
let selectedStillVisible = false;
|
||||
|
||||
options.forEach((option) => {
|
||||
const optionFloorId = Number(option.dataset.floorId || 0);
|
||||
const matchesFloor = !currentFloorId || optionFloorId === currentFloorId;
|
||||
option.hidden = !matchesFloor;
|
||||
if (matchesFloor && !option.disabled && !firstMatch) {
|
||||
firstMatch = option.value;
|
||||
}
|
||||
if (matchesFloor && option.selected) {
|
||||
selectedStillVisible = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (!selectedStillVisible && firstMatch && !outletBindPatchpanelSelect.value) {
|
||||
outletBindPatchpanelSelect.value = firstMatch;
|
||||
}
|
||||
};
|
||||
|
||||
const renderReferenceMarkers = () => {
|
||||
clearRoomHighlight();
|
||||
clearReferenceMarkers();
|
||||
@@ -256,9 +329,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
floorPlanSvg.hidden = true;
|
||||
planSize.width = DEFAULT_PLAN_SIZE.width;
|
||||
planSize.height = DEFAULT_PLAN_SIZE.height;
|
||||
updateOverlayViewBox();
|
||||
resetView();
|
||||
}
|
||||
renderReferenceMarkers();
|
||||
filterPatchpanelBindOptions();
|
||||
};
|
||||
|
||||
if (floorPlanSvg) {
|
||||
@@ -291,7 +365,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (parts.length === 4 && parts.every((value) => Number.isFinite(value))) {
|
||||
planSize.width = Math.max(1, parts[2]);
|
||||
planSize.height = Math.max(1, parts[3]);
|
||||
updateOverlayViewBox();
|
||||
resetView();
|
||||
renderReferenceMarkers();
|
||||
updateFromInputs();
|
||||
return;
|
||||
@@ -307,13 +381,13 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
planSize.width = DEFAULT_PLAN_SIZE.width;
|
||||
planSize.height = DEFAULT_PLAN_SIZE.height;
|
||||
}
|
||||
updateOverlayViewBox();
|
||||
resetView();
|
||||
renderReferenceMarkers();
|
||||
updateFromInputs();
|
||||
} catch (error) {
|
||||
planSize.width = DEFAULT_PLAN_SIZE.width;
|
||||
planSize.height = DEFAULT_PLAN_SIZE.height;
|
||||
updateOverlayViewBox();
|
||||
resetView();
|
||||
renderReferenceMarkers();
|
||||
updateFromInputs();
|
||||
}
|
||||
@@ -382,6 +456,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
activeMarker.addEventListener('pointerdown', (event) => {
|
||||
event.preventDefault();
|
||||
dragging = true;
|
||||
panning = false;
|
||||
const point = toOverlayPoint(event.clientX, event.clientY);
|
||||
if (!point) {
|
||||
return;
|
||||
@@ -403,12 +478,18 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
});
|
||||
|
||||
const stopDrag = (event) => {
|
||||
if (!dragging) {
|
||||
return;
|
||||
if (dragging) {
|
||||
dragging = false;
|
||||
if (activeMarker.hasPointerCapture(event.pointerId)) {
|
||||
activeMarker.releasePointerCapture(event.pointerId);
|
||||
}
|
||||
}
|
||||
dragging = false;
|
||||
if (activeMarker.hasPointerCapture(event.pointerId)) {
|
||||
activeMarker.releasePointerCapture(event.pointerId);
|
||||
if (panning) {
|
||||
panning = false;
|
||||
panStart = null;
|
||||
if (overlay.hasPointerCapture(event.pointerId)) {
|
||||
overlay.releasePointerCapture(event.pointerId);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -417,6 +498,19 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
});
|
||||
|
||||
overlay.addEventListener('pointerdown', (event) => {
|
||||
if (event.shiftKey || event.button === 1) {
|
||||
event.preventDefault();
|
||||
panning = true;
|
||||
dragging = false;
|
||||
panStart = {
|
||||
clientX: event.clientX,
|
||||
clientY: event.clientY,
|
||||
viewX,
|
||||
viewY
|
||||
};
|
||||
overlay.setPointerCapture(event.pointerId);
|
||||
return;
|
||||
}
|
||||
if (event.target !== overlay) {
|
||||
return;
|
||||
}
|
||||
@@ -427,6 +521,32 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
setMarkerPosition(point.x - markerWidth / 2, point.y - markerHeight / 2);
|
||||
});
|
||||
|
||||
overlay.addEventListener('pointermove', (event) => {
|
||||
if (!panning || !panStart) {
|
||||
return;
|
||||
}
|
||||
const rect = overlay.getBoundingClientRect();
|
||||
if (rect.width <= 0 || rect.height <= 0) {
|
||||
return;
|
||||
}
|
||||
const scaleX = viewWidth / rect.width;
|
||||
const scaleY = viewHeight / rect.height;
|
||||
const dx = (event.clientX - panStart.clientX) * scaleX;
|
||||
const dy = (event.clientY - panStart.clientY) * scaleY;
|
||||
viewX = panStart.viewX - dx;
|
||||
viewY = panStart.viewY - dy;
|
||||
applyView();
|
||||
});
|
||||
|
||||
overlay.addEventListener('pointerup', stopDrag);
|
||||
overlay.addEventListener('pointercancel', stopDrag);
|
||||
|
||||
overlay.addEventListener('wheel', (event) => {
|
||||
event.preventDefault();
|
||||
const factor = event.deltaY < 0 ? 0.9 : 1.1;
|
||||
zoomAt(event.clientX, event.clientY, factor);
|
||||
}, { passive: false });
|
||||
|
||||
[xField, yField].forEach((input) => {
|
||||
input.addEventListener('input', () => {
|
||||
updateFromInputs();
|
||||
@@ -464,8 +584,26 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
});
|
||||
}
|
||||
|
||||
document.querySelectorAll('[data-floor-plan-zoom]').forEach((button) => {
|
||||
button.addEventListener('click', () => {
|
||||
const action = button.getAttribute('data-floor-plan-zoom');
|
||||
if (action === 'in') {
|
||||
const rect = overlay.getBoundingClientRect();
|
||||
zoomAt(rect.left + (rect.width / 2), rect.top + (rect.height / 2), 0.85);
|
||||
return;
|
||||
}
|
||||
if (action === 'out') {
|
||||
const rect = overlay.getBoundingClientRect();
|
||||
zoomAt(rect.left + (rect.width / 2), rect.top + (rect.height / 2), 1.15);
|
||||
return;
|
||||
}
|
||||
resetView();
|
||||
});
|
||||
});
|
||||
|
||||
updateOverlayViewBox();
|
||||
updateFromInputs();
|
||||
filterPatchpanelBindOptions();
|
||||
|
||||
if (panelLocationSelect) {
|
||||
filterBuildingOptions();
|
||||
|
||||
Reference in New Issue
Block a user