238 lines
6.0 KiB
JavaScript
238 lines
6.0 KiB
JavaScript
(() => {
|
|
const svgElement = document.querySelector('#device-svg');
|
|
if (!svgElement) {
|
|
return;
|
|
}
|
|
|
|
const DEVICE_TYPE_ID = Number(svgElement.dataset.deviceTypeId || 0);
|
|
const API_LOAD_PORTS = '/api/device_type_ports.php?action=load';
|
|
const API_SAVE_PORTS = '/api/device_type_ports.php?action=save';
|
|
const DEFAULT_PORT_TYPE_ID = null;
|
|
|
|
let ports = [];
|
|
let selectedPortId = null;
|
|
let isDragging = false;
|
|
let dragOffset = { x: 0, y: 0 };
|
|
|
|
bindSvgEvents();
|
|
loadPorts();
|
|
|
|
function bindSvgEvents() {
|
|
svgElement.addEventListener('click', onSvgClick);
|
|
svgElement.addEventListener('mousemove', onSvgMouseMove);
|
|
svgElement.addEventListener('mouseup', onSvgMouseUp);
|
|
|
|
const saveButton = document.querySelector('[data-save-svg-ports]');
|
|
if (saveButton) {
|
|
saveButton.addEventListener('click', (event) => {
|
|
event.preventDefault();
|
|
savePorts();
|
|
});
|
|
}
|
|
}
|
|
|
|
function onSvgClick(event) {
|
|
if (event.target.classList.contains('port-point')) {
|
|
selectPort(event.target.dataset.id);
|
|
return;
|
|
}
|
|
|
|
// New ports are only created while SHIFT is held.
|
|
if (!event.shiftKey) {
|
|
return;
|
|
}
|
|
|
|
const point = getSvgCoordinates(event);
|
|
createPort(point.x, point.y);
|
|
}
|
|
|
|
function createPort(x, y) {
|
|
const id = generateTempId();
|
|
const port = {
|
|
id,
|
|
name: `Port ${ports.length + 1}`,
|
|
port_type_id: DEFAULT_PORT_TYPE_ID,
|
|
x,
|
|
y,
|
|
metadata: null
|
|
};
|
|
|
|
ports.push(port);
|
|
renderPort(port);
|
|
selectPort(id);
|
|
}
|
|
|
|
function renderPort(port) {
|
|
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
|
circle.setAttribute('cx', port.x);
|
|
circle.setAttribute('cy', port.y);
|
|
circle.setAttribute('r', 6);
|
|
circle.classList.add('port-point');
|
|
circle.dataset.id = port.id;
|
|
|
|
circle.addEventListener('mousedown', (e) => {
|
|
startDrag(e, port.id);
|
|
e.stopPropagation();
|
|
});
|
|
|
|
svgElement.appendChild(circle);
|
|
}
|
|
|
|
function rerenderPorts() {
|
|
svgElement.querySelectorAll('.port-point').forEach((node) => node.remove());
|
|
ports.forEach(renderPort);
|
|
if (selectedPortId !== null) {
|
|
selectPort(selectedPortId);
|
|
}
|
|
}
|
|
|
|
function selectPort(id) {
|
|
selectedPortId = id;
|
|
|
|
document.querySelectorAll('.port-point').forEach((el) => {
|
|
el.classList.toggle('selected', el.dataset.id === String(id));
|
|
});
|
|
|
|
const selected = getPortById(id);
|
|
fillSidebar(selected);
|
|
}
|
|
|
|
function fillSidebar(port) {
|
|
const nameField = document.querySelector('[data-port-name]');
|
|
const typeField = document.querySelector('[data-port-type-id]');
|
|
const xField = document.querySelector('[data-port-x]');
|
|
const yField = document.querySelector('[data-port-y]');
|
|
|
|
if (nameField) nameField.value = port?.name || '';
|
|
if (typeField) typeField.value = port?.port_type_id || '';
|
|
if (xField) xField.value = port ? Math.round(port.x) : '';
|
|
if (yField) yField.value = port ? Math.round(port.y) : '';
|
|
}
|
|
|
|
function resetSidebar() {
|
|
fillSidebar(null);
|
|
}
|
|
|
|
function startDrag(event, portId) {
|
|
const port = getPortById(portId);
|
|
if (!port) return;
|
|
|
|
isDragging = true;
|
|
selectedPortId = portId;
|
|
|
|
const point = getSvgCoordinates(event);
|
|
dragOffset.x = port.x - point.x;
|
|
dragOffset.y = port.y - point.y;
|
|
}
|
|
|
|
function onSvgMouseMove(event) {
|
|
if (!isDragging || !selectedPortId) return;
|
|
|
|
const port = getPortById(selectedPortId);
|
|
if (!port) return;
|
|
|
|
const point = getSvgCoordinates(event);
|
|
port.x = point.x + dragOffset.x;
|
|
port.y = point.y + dragOffset.y;
|
|
|
|
rerenderPorts();
|
|
}
|
|
|
|
function onSvgMouseUp() {
|
|
isDragging = false;
|
|
}
|
|
|
|
function deleteSelectedPort() {
|
|
if (!selectedPortId) {
|
|
return;
|
|
}
|
|
|
|
if (!confirm('Ausgewaehlten Port loeschen?')) {
|
|
return;
|
|
}
|
|
|
|
ports = ports.filter((port) => String(port.id) !== String(selectedPortId));
|
|
selectedPortId = null;
|
|
rerenderPorts();
|
|
resetSidebar();
|
|
}
|
|
|
|
function loadPorts() {
|
|
if (!DEVICE_TYPE_ID) {
|
|
console.warn('SVG Editor: DEVICE_TYPE_ID fehlt auf #device-svg');
|
|
return;
|
|
}
|
|
|
|
fetch(`${API_LOAD_PORTS}&device_type_id=${DEVICE_TYPE_ID}`)
|
|
.then((res) => res.json())
|
|
.then((data) => {
|
|
if (!Array.isArray(data)) {
|
|
throw new Error('Antwortformat ungueltig');
|
|
}
|
|
|
|
ports = data
|
|
.filter((entry) => entry && typeof entry === 'object')
|
|
.map((entry) => ({
|
|
id: entry.id,
|
|
name: String(entry.name || ''),
|
|
port_type_id: entry.port_type_id ? Number(entry.port_type_id) : null,
|
|
x: Number(entry.x || 0),
|
|
y: Number(entry.y || 0),
|
|
metadata: entry.metadata || null
|
|
}));
|
|
|
|
rerenderPorts();
|
|
})
|
|
.catch((err) => {
|
|
console.error('Fehler beim Laden der Ports', err);
|
|
});
|
|
}
|
|
|
|
function savePorts() {
|
|
if (!DEVICE_TYPE_ID) {
|
|
return;
|
|
}
|
|
|
|
fetch(API_SAVE_PORTS, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
device_type_id: DEVICE_TYPE_ID,
|
|
ports
|
|
})
|
|
})
|
|
.then((res) => res.json())
|
|
.then((data) => {
|
|
if (data?.error) {
|
|
throw new Error(data.error);
|
|
}
|
|
alert('Ports gespeichert');
|
|
})
|
|
.catch((err) => {
|
|
alert('Speichern fehlgeschlagen: ' + err.message);
|
|
});
|
|
}
|
|
|
|
function getSvgCoordinates(event) {
|
|
const pt = svgElement.createSVGPoint();
|
|
pt.x = event.clientX;
|
|
pt.y = event.clientY;
|
|
const transformed = pt.matrixTransform(svgElement.getScreenCTM().inverse());
|
|
return { x: transformed.x, y: transformed.y };
|
|
}
|
|
|
|
function getPortById(id) {
|
|
return ports.find((port) => String(port.id) === String(id));
|
|
}
|
|
|
|
function generateTempId() {
|
|
return 'tmp_' + Math.random().toString(36).slice(2, 11);
|
|
}
|
|
|
|
document.addEventListener('keydown', (event) => {
|
|
if (event.key === 'Delete') {
|
|
deleteSelectedPort();
|
|
}
|
|
});
|
|
})();
|