{#snippet eventAccordion( item: EventCardItem, openByDefault: boolean, isNext: boolean, )} {@const eventHref = getEventLinkHref(item.link)} {@const live = liveStatus(item)} {@const title = item.title || t(T.calendar_untitled)} {@const urgency = urgencyOf(item)} {@const locText = locationText(item.location)} {@const hasBody = !!( item.description || eventHref || locText || item.isMultiDay )}
{title}
{#if live}
{live}
{/if}
{#if item.timeStr && !item.isMultiDay}
{item.timeStr}
{/if} {#if locText}
{locText}
{/if} {#if item.tags && item.tags.length > 0} {#each item.tags.slice(0, 2) as tg}
{tg}
{/each} {#if item.tags.length > 2}
+{item.tags.length - 2}
{/if} {/if}
{relativeDays(item.start)}
{#if item.description}
{@html marked.parse(item.description)}
{/if} {#if item.tags && item.tags.length > 2}
{#each item.tags as tg}
{tg}
{/each}
{/if} {#if item.image} {@const imageField = extractCmsImageField(item.image)} {#if imageField} {@const thumbSrc = getCmsImageUrl(imageField.url, { width: 240, height: 240, fit: "contain", })} {@const fullSrc = getCmsImageUrl(imageField.url, { width: 1400, fit: "contain", })} {@const imageAlt = imageField.alt ?? (typeof item.image === "object" && "description" in item.image ? (item.image.description ?? "") : "")}
(modalImage = { src: fullSrc, alt: imageAlt, })} class="group relative flex max-w-24 max-h-24 shrink-0 items-center justify-center overflow-hidden hover:opacity-90 transition-opacity" aria-label="Bild vergrößern" >
{/if} {/if}
{#if item._slug || eventHref || locText || item.attachment?.src}
{#if item._slug}
Details
{/if} {#if eventHref}
{t(T.calendar_more_info)}
{/if} {#if locText}
{t(T.calendar_open_maps)}
{/if} {#if item.attachment?.src} {@const dlSrc = `/cms-files?src=${encodeURIComponent(item.attachment.src)}&dl=1`}
{item.attachment.label || "Anhang"}
{/if}
{/if}
Kalender
downloadICS(toICS(item), fileSlug(title))} class="inline-flex items-center gap-1 rounded px-1.5 py-1 text-[11px] text-stein-700 hover:bg-white" title={t(T.calendar_download_ics)} aria-label={t(T.calendar_download_ics)} >
ICS
Google
{#if feedHost} {@const shareKey = item._slug ?? String(item.start.getTime())} {@const copied = copyToastKey === shareKey} {@const shared = shareToastKey === shareKey}
Teilen
copyEventLink(item)} class="inline-flex items-center gap-1 rounded px-1.5 py-1 text-[11px] text-stein-700 hover:bg-white" title={t(T.post_action_copy)} aria-label={t(T.post_action_copy)} >
{copied ? t(T.post_action_copied) : "Link"}
shareEvent(item, title, locText)} class="inline-flex items-center gap-1 rounded px-1.5 py-1 text-[11px] text-stein-700 hover:bg-white" title={t(T.calendar_share_aria)} aria-label={t(T.calendar_share_aria)} >
{shared ? t(T.calendar_share_copied) : t(T.calendar_share)}
{#if item._slug}
openSocial(item)} class="inline-flex items-center gap-1 rounded px-1.5 py-1 text-[11px] text-stein-700 hover:bg-white" title="Social-Bild Vorschau" aria-label="Social-Bild Vorschau" >
Bild
{/if}
openQr(item)} class="inline-flex items-center gap-1 rounded px-1.5 py-1 text-[11px] text-stein-700 hover:bg-white" title="QR-Code anzeigen & herunterladen" aria-label="QR-Code anzeigen & herunterladen" >
QR
{/if}
jumpToEventInGrid(item)} class="inline-flex items-center gap-1 rounded px-1.5 py-1 text-[11px] text-stein-500 hover:bg-white" title={t(T.calendar_jump_to_month)} aria-label={t(T.calendar_jump_to_month)} >
Im Grid
{/snippet} {#if block.title}
{block.title}
{/if} {#if allTags.length > 0}
{t(T.calendar_filter_by_tag)}
(activeTag = null)} class="chip {!activeTag ? 'bg-himmel-700 text-himmel-50 border-himmel-700' : 'border-stein-300 text-stein-700 hover:bg-stein-100'}" aria-pressed={!activeTag} > {t(T.calendar_all_tags)}
{#each allTags as tag}
(activeTag = activeTag === tag ? null : tag)} class="chip {activeTag === tag ? 'bg-himmel-700 text-himmel-50 border-himmel-700' : 'border-stein-300 text-stein-700 hover:bg-stein-100'}" aria-pressed={activeTag === tag} > {tag}
{/each}
{/if} {#if feedHost}
Termin melden
{#if isApplePlatform}
{t(T.calendar_subscribe)}
{:else}
{t(T.calendar_subscribe)}
{/if} {#if items.length > 0}
{t(T.calendar_export_all)}
{/if}
{/if}
{monthLabel}
{#each weekdays as w, i}
{w}
{/each} {#each calendarDays as d} {#if d === null}
{:else} {@const key = dayKey(d)} {@const isCurrentMonth = d.getMonth() === month} {@const dayEvents = eventsByDay.get(key) ?? []} {@const hasEvents = dayEvents.length > 0} {@const isSelected = selectedDay === key} {@const isToday = key === todayKey} {@const isFuture = key >= todayKey} {@const isWeekend = d.getDay() === 0 || d.getDay() === 6} {#if hasEvents}
selectDay(key)} onmouseenter={(e) => showTooltip(e, key)} onmouseleave={hideTooltip} aria-pressed={isSelected} aria-current={isToday ? "date" : undefined} aria-label="{d.toLocaleDateString('de-DE', { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric', })}, {dayEvents.length} {t( T.calendar_event_count, { n: dayEvents.length }, ).replace(/\d+\s*/, '')}" >
{d.getDate()}
{dayEvents.length}
{:else}
{d.getDate()}
{#if isToday}
{t(T.calendar_today_marker)}
{/if}
{/if} {/if} {/each}
{#if hasHover && tooltipDay && tooltipEvents.length > 0}
{#each tooltipEvents.slice(0, 3) as ev}
{ev.title || t(T.calendar_untitled)}
{#if ev.timeStr}
{ev.timeStr}
{/if}
{/each} {#if tooltipEvents.length > 3}
+{tooltipEvents.length - 3} weitere
{/if}
{/if}
{#if items.length === 0}
{t(T.calendar_no_events)}
{:else if selectedDay}
{new Date(selectedDay + "T12:00:00").toLocaleDateString( "de-DE", { weekday: "long", day: "numeric", month: "long", }, )}
(selectedDay = null)} class="inline-flex items-center gap-1 text-[11px] text-himmel-700 hover:text-himmel-900 hover:underline shrink-0" >
{t(T.calendar_clear_filter)}
{#each selectedDayEvents as item} {@render eventAccordion(item, true, false)} {/each}
{:else if futureEventsByDay.length > 0}
{t(T.calendar_all_upcoming)}
{t(T.calendar_event_count, { n: futureEvents.length })}
{#each futureEventsByDay as group, gIdx}
{fmtGroupHeader(group.date)}
· {relativeDays(group.date)}
{#each group.events as item, eIdx} {@render eventAccordion( item, gIdx === 0, gIdx === 0 && eIdx === 0, )} {/each}
{/each}
{#if pastEvents.length > 0}
{t(T.calendar_past_events)}
· {pastEvents.length}
{#each pastEvents as item} {@render eventAccordion(item, false, false)} {/each}
{/if} {:else if pastEvents.length > 0}
{t(T.calendar_past_events)}
· {pastEvents.length}
{#each pastEvents as item} {@render eventAccordion(item, false, false)} {/each}
{:else}
{t(T.calendar_no_future_events)}
{/if}
{#if modalImage}
(modalImage = null)} /> {/if} {#if socialModal}
{/if} {#if qrModal}
(qrModal = null)} /> {/if}