Einfügen Icons + Anpassung Styling Radio-Buttons und Foto-Buttons bei Eventerstellung

This commit is contained in:
Ysabelle Moser 2026-04-23 16:25:23 +02:00
parent ee1c78f5de
commit 4bd4f8046d
3 changed files with 258 additions and 31 deletions

View File

@ -229,6 +229,24 @@ textarea:focus {
transition: box-shadow 0.2s ease, transform 0.2s ease, background-color 0.2s ease;
}
.option-card--with-icon {
justify-items: center;
align-content: center;
gap: var(--space-2);
color: var(--brown);
}
.option-card__icon {
color: var(--brown);
font-size: 1.35rem;
line-height: 1;
pointer-events: none;
}
.option-card--with-icon span {
pointer-events: none;
}
.option-card:hover {
background: var(--olive-light);
@ -236,6 +254,41 @@ textarea:focus {
transform: translateY(-3px);
}
.option-grid--tomato-choices .option-card:hover,
.option-grid--tomato-choices .option-card:has(input:focus-visible) {
border-color: var(--tomato);
background: var(--tomato);
color: var(--butter-light);
}
.option-grid--tomato-choices .option-card:has(input:checked) {
border-color: var(--tomato);
background: var(--tomato);
color: var(--butter-light);
}
.option-grid--icon-choices .option-card--with-icon:hover .option-card__icon,
.option-grid--icon-choices .option-card--with-icon:has(input:focus-visible) .option-card__icon,
.option-grid--icon-choices .option-card--with-icon:has(input:checked) .option-card__icon {
color: var(--butter-light);
}
.option-grid--icon-choices .option-card--with-icon:has(input:disabled) {
opacity: 0.45;
}
.option-grid--icon-choices .option-card--with-icon:has(input:disabled):hover {
border-color: var(--olive-light);
background: var(--butter-light);
color: var(--brown);
box-shadow: none;
transform: none;
}
.option-grid--icon-choices .option-card--with-icon:has(input:disabled):hover .option-card__icon {
color: var(--brown);
}
.option-card input {
position: absolute;
inset: 0;
@ -251,6 +304,20 @@ textarea:focus {
.option-card--invalid {
border-color: var(--error) !important;
box-shadow: 0 0 0 2px rgba(212, 75, 36, 0.14);
}
.guest-count-icon {
display: flex;
justify-content: center;
width: 100%;
color: var(--brown);
font-size: 3rem;
line-height: 1;
}
.guest-count-icon::before {
display: block;
transform: translateX(1.1rem);
}
.counter {
@ -259,8 +326,17 @@ textarea:focus {
gap: var(--space-16);
}
.counter input {
.counter-value-group {
display: grid;
justify-items: stretch;
row-gap: var(--space-1);
width: 6rem;
}
.counter input {
width: 100%;
height: 3rem;
padding-block: 0.75rem;
text-align: center;
}
@ -327,6 +403,20 @@ textarea:focus {
color: var(--color-text-secondary);
}
.review-gallery {
display: flex;
flex-wrap: wrap;
gap: var(--space-1);
}
.review-gallery__thumb {
width: 4.5rem;
height: 4.5rem;
border-radius: var(--radius-sm);
object-fit: cover;
box-shadow: var(--shadow-interaction);
}
.submission-success-actions {
display: flex;
justify-content: flex-start;
@ -417,22 +507,58 @@ textarea:focus {
margin-top: var(--space-2);
}
/* Gallery Upload */
.gallery-upload {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.75rem;
gap: var(--space-16);
}
.gallery-preview {
display: contents;
}
.gallery-add-button {
width: 7rem;
height: 7rem;
display: grid;
place-items: center;
align-content: center;
gap: var(--space-1);
padding: var(--space-1);
border: 1.5px solid var(--olive-light);
border-radius: var(--radius-md);
background: var(--butter-light);
color: var(--olive);
font-family: var(--font-main);
cursor: pointer;
transition: background-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
}
.gallery-add-button__icon {
color: var(--brown);
font-size: 2rem;
line-height: 1;
}
.gallery-add-button__text {
color: var(--black);
font-size: 0.95rem;
line-height: 1.15;
text-align: center;
overflow-wrap: anywhere;
}
.gallery-add-button:hover {
background: var(--olive-light);
box-shadow: var(--shadow-interaction);
transform: translateY(-3px);
}
.gallery-thumb {
position: relative;
width: 5.5rem;
height: 5.5rem;
width: 7rem;
height: 7rem;
border-radius: var(--radius-sm, 0.5rem);
overflow: hidden;
flex-shrink: 0;

View File

@ -80,24 +80,28 @@
<fieldset class="form-field">
<label>Art des Essens / Eventtyp</label>
<div class="option-grid option-grid--4">
<label class="option-card option">
<div class="option-grid option-grid--4 option-grid--event-type option-grid--icon-choices option-grid--tomato-choices">
<label class="option-card option option-card--with-icon">
<input type="radio" name="eventType" value="Brunch" required />
<i class="fa-solid fa-bread-slice option-card__icon" aria-hidden="true"></i>
<span>Brunch</span>
</label>
<label class="option-card option">
<label class="option-card option option-card--with-icon">
<input type="radio" name="eventType" value="Lunch" />
<i class="fa-solid fa-pizza-slice option-card__icon" aria-hidden="true"></i>
<span>Lunch</span>
</label>
<label class="option-card option">
<label class="option-card option option-card--with-icon">
<input type="radio" name="eventType" value="Kaffee + Kuchen" />
<i class="fa-solid fa-mug-hot option-card__icon" aria-hidden="true"></i>
<span>Kaffee + Kuchen</span>
</label>
<label class="option-card option">
<label class="option-card option option-card--with-icon">
<input type="radio" name="eventType" value="Dinner" />
<i class="fa-solid fa-martini-glass option-card__icon" aria-hidden="true"></i>
<span>Dinner</span>
</label>
</div>
@ -115,15 +119,18 @@
>
</button>
<input
type="number"
id="maxGuests"
name="maxGuests"
min="1"
step="1"
value="4"
required
/>
<div class="counter-value-group">
<i class="fa-solid fa-user-group guest-count-icon" aria-hidden="true"></i>
<input
type="number"
id="maxGuests"
name="maxGuests"
min="1"
step="1"
value="0"
required
/>
</div>
<button
type="button"
class="counter-button"
@ -152,24 +159,28 @@
<fieldset class="form-field">
<label>Ernährungsform</label>
<div class="option-grid option-grid--4">
<label class="option-card option">
<div class="option-grid option-grid--4 option-grid--icon-choices option-grid--tomato-choices">
<label class="option-card option option-card--with-icon">
<input type="checkbox" name="dietType" value="Fleisch" />
<i class="fa-solid fa-drumstick-bite option-card__icon" aria-hidden="true"></i>
<span>Fleisch</span>
</label>
<label class="option-card option">
<label class="option-card option option-card--with-icon">
<input type="checkbox" name="dietType" value="Fisch" />
<i class="fa-solid fa-fish option-card__icon" aria-hidden="true"></i>
<span>Fisch</span>
</label>
<label class="option-card option">
<label class="option-card option option-card--with-icon">
<input type="checkbox" name="dietType" value="Vegetarisch" />
<i class="fa-solid fa-seedling option-card__icon" aria-hidden="true"></i>
<span>Vegetarisch</span>
</label>
<label class="option-card option">
<label class="option-card option option-card--with-icon">
<input type="checkbox" name="dietType" value="Vegan" />
<i class="fa-solid fa-leaf option-card__icon" aria-hidden="true"></i>
<span>Vegan</span>
</label>
</div>
@ -198,7 +209,7 @@
<label>Allergene / Unverträglichkeiten</label>
<p class="field-hint">Optional nur auswählen, wenn es für dein Event relevant ist.</p>
<div class="option-grid option-grid--3">
<div class="option-grid option-grid--3 option-grid--tomato-choices">
<label class="option-card option">
<input type="checkbox" name="allergies" value="glutenfrei" />
<span>Glutenfrei</span>
@ -243,7 +254,7 @@
<div class="form-field">
<label for="eventTime">Uhrzeit</label>
<input type="time" id="eventTime" name="eventTime" value="00:00" required />
<input type="time" id="eventTime" name="eventTime" required />
</div>
</div>
</div>
@ -297,10 +308,13 @@
</div>
<div class="form-field">
<label>Fotos hinzufügen <span class="field-hint-inline">(optional)</span></label>
<label>Wie wird dein Event aussehen? <span class="field-hint-inline">(optional)</span></label>
<div class="gallery-upload">
<div class="gallery-preview" id="galleryPreview"></div>
<button type="button" class="button-primary" id="galleryAddBtn" aria-label="Foto hinzufügen">Foto hinzufügen</button>
<button type="button" class="gallery-add-button" id="galleryAddBtn" aria-label="Foto hinzufügen">
<i class="fa-solid fa-plus gallery-add-button__icon" aria-hidden="true"></i>
<span class="gallery-add-button__text">Foto<br>hinzufügen</span>
</button>
<input type="file" id="galleryFileInput" accept="image/*" multiple hidden />
</div>
</div>
@ -375,6 +389,15 @@
<dt>Event-Abend</dt>
<dd data-review="eventDescription"></dd>
</div>
<div class="review-item" data-edit-step="6" data-edit-field="galleryAddBtn" role="button" tabindex="0" aria-label="Fotos bearbeiten">
<dt>Fotos</dt>
<dd>
<div class="review-gallery" data-review-gallery>
<span>Keine Fotos hinzugefügt</span>
</div>
</dd>
</div>
</dl>
</div>
</div>

View File

@ -270,6 +270,29 @@ function updateReviewField(fieldName, value) {
}
}
/**
* Zeigt hochgeladene Fotos als kleine Vorschaubilder in der Review.
*/
function updateReviewGallery() {
const target = document.querySelector("[data-review-gallery]");
if (!target) return;
target.innerHTML = "";
if (galleryImages.length === 0) {
target.textContent = "Keine Fotos hinzugefügt";
return;
}
galleryImages.forEach((src, index) => {
const image = document.createElement("img");
image.className = "review-gallery__thumb";
image.src = src;
image.alt = `Foto ${index + 1}`;
target.appendChild(image);
});
}
/**
* Baut den Text für Allergien / Hinweise zusammen.
* Dabei werden Checkboxen und zusätzliches Freitextfeld kombiniert.
@ -297,6 +320,8 @@ function updateReview() {
Object.entries(reviewValues).forEach(([key, value]) => {
updateReviewField(key, value);
});
updateReviewGallery();
}
/**
@ -539,6 +564,52 @@ function validateDietType() {
return { isValid: true };
}
/**
* Verhindert widersprüchliche Ernährungsformen:
* Fleisch/Fisch dürfen zusammen, schliessen aber vegetarisch/vegan aus.
*/
function registerDietConflictHandlers() {
const dietOptions = Array.from(form.querySelectorAll('input[name="dietType"]'));
const meatFishOptions = dietOptions.filter(input => ["Fleisch", "Fisch"].includes(input.value));
const plantOptions = dietOptions.filter(input => ["Vegetarisch", "Vegan"].includes(input.value));
const updateDietAvailability = changedInput => {
if (changedInput?.checked && changedInput.value === "Vegetarisch") {
dietOptions.forEach(input => {
if (input.value === "Vegan") input.checked = false;
});
}
if (changedInput?.checked && changedInput.value === "Vegan") {
dietOptions.forEach(input => {
if (input.value === "Vegetarisch") input.checked = false;
});
}
const hasMeatOrFish = meatFishOptions.some(input => input.checked);
const selectedPlantDiet = plantOptions.find(input => input.checked);
meatFishOptions.forEach(input => {
input.disabled = Boolean(selectedPlantDiet);
});
plantOptions.forEach(input => {
input.disabled = hasMeatOrFish || Boolean(selectedPlantDiet && input !== selectedPlantDiet);
});
};
dietOptions.forEach(input => {
input.addEventListener("change", () => {
dietOptions.forEach(option => {
option.closest(".option-card")?.classList.remove("option-card--invalid");
});
updateDietAvailability(input);
});
});
updateDietAvailability();
}
/**
* Prüft alle Pflichtfelder ausser Radios und Checkboxen.
* Rückgabe:
@ -648,11 +719,17 @@ function registerCounterHandlers() {
/**
* Erhöht oder verringert den Wert eines Zahlenfelds.
* Der Wert darf dabei nie kleiner als das definierte Minimum werden.
* 0 bleibt als bewusster Startwert erlaubt; gültig ist erst eine Auswahl ab min.
*/
function updateCounterValue(input, change) {
const min = Number(input.min || 1);
const currentValue = Number(input.value || min);
const currentValue = Number(input.value || 0);
if (currentValue < min && change < 0) {
input.value = currentValue;
return;
}
input.value = Math.max(min, currentValue + change);
}
@ -765,7 +842,7 @@ function registerValidationFeedbackHandlers() {
* Setzt den Fokus auf ein bestimmtes Feld oder die erste Option einer Radio-Gruppe.
*/
function focusFieldByName(fieldName) {
const field = form.elements[fieldName];
const field = form.elements[fieldName] || document.getElementById(fieldName);
if (!field) return;
@ -862,6 +939,7 @@ function initEventCreationFlow() {
// Counter aktivieren
registerCounterHandlers();
registerDietConflictHandlers();
registerMenuBulletHandler();
registerValidationFeedbackHandlers();
registerReviewEditHandlers();