- 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>
147 lines
4.9 KiB
JavaScript
147 lines
4.9 KiB
JavaScript
// ============================================
|
|
// CORE - Initialisierung und Rendering
|
|
// Gutachter Symbolbibliothek v2.0
|
|
// ============================================
|
|
|
|
// ========== GLOBALE VARIABLEN ==========
|
|
let currentFilter = 'all';
|
|
let currentSearch = '';
|
|
let selectedSymbols = new Set();
|
|
let legendItems = [];
|
|
|
|
// ========== INITIALISIERUNG ==========
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
renderSymbols();
|
|
setupEventListeners();
|
|
loadLegendFromStorage();
|
|
});
|
|
|
|
// ========== EVENT LISTENERS ==========
|
|
function setupEventListeners() {
|
|
// Suche
|
|
document.getElementById('searchInput').addEventListener('input', function(e) {
|
|
currentSearch = e.target.value.toLowerCase();
|
|
renderSymbols();
|
|
});
|
|
|
|
// Filter Pills
|
|
document.querySelectorAll('.filter-pill').forEach(pill => {
|
|
pill.addEventListener('click', function() {
|
|
document.querySelectorAll('.filter-pill').forEach(p => p.classList.remove('active'));
|
|
this.classList.add('active');
|
|
currentFilter = this.dataset.filter;
|
|
renderSymbols();
|
|
});
|
|
});
|
|
|
|
// Modal schließen
|
|
document.getElementById('legendModal').addEventListener('click', function(e) {
|
|
if (e.target === this) {
|
|
closeLegendModal();
|
|
}
|
|
});
|
|
|
|
// Escape-Taste zum Schließen
|
|
document.addEventListener('keydown', function(e) {
|
|
if (e.key === 'Escape') {
|
|
closeLegendModal();
|
|
}
|
|
});
|
|
}
|
|
|
|
// ========== SYMBOLE RENDERN ==========
|
|
function renderSymbols() {
|
|
const container = document.getElementById('symbolGrid');
|
|
container.innerHTML = '';
|
|
|
|
let hasResults = false;
|
|
|
|
Object.keys(SYMBOLS).forEach(categoryKey => {
|
|
const category = SYMBOLS[categoryKey];
|
|
|
|
// Filter nach Kategorie
|
|
if (currentFilter !== 'all' && currentFilter !== categoryKey) {
|
|
return;
|
|
}
|
|
|
|
const filteredItems = category.items.filter(item => {
|
|
if (!currentSearch) return true;
|
|
return item.name.toLowerCase().includes(currentSearch) ||
|
|
item.tags.some(tag => tag.toLowerCase().includes(currentSearch));
|
|
});
|
|
|
|
if (filteredItems.length === 0) return;
|
|
|
|
hasResults = true;
|
|
|
|
// Kategorie-Header
|
|
const categoryHeader = document.createElement('div');
|
|
categoryHeader.className = 'category-header';
|
|
categoryHeader.innerHTML = `<span>${category.icon} ${category.name}</span><span class="category-count">${filteredItems.length} Symbole</span>`;
|
|
container.appendChild(categoryHeader);
|
|
|
|
// Symbol-Grid für diese Kategorie
|
|
const categoryGrid = document.createElement('div');
|
|
categoryGrid.className = 'category-grid';
|
|
|
|
filteredItems.forEach(item => {
|
|
const card = createSymbolCard(item, categoryKey);
|
|
categoryGrid.appendChild(card);
|
|
});
|
|
|
|
container.appendChild(categoryGrid);
|
|
});
|
|
|
|
if (!hasResults) {
|
|
container.innerHTML = '<div class="no-results">Keine Symbole gefunden. Versuchen Sie einen anderen Suchbegriff.</div>';
|
|
}
|
|
}
|
|
|
|
// ========== SYMBOL-KARTE ERSTELLEN ==========
|
|
function createSymbolCard(item, categoryKey) {
|
|
const card = document.createElement('div');
|
|
card.className = 'symbol-card';
|
|
|
|
// Spezielle Klasse für Vermessungssymbole
|
|
if (categoryKey.startsWith('vermessung_')) {
|
|
card.classList.add('vermessung');
|
|
}
|
|
|
|
const isSelected = selectedSymbols.has(item.id);
|
|
if (isSelected) {
|
|
card.classList.add('selected');
|
|
}
|
|
|
|
card.innerHTML = `
|
|
<div class="symbol-preview">${item.svg}</div>
|
|
<div class="symbol-name">${item.name}</div>
|
|
<div class="symbol-actions">
|
|
<button class="btn-action btn-copy" onclick="copyAsImage('${item.id}')" title="Als Bild kopieren (transparent)">
|
|
📋 Kopieren
|
|
</button>
|
|
<button class="btn-action btn-svg" onclick="downloadSVG('${item.id}')" title="SVG herunterladen">
|
|
⬇️ SVG
|
|
</button>
|
|
<button class="btn-action btn-png" onclick="downloadSymbolPNG('${item.id}')" title="PNG herunterladen">PNG</button>
|
|
<button class="btn-action btn-jpg" onclick="downloadSymbolJPG('${item.id}')" title="JPG herunterladen">JPG</button>
|
|
<button class="btn-action btn-dxf" onclick="downloadDXF('${item.id}')" title="DXF herunterladen">
|
|
📐 DXF
|
|
</button>
|
|
<button class="btn-action btn-legend ${isSelected ? 'active' : ''}" onclick="toggleLegendSelection('${item.id}')" title="Zur Legende hinzufügen">
|
|
📑
|
|
</button>
|
|
</div>
|
|
`;
|
|
|
|
return card;
|
|
}
|
|
|
|
// ========== SYMBOL FINDEN ==========
|
|
function findSymbol(id) {
|
|
for (const categoryKey of Object.keys(SYMBOLS)) {
|
|
const item = SYMBOLS[categoryKey].items.find(i => i.id === id);
|
|
if (item) return item;
|
|
}
|
|
return null;
|
|
}
|