feat: add section content editing, animation selection and HTML5 drag & drop in live editor
All checks were successful
Production Build & Deploy / build-and-deploy (push) Successful in 1m0s

This commit is contained in:
Daniel S
2026-08-09 12:45:36 +02:00
parent b159b061ae
commit 3453746fc0
5 changed files with 184 additions and 11 deletions

View File

@@ -83,16 +83,16 @@ const { siteConfig, themeConfig } = loadWaasConfigs(Astro.request.headers.get('u
</div>
</div>
<!-- Sektionen Re-Order -->
<!-- Sektionen Re-Order mit HTML5 Drag & Drop -->
<div class="glass-panel rounded-xl p-4 space-y-3">
<h3 class="text-xs font-bold text-sky-400 uppercase tracking-wider flex items-center justify-between">
<span>Sektionen Reihenfolge (Layout)</span>
<span class="text-[10px] text-slate-500 font-mono">Drag & Drop</span>
<span class="text-[10px] text-slate-400 font-mono">Drag &amp; Drop</span>
</h3>
<div id="sections-list" class="space-y-2">
{siteConfig.sectionsOrder.map((sec, idx) => (
<div data-section={sec} draggable="true" class="section-item flex items-center justify-between p-3 rounded-lg bg-slate-900/60 border border-white/5 hover:border-white/20 transition-all group cursor-grab">
<div data-section={sec} draggable="true" class="section-item flex items-center justify-between p-3 rounded-lg bg-slate-900/60 border border-white/5 hover:border-sky-400/40 transition-all group cursor-grab active:cursor-grabbing">
<div class="flex items-center gap-2">
<span class="text-slate-500 font-mono text-xs">#{idx + 1}</span>
<span class="text-xs font-semibold capitalize text-white">{sec}</span>
@@ -107,6 +107,63 @@ const { siteConfig, themeConfig } = loadWaasConfigs(Astro.request.headers.get('u
</div>
</div>
<!-- Sektions-Inhalte & Animationen Bearbeiten -->
<div class="glass-panel rounded-xl p-4 space-y-4">
<h3 class="text-xs font-bold text-sky-400 uppercase tracking-wider">
Hero Sektion: Inhalt &amp; Animation
</h3>
<div class="space-y-3 text-xs">
<div>
<label class="block text-[10px] font-bold text-slate-400 uppercase mb-1">Hero Title</label>
<input type="text" id="hero-title-input" value={siteConfig.hero.title} class="w-full glass-panel bg-slate-900 rounded-lg p-2 text-xs text-white focus:outline-none focus:border-sky-400" />
</div>
<div>
<label class="block text-[10px] font-bold text-slate-400 uppercase mb-1">Hero Subtitle</label>
<textarea id="hero-subtitle-input" class="w-full h-16 glass-panel bg-slate-900 rounded-lg p-2 text-xs text-white focus:outline-none focus:border-sky-400">{siteConfig.hero.subtitle}</textarea>
</div>
<div>
<label class="block text-[10px] font-bold text-slate-400 uppercase mb-1">Hero Badge Text</label>
<input type="text" id="hero-badge-input" value={siteConfig.hero.badge} class="w-full glass-panel bg-slate-900 rounded-lg p-2 text-xs text-white focus:outline-none focus:border-sky-400" />
</div>
<div>
<label class="block text-[10px] font-bold text-slate-400 uppercase mb-1">Hero Sektion Animation</label>
<select id="hero-anim-select" class="w-full glass-panel bg-slate-900 rounded-lg p-2 text-xs text-white focus:outline-none focus:border-sky-400">
<option value="" selected={!siteConfig.hero.animation}>Keine Animation</option>
<option value="spring-fade" selected={siteConfig.hero.animation === 'spring-fade'}>Spring Fade (Federleicht)</option>
<option value="slide-up" selected={siteConfig.hero.animation === 'slide-up'}>Slide Up (Eingleiten)</option>
<option value="zoom-in" selected={siteConfig.hero.animation === 'zoom-in'}>Zoom In (Heranzoomen)</option>
<option value="pulse-glow" selected={siteConfig.hero.animation === 'pulse-glow'}>Pulse Glow (Leuchten)</option>
</select>
</div>
</div>
</div>
<!-- Bento Grid: Inhalt & Animation -->
<div class="glass-panel rounded-xl p-4 space-y-4">
<h3 class="text-xs font-bold text-sky-400 uppercase tracking-wider">
Bento Grid: Inhalt &amp; Animation
</h3>
<div class="space-y-3 text-xs">
<div>
<label class="block text-[10px] font-bold text-slate-400 uppercase mb-1">Sektions-Titel</label>
<input type="text" id="bento-title-input" value={siteConfig.bento.sectionTitle} class="w-full glass-panel bg-slate-900 rounded-lg p-2 text-xs text-white focus:outline-none focus:border-sky-400" />
</div>
<div>
<label class="block text-[10px] font-bold text-slate-400 uppercase mb-1">Sektions-Untertitel</label>
<input type="text" id="bento-subtitle-input" value={siteConfig.bento.sectionSubtitle} class="w-full glass-panel bg-slate-900 rounded-lg p-2 text-xs text-white focus:outline-none focus:border-sky-400" />
</div>
<div>
<label class="block text-[10px] font-bold text-slate-400 uppercase mb-1">Bento Grid Animation</label>
<select id="bento-anim-select" class="w-full glass-panel bg-slate-900 rounded-lg p-2 text-xs text-white focus:outline-none focus:border-sky-400">
<option value="" selected={!siteConfig.bento.animation}>Keine Animation</option>
<option value="slide-up" selected={siteConfig.bento.animation === 'slide-up'}>Slide Up</option>
<option value="spring-fade" selected={siteConfig.bento.animation === 'spring-fade'}>Spring Fade</option>
<option value="zoom-in" selected={siteConfig.bento.animation === 'zoom-in'}>Zoom In</option>
</select>
</div>
</div>
</div>
<!-- Dynamische UIKomponente Hinzufuegen per Sektion -->
<div class="glass-panel rounded-xl p-4 space-y-4">
<h3 class="text-xs font-bold text-sky-400 uppercase tracking-wider">
@@ -150,6 +207,18 @@ const { siteConfig, themeConfig } = loadWaasConfigs(Astro.request.headers.get('u
</select>
</div>
<div>
<label class="block text-[10px] font-bold text-slate-400 uppercase mb-1">Element Animation</label>
<select id="new-el-anim" class="w-full glass-panel bg-slate-900 rounded-lg p-2 text-xs text-white focus:outline-none focus:border-sky-400">
<option value="">Keine Animation</option>
<option value="spring-fade">Spring Fade</option>
<option value="hover-bounce">Hover Bounce</option>
<option value="pulse-glow">Pulse Glow</option>
<option value="slide-up">Slide Up</option>
<option value="zoom-in">Zoom In</option>
</select>
</div>
<div>
<label class="block text-[10px] font-bold text-slate-400 uppercase mb-1">Inhalt / Label</label>
<input type="text" id="new-el-content" placeholder="z.B. Jetzt Anfragen" class="w-full glass-panel bg-slate-900 rounded-lg p-2 text-xs text-white focus:outline-none focus:border-sky-400" />
@@ -180,7 +249,7 @@ const { siteConfig, themeConfig } = loadWaasConfigs(Astro.request.headers.get('u
<div id="elements-container" class="space-y-2 max-h-80 overflow-y-auto pr-1 custom-scroll">
{((siteConfig.hero.elements || [])).map((el, idx) => (
<div data-id={el.id} class="p-3 rounded-lg bg-slate-900/70 border border-white/10 space-y-2 text-xs group">
<div data-id={el.id} draggable="true" class="element-item p-3 rounded-lg bg-slate-900/70 border border-white/10 space-y-2 text-xs group cursor-grab active:cursor-grabbing">
<div class="flex items-center justify-between">
<div>
<span class="font-bold text-sky-400 uppercase text-[10px] block">{el.type} (Slot: {el.slotId})</span>
@@ -404,11 +473,48 @@ const { siteConfig, themeConfig } = loadWaasConfigs(Astro.request.headers.get('u
renderSectionsList();
};
// --- Drag & Drop for Section Items ---
let draggedSecIndex = -1;
function initSectionDragAndDrop() {
const listContainer = document.getElementById('sections-list');
if (!listContainer) return;
const items = listContainer.querySelectorAll('.section-item');
items.forEach((item, index) => {
item.addEventListener('dragstart', (e) => {
draggedSecIndex = index;
e.dataTransfer.effectAllowed = 'move';
item.classList.add('opacity-40');
});
item.addEventListener('dragend', () => {
item.classList.remove('opacity-40');
draggedSecIndex = -1;
});
item.addEventListener('dragover', (e) => {
e.preventDefault();
e.dataTransfer.dropEffect = 'move';
});
item.addEventListener('drop', (e) => {
e.preventDefault();
if (draggedSecIndex !== -1 && draggedSecIndex !== index) {
const arr = siteConfigState.sectionsOrder;
const movedItem = arr.splice(draggedSecIndex, 1)[0];
arr.splice(index, 0, movedItem);
renderSectionsList();
}
});
});
}
function renderSectionsList() {
const listContainer = document.getElementById('sections-list');
if (!listContainer) return;
listContainer.innerHTML = siteConfigState.sectionsOrder.map((sec, idx) => (
'<div data-section="' + sec + '" draggable="true" class="section-item flex items-center justify-between p-3 rounded-lg bg-slate-900/60 border border-white/5 hover:border-white/20 transition-all group cursor-grab">' +
'<div data-section="' + sec + '" draggable="true" class="section-item flex items-center justify-between p-3 rounded-lg bg-slate-900/60 border border-white/5 hover:border-sky-400/40 transition-all group cursor-grab active:cursor-grabbing">' +
'<div class="flex items-center gap-2">' +
'<span class="text-slate-500 font-mono text-xs">#' + (idx + 1) + '</span>' +
'<span class="text-xs font-semibold capitalize text-white">' + sec + '</span>' +
@@ -420,8 +526,13 @@ const { siteConfig, themeConfig } = loadWaasConfigs(Astro.request.headers.get('u
'</div>' +
'</div>'
)).join('');
initSectionDragAndDrop();
}
// Initial call for drag and drop setup
initSectionDragAndDrop();
function updateSectionSelectors() {
const newElSec = document.getElementById('new-el-section');
const filterSec = document.getElementById('filter-section-select');
@@ -443,6 +554,7 @@ const { siteConfig, themeConfig } = loadWaasConfigs(Astro.request.headers.get('u
const section = document.getElementById('new-el-section').value;
const slotId = document.getElementById('new-el-slot').value;
const type = document.getElementById('new-el-type').value;
const anim = document.getElementById('new-el-anim').value;
const content = document.getElementById('new-el-content').value || 'Neues Element';
const newComp = {
@@ -450,6 +562,7 @@ const { siteConfig, themeConfig } = loadWaasConfigs(Astro.request.headers.get('u
slotId: section + '-' + slotId,
type,
content,
animation0: anim || undefined,
style: 'primary'
};
@@ -509,7 +622,7 @@ const { siteConfig, themeConfig } = loadWaasConfigs(Astro.request.headers.get('u
if (countEl) countEl.textContent = els.length;
container.innerHTML = els.map((el, idx) => (
'<div data-id="' + el.id + '" class="p-3 rounded-lg bg-slate-900/70 border border-white/10 space-y-2 text-xs group">' +
'<div data-id="' + el.id + '" draggable="true" class="element-item p-3 rounded-lg bg-slate-900/70 border border-white/10 space-y-2 text-xs group cursor-grab active:cursor-grabbing">' +
'<div class="flex items-center justify-between">' +
'<div>' +
'<span class="font-bold text-sky-400 uppercase text-[10px] block">' + el.type + ' (Slot: ' + el.slotId + ')</span>' +
@@ -544,6 +657,28 @@ const { siteConfig, themeConfig } = loadWaasConfigs(Astro.request.headers.get('u
saveBtn.disabled = true;
saveBtn.textContent = 'Wird gespeichert...';
// Collect Hero Section Inputs & Animation
const heroTitle = document.getElementById('hero-title-input')?.value;
const heroSubtitle = document.getElementById('hero-subtitle-input')?.value;
const heroBadge = document.getElementById('hero-badge-input')?.value;
const heroAnim = document.getElementById('hero-anim-select')?.value;
if (!siteConfigState.hero) siteConfigState.hero = {};
if (heroTitle !== undefined) siteConfigState.hero.title = heroTitle;
if (heroSubtitle !== undefined) siteConfigState.hero.subtitle = heroSubtitle;
if (heroBadge !== undefined) siteConfigState.hero.badge = heroBadge;
siteConfigState.hero.animation = heroAnim || undefined;
// Collect Bento Section Inputs & Animation
const bentoTitle = document.getElementById('bento-title-input')?.value;
const bentoSubtitle = document.getElementById('bento-subtitle-input')?.value;
const bentoAnim = document.getElementById('bento-anim-select')?.value;
if (!siteConfigState.bento) siteConfigState.bento = {};
if (bentoTitle !== undefined) siteConfigState.bento.sectionTitle = bentoTitle;
if (bentoSubtitle !== undefined) siteConfigState.bento.sectionSubtitle = bentoSubtitle;
siteConfigState.bento.animation = bentoAnim || undefined;
// Collect Header Inputs
const brandName = document.getElementById('header-brand-name')?.value;
const brandHighlight = document.getElementById('header-brand-highlight')?.value;