Files
SPA-landing/symbols/js/app/legend.js
architeur d707c5001d Refactor symbols app: split large files (max 300 lines)
- Split styles.css (1319 lines) into 6 CSS modules:
  - base.css, layout.css, modal.css, text-generator.css, components.css, legend.css
- Split app.js (1219 lines) into 8 JS modules:
  - core.js, custom.js, dxf.js, export.js, legend.js, legend-export.js, path-parser.js, utils.js
- Split symbols.js (870 lines) into 10 JS modules:
  - index.js, schaeden.js, werkzeuge.js, bauteile.js, moebel.js, sanitaer.js, vermessung.js, vermessung-infra.js, vermessung-topo.js, init.js
- Updated index.html to load new modular files

All files now comply with 300-line maximum rule.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-14 21:34:03 +01:00

244 lines
7.8 KiB
JavaScript

// ============================================
// LEGEND - Legenden-Funktionen
// ============================================
// ========== LEGENDE AUSWAHL ==========
function toggleLegendSelection(id) {
if (selectedSymbols.has(id)) {
selectedSymbols.delete(id);
} else {
selectedSymbols.add(id);
const item = findSymbol(id);
if (item && !legendItems.find(l => l.id === id)) {
legendItems.push({
id: item.id,
name: item.name,
svg: item.svg,
description: ''
});
}
}
renderSymbols();
updateLegendCount();
saveLegendToStorage();
}
function updateLegendCount() {
const countEl = document.getElementById('legendCount');
if (countEl) {
countEl.textContent = legendItems.length;
}
}
// ========== LEGENDE MODAL ==========
function openLegendModal() {
const modal = document.getElementById('legendModal');
modal.classList.add('active');
renderLegendEditor();
updateLegendPreview();
}
function closeLegendModal() {
const modal = document.getElementById('legendModal');
modal.classList.remove('active');
}
function renderLegendEditor() {
const container = document.getElementById('legendItems');
if (legendItems.length === 0) {
container.innerHTML = '<div class="legend-empty">Keine Symbole in der Legende. Klicken Sie auf 📑 bei einem Symbol, um es hinzuzufügen.</div>';
updateLegendPreview();
return;
}
container.innerHTML = legendItems.map((item, index) => `
<div class="legend-item" data-index="${index}">
<div class="legend-item-preview">${item.svg}</div>
<div class="legend-item-content">
<input type="text" class="legend-item-name" value="${item.name}"
oninput="updateLegendItem(${index}, 'name', this.value)" placeholder="Name">
<input type="text" class="legend-item-desc" value="${item.description || ''}"
oninput="updateLegendItem(${index}, 'description', this.value)" placeholder="Beschreibung (optional)">
</div>
<div class="legend-item-actions">
<button type="button" onclick="moveLegendItem(${index}, -1)" title="Nach oben" ${index === 0 ? 'disabled' : ''}>▲</button>
<button type="button" onclick="moveLegendItem(${index}, 1)" title="Nach unten" ${index === legendItems.length - 1 ? 'disabled' : ''}>▼</button>
<button type="button" onclick="removeLegendItem(${index})" title="Entfernen" class="btn-remove">✕</button>
</div>
</div>
`).join('');
updateLegendPreview();
}
function updateLegendItem(index, field, value) {
if (legendItems[index]) {
legendItems[index][field] = value;
saveLegendToStorage();
updateLegendPreview();
}
}
function moveLegendItem(index, direction) {
const newIndex = index + direction;
if (newIndex >= 0 && newIndex < legendItems.length) {
const temp = legendItems[index];
legendItems[index] = legendItems[newIndex];
legendItems[newIndex] = temp;
renderLegendEditor();
saveLegendToStorage();
}
}
function removeLegendItem(index) {
const item = legendItems[index];
if (item) {
selectedSymbols.delete(item.id);
}
legendItems.splice(index, 1);
renderLegendEditor();
renderSymbols();
updateLegendCount();
saveLegendToStorage();
}
function clearLegend() {
if (confirm('Möchten Sie die Legende wirklich leeren?')) {
legendItems = [];
selectedSymbols.clear();
renderLegendEditor();
renderSymbols();
updateLegendCount();
saveLegendToStorage();
}
}
// ========== LEGENDE SPEICHERN/LADEN ==========
function saveLegendToStorage() {
try {
localStorage.setItem('gutachter_legende', JSON.stringify(legendItems));
localStorage.setItem('gutachter_selected', JSON.stringify([...selectedSymbols]));
} catch (e) {
console.warn('LocalStorage nicht verfügbar');
}
}
function loadLegendFromStorage() {
try {
const saved = localStorage.getItem('gutachter_legende');
const savedSelected = localStorage.getItem('gutachter_selected');
if (saved) {
legendItems = JSON.parse(saved);
}
if (savedSelected) {
selectedSymbols = new Set(JSON.parse(savedSelected));
}
updateLegendCount();
} catch (e) {
console.warn('Fehler beim Laden der Legende');
}
}
// ========== LEGENDE VORSCHAU ==========
function generateLegendSVG() {
if (legendItems.length === 0) return null;
const itemHeight = 50;
const width = 320;
const height = legendItems.length * itemHeight + 60;
let svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}" width="${width}" height="${height}">
<rect width="${width}" height="${height}" fill="white"/>
<text x="20" y="30" font-family="Arial" font-size="18" font-weight="bold">Legende</text>
<line x1="20" y1="40" x2="${width - 20}" y2="40" stroke="#ccc" stroke-width="1"/>`;
legendItems.forEach((item, index) => {
const y = 60 + index * itemHeight;
svg += `<g transform="translate(20, ${y})">
<g transform="scale(0.5)">${item.svg.replace(/<svg[^>]*>/, '').replace('</svg>', '')}</g>
<text x="50" y="20" font-family="Arial" font-size="14" font-weight="bold">${escapeHtml(item.name)}</text>
${item.description ? `<text x="50" y="35" font-family="Arial" font-size="11" fill="#666">${escapeHtml(item.description)}</text>` : ''}
</g>`;
});
svg += '</svg>';
return svg;
}
function updateLegendPreview() {
const previewBox = document.getElementById('legendPreviewBox');
if (!previewBox) return;
if (legendItems.length === 0) {
previewBox.innerHTML = '<div class="legend-preview-empty">Keine Eintraege vorhanden</div>';
return;
}
const svg = generateLegendSVG();
if (svg) {
previewBox.innerHTML = svg;
}
}
async function copyLegendAsImage() {
if (legendItems.length === 0) {
showNotification('Legende ist leer', 'error');
return;
}
const svg = generateLegendSVG();
if (!svg) return;
try {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const itemHeight = 50;
const width = 320;
const height = legendItems.length * itemHeight + 60;
canvas.width = width * 2;
canvas.height = height * 2;
ctx.scale(2, 2);
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, width, height);
const img = new Image();
const svgBlob = new Blob([svg], { type: 'image/svg+xml;charset=utf-8' });
const url = URL.createObjectURL(svgBlob);
await new Promise((resolve, reject) => {
img.onload = resolve;
img.onerror = reject;
img.src = url;
});
ctx.drawImage(img, 0, 0, width, height);
URL.revokeObjectURL(url);
canvas.toBlob(async (blob) => {
try {
await navigator.clipboard.write([
new ClipboardItem({ 'image/png': blob })
]);
showNotification('Legende in Zwischenablage kopiert!');
} catch (err) {
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = 'legende.png';
link.click();
showNotification('Legende als PNG heruntergeladen');
}
}, 'image/png');
} catch (err) {
console.error('Fehler beim Kopieren:', err);
showNotification('Fehler beim Kopieren', 'error');
}
}
// Export-Funktionen sind in legend-export.js