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>
This commit is contained in:
146
symbols/js/app/core.js
Normal file
146
symbols/js/app/core.js
Normal file
@@ -0,0 +1,146 @@
|
||||
// ============================================
|
||||
// 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;
|
||||
}
|
||||
Reference in New Issue
Block a user