{#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 )}
{#if item.isMultiDay && item.end}
{item.start.toLocaleDateString("de-DE", { month: "short", })}
{item.start.getDate()}–{item.end.getDate()}
{item.start.getFullYear() === item.end.getFullYear() ? item.end.toLocaleDateString("de-DE", { month: "short", }) : item.end.getFullYear()}
{:else}
{item.start.toLocaleDateString("de-DE", { weekday: "short", })}
{item.start.getDate()}
{item.start.toLocaleDateString("de-DE", { month: "short", })}
{/if}
{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}
{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: 160, height: 160, fit: "cover", })} {@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 w-24 h-24 rounded overflow-hidden border border-stein-200 shrink-0 hover:opacity-90 transition-opacity" aria-label="Bild vergrößern" >
{/if} {/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 herunterladen"}
{/if}
downloadICS(toICS(item), fileSlug(title))} class="btn-outline btn-sm group" aria-label={t(T.calendar_download_ics)} >
{t(T.calendar_download_ics)}
{t(T.calendar_add_to_google)}
{#if feedHost} {@const shareKey = item._slug ?? String(item.start.getTime())} {@const copied = copyToastKey === shareKey}
copyEventLink(item)} class="btn-outline btn-sm group" aria-label={t(T.post_action_copy)} >
{copied ? t(T.post_action_copied) : t(T.post_action_copy)}
{@const shared = shareToastKey === shareKey}
shareEvent(item, title, locText)} class="btn-outline btn-sm group" aria-label={t(T.calendar_share_aria)} >
{shared ? t(T.calendar_share_copied) : t(T.calendar_share)}
{/if}
jumpToEventInGrid(item)} class="btn-outline btn-sm group" aria-label={t(T.calendar_jump_to_month)} >
{t(T.calendar_jump_to_month)}
{/snippet} {#if block.title}
{block.title}
{/if} {#if allTags.length > 0}
{t(T.calendar_filter_by_tag)}
(activeTag = null)} class="text-[11px] px-2 py-0.5 rounded-xs border {!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="text-[11px] px-2 py-0.5 rounded-xs border {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}
{#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}