refactor(calendar): stack layout, wider grid, fixed cell height
- Calendar widget stays stacked (calendar above events list) - Grid widens to max-w-2xl with fixed h-10 cells (no aspect-square height growth) - Gap between cells: 0.5 → 1.5 - Button system extracted to shared btn-* utility classes in app.css - Replaced Google Calendar link + Google Maps with OSM - Removed google icon from iconify subset Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+35
-28
@@ -279,12 +279,12 @@ h4 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Content-Links: Himmel (Design System – Links) */
|
/* Content-Links: Himmel (Design System – Links) */
|
||||||
main a:not(.no-underline) {
|
main a:not(.no-underline):not([class*="btn"]) {
|
||||||
color: var(--color-link);
|
color: var(--color-link);
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
main a:not(.no-underline):hover {
|
main a:not(.no-underline):not([class*="btn"]):hover {
|
||||||
color: var(--color-himmel-600);
|
color: var(--color-himmel-600);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -317,9 +317,17 @@ main ol:not(.not-prose) li {
|
|||||||
list-style-type: decimal;
|
list-style-type: decimal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Buttons – gemeinsame Basis */
|
||||||
|
.btn-primary,
|
||||||
|
.btn-secondary,
|
||||||
|
.btn-tertiary,
|
||||||
|
.btn-outline {
|
||||||
|
@apply inline-flex items-center gap-1 no-underline whitespace-nowrap transition-colors duration-200 font-medium px-4 py-2 rounded-xs;
|
||||||
|
}
|
||||||
|
|
||||||
/* Buttons – Wald (Primary) */
|
/* Buttons – Wald (Primary) */
|
||||||
.btn-primary {
|
.btn-primary {
|
||||||
@apply inline-block no-underline transition-colors duration-200 font-medium px-4 py-2 rounded-xs shadow-sm bg-btn-bg text-btn-txt;
|
@apply shadow-sm bg-btn-bg text-btn-txt;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-primary:hover {
|
.btn-primary:hover {
|
||||||
@@ -328,13 +336,36 @@ main ol:not(.not-prose) li {
|
|||||||
|
|
||||||
/* Buttons – Stein outline (Secondary) */
|
/* Buttons – Stein outline (Secondary) */
|
||||||
.btn-secondary {
|
.btn-secondary {
|
||||||
@apply inline-block no-underline transition-colors duration-200 font-medium px-4 py-2 rounded-xs border border-stein-300 bg-white text-stein-700 shadow-sm;
|
@apply border border-stein-300 bg-white text-stein-700 shadow-sm;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-secondary:hover {
|
.btn-secondary:hover {
|
||||||
@apply bg-stein-100 border-stein-400;
|
@apply bg-stein-100 border-stein-400;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Buttons – Himmel (Tertiary) */
|
||||||
|
.btn-tertiary {
|
||||||
|
@apply shadow-sm bg-himmel-700 text-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-tertiary:hover {
|
||||||
|
@apply bg-himmel-800;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buttons – Stein-Border klein (Outline) */
|
||||||
|
.btn-outline {
|
||||||
|
@apply border border-stein-300 text-stein-700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-outline:hover {
|
||||||
|
@apply bg-stein-100;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Größen-Modifier — nach den btn-* definiert damit override greift */
|
||||||
|
.btn-sm {
|
||||||
|
@apply px-2 py-1 text-xs gap-1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Dekorativer Hintergrund mit zwei verschwommenen Verlaufs-Blobs (Wald → Erde).
|
/* Dekorativer Hintergrund mit zwei verschwommenen Verlaufs-Blobs (Wald → Erde).
|
||||||
Nutzung: Container mit relativer Positionierung; Blobs sitzen in ::before/::after.
|
Nutzung: Container mit relativer Positionierung; Blobs sitzen in ::before/::after.
|
||||||
Inhalt sollte z-Index >= 0 haben (Standard); Blobs liegen unter Stacking-Kontext. */
|
Inhalt sollte z-Index >= 0 haben (Standard); Blobs liegen unter Stacking-Kontext. */
|
||||||
@@ -571,30 +602,6 @@ footer a:hover,
|
|||||||
background: var(--color-container-breakout);
|
background: var(--color-container-breakout);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Kalender: Container Query – links Kalender, rechts Eventblock ab ~500px Containerbreite */
|
|
||||||
.calendar-block {
|
|
||||||
container-type: inline-size;
|
|
||||||
container-name: calendar;
|
|
||||||
}
|
|
||||||
@container calendar (min-width: 600px) {
|
|
||||||
.calendar-block .calendar-grid-wrapper {
|
|
||||||
grid-template-columns: auto 1fr;
|
|
||||||
}
|
|
||||||
.calendar-block .calendar-column {
|
|
||||||
min-width: 300px;
|
|
||||||
max-width: 450px;
|
|
||||||
}
|
|
||||||
.calendar-block .calendar-events-wrapper {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.calendar-block .calendar-events-panel {
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
border-top: none;
|
|
||||||
border-left: 1px solid var(--color-stein-200);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ── Print rules ────────────────────────────────────────────────────────────
|
/* ── Print rules ────────────────────────────────────────────────────────────
|
||||||
Goal: print only the article. `print:hidden` on chrome (header/footer/
|
Goal: print only the article. `print:hidden` on chrome (header/footer/
|
||||||
banner/breadcrumbs/comments) handles structural removal. The rules below
|
banner/breadcrumbs/comments) handles structural removal. The rules below
|
||||||
|
|||||||
+2
-20
@@ -122,25 +122,7 @@ export function downloadICS(item: CalendarItemICS, filename: string): void {
|
|||||||
URL.revokeObjectURL(url);
|
URL.revokeObjectURL(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Google-Calendar's render-template URL. Times must be UTC, in
|
/** OpenStreetMap search URL for an address or place name. */
|
||||||
* YYYYMMDDTHHMMSSZ format, joined by `/`. */
|
|
||||||
export function googleCalUrl(item: CalendarItemICS): string {
|
|
||||||
const start = item.start;
|
|
||||||
const end =
|
|
||||||
item.end ?? new Date(start.getTime() + 60 * 60 * 1000);
|
|
||||||
const dates = `${fmtICSDate(start)}/${fmtICSDate(end)}`;
|
|
||||||
const params = new URLSearchParams({
|
|
||||||
action: "TEMPLATE",
|
|
||||||
text: item.title,
|
|
||||||
dates,
|
|
||||||
});
|
|
||||||
if (item.description) params.set("details", item.description);
|
|
||||||
if (item.location) params.set("location", item.location);
|
|
||||||
return `https://calendar.google.com/calendar/render?${params.toString()}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Generic maps URL — Google Maps search by query. Safe with addresses
|
|
||||||
* or place names. */
|
|
||||||
export function mapsUrl(location: string): string {
|
export function mapsUrl(location: string): string {
|
||||||
return `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(location)}`;
|
return `https://www.openstreetmap.org/search?query=${encodeURIComponent(location)}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
import type { Translations } from "$lib/translations";
|
import type { Translations } from "$lib/translations";
|
||||||
import {
|
import {
|
||||||
downloadICS,
|
downloadICS,
|
||||||
googleCalUrl,
|
|
||||||
mapsUrl,
|
mapsUrl,
|
||||||
type CalendarItemICS,
|
type CalendarItemICS,
|
||||||
} from "$lib/calendar-ics";
|
} from "$lib/calendar-ics";
|
||||||
@@ -90,6 +89,17 @@
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Location kann string ODER {text, lat, lng}-Objekt sein
|
||||||
|
* (RustyCMS-Resolve liefert eventLocation-Struktur). */
|
||||||
|
function locationText(loc: unknown): string {
|
||||||
|
if (typeof loc === "string") return loc;
|
||||||
|
if (loc && typeof loc === "object") {
|
||||||
|
const t = (loc as { text?: unknown }).text;
|
||||||
|
if (typeof t === "string") return t;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
/** Normalised, sorted item list. Filters out malformed entries. */
|
/** Normalised, sorted item list. Filters out malformed entries. */
|
||||||
const items = $derived.by((): EventCardItem[] => {
|
const items = $derived.by((): EventCardItem[] => {
|
||||||
const raw = block.items ?? [];
|
const raw = block.items ?? [];
|
||||||
@@ -312,10 +322,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** "Show me this in the calendar" — used from accordion bodies for
|
/** "Show me this in the calendar" — used from accordion bodies for
|
||||||
* events that may be in a different month than the current view. */
|
* events that may be in a different month than the current view.
|
||||||
|
* Scrolls back up to the widget since it now sits above the list. */
|
||||||
function jumpToEventInGrid(ev: EventCardItem) {
|
function jumpToEventInGrid(ev: EventCardItem) {
|
||||||
currentMonth = new Date(ev.start.getFullYear(), ev.start.getMonth(), 1);
|
currentMonth = new Date(ev.start.getFullYear(), ev.start.getMonth(), 1);
|
||||||
selectedDay = dayKey(ev.start);
|
selectedDay = dayKey(ev.start);
|
||||||
|
widgetEl?.scrollIntoView({ block: "start", behavior: "smooth" });
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Visual urgency-bucket for the colored left-border on each event
|
/** Visual urgency-bucket for the colored left-border on each event
|
||||||
@@ -346,7 +358,7 @@
|
|||||||
start: ev.start,
|
start: ev.start,
|
||||||
end: ev.end ?? null,
|
end: ev.end ?? null,
|
||||||
description: ev.description ?? undefined,
|
description: ev.description ?? undefined,
|
||||||
location: ev.location ?? undefined,
|
location: locationText(ev.location) || undefined,
|
||||||
url: getEventLinkHref(ev.link) || undefined,
|
url: getEventLinkHref(ev.link) || undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -374,22 +386,12 @@
|
|||||||
t(T.calendar_weekday_so),
|
t(T.calendar_weekday_so),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let panelEl: HTMLElement | null = $state(null);
|
let widgetEl: HTMLElement | null = $state(null);
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
tickTimer = setInterval(() => {
|
tickTimer = setInterval(() => {
|
||||||
nowMs = Date.now();
|
nowMs = Date.now();
|
||||||
}, 60_000);
|
}, 60_000);
|
||||||
// After hydration, scroll the next-up event into view inside its
|
|
||||||
// panel (panel-local scroll, not page scroll — jumping the whole
|
|
||||||
// page when the block is below the fold would be hostile).
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
if (!panelEl) return;
|
|
||||||
const target = panelEl.querySelector<HTMLElement>(".event-item-next");
|
|
||||||
if (target) {
|
|
||||||
target.scrollIntoView({ block: "start", behavior: "auto" });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
if (tickTimer) clearInterval(tickTimer);
|
if (tickTimer) clearInterval(tickTimer);
|
||||||
@@ -406,10 +408,11 @@
|
|||||||
{@const live = liveStatus(item)}
|
{@const live = liveStatus(item)}
|
||||||
{@const title = item.title || t(T.calendar_untitled)}
|
{@const title = item.title || t(T.calendar_untitled)}
|
||||||
{@const urgency = urgencyOf(item)}
|
{@const urgency = urgencyOf(item)}
|
||||||
|
{@const locText = locationText(item.location)}
|
||||||
{@const hasBody = !!(
|
{@const hasBody = !!(
|
||||||
item.description ||
|
item.description ||
|
||||||
eventHref ||
|
eventHref ||
|
||||||
item.location ||
|
locText ||
|
||||||
item.isMultiDay
|
item.isMultiDay
|
||||||
)}
|
)}
|
||||||
<li
|
<li
|
||||||
@@ -438,14 +441,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="event-date-block shrink-0 w-14 text-center leading-tight">
|
<div class="event-date-block shrink-0 w-14 aspect-square flex flex-col items-center justify-between leading-tight bg-himmel-800 rounded-xs py-2">
|
||||||
<div class="text-[10px] uppercase tracking-wide text-stein-500">
|
<div class="text-[10px] uppercase tracking-wide text-himmel-300">
|
||||||
{item.start.toLocaleDateString("de-DE", { weekday: "short" })}
|
{item.start.toLocaleDateString("de-DE", { weekday: "short" })}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-2xl font-bold text-stein-900 tabular-nums">
|
<div class="text-2xl font-bold text-himmel-50 tabular-nums">
|
||||||
{item.start.getDate()}
|
{item.start.getDate()}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-[10px] uppercase tracking-wide text-stein-500">
|
<div class="text-[10px] uppercase tracking-wide text-himmel-300">
|
||||||
{item.start.toLocaleDateString("de-DE", { month: "short" })}
|
{item.start.toLocaleDateString("de-DE", { month: "short" })}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -468,10 +471,10 @@
|
|||||||
{item.timeStr}
|
{item.timeStr}
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
{#if item.location}
|
{#if locText}
|
||||||
<span class="inline-flex items-center gap-1 truncate max-w-[14rem]">
|
<span class="inline-flex items-center gap-1 truncate max-w-[14rem]">
|
||||||
<Icon icon="mdi:map-marker-outline" class="size-3 shrink-0" aria-hidden="true" />
|
<Icon icon="mdi:map-marker-outline" class="size-3 shrink-0" aria-hidden="true" />
|
||||||
<span class="truncate">{item.location}</span>
|
<span class="truncate">{locText}</span>
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
{#if item.tags && item.tags.length > 0}
|
{#if item.tags && item.tags.length > 0}
|
||||||
@@ -496,18 +499,6 @@
|
|||||||
<div class="text-[11px] uppercase tracking-wide text-stein-500 -mt-1">
|
<div class="text-[11px] uppercase tracking-wide text-stein-500 -mt-1">
|
||||||
{relativeDays(item.start)}
|
{relativeDays(item.start)}
|
||||||
</div>
|
</div>
|
||||||
{#if item.location}
|
|
||||||
<a
|
|
||||||
href={mapsUrl(item.location)}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
class="inline-flex items-center gap-1 text-[12px] text-himmel-700 hover:underline"
|
|
||||||
>
|
|
||||||
<Icon icon="mdi:map-marker" class="size-4 shrink-0" aria-hidden="true" />
|
|
||||||
{t(T.calendar_open_maps)}
|
|
||||||
<Icon icon="mdi:open-in-new" class="size-3" aria-hidden="true" />
|
|
||||||
</a>
|
|
||||||
{/if}
|
|
||||||
{#if item.description}
|
{#if item.description}
|
||||||
<p class="font-light text-stein-700 m-0! leading-relaxed">{item.description}</p>
|
<p class="font-light text-stein-700 m-0! leading-relaxed">{item.description}</p>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -526,33 +517,35 @@
|
|||||||
href={eventHref}
|
href={eventHref}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="inline-flex items-center gap-1 rounded-xs bg-himmel-700 text-himmel-50 px-2 py-1 font-medium hover:bg-himmel-800"
|
class="btn-tertiary btn-sm"
|
||||||
>
|
>
|
||||||
{t(T.calendar_more_info)}
|
{t(T.calendar_more_info)}
|
||||||
<Icon icon="mdi:open-in-new" class="size-3.5" aria-hidden="true" />
|
<Icon icon="mdi:open-in-new" class="size-3.5" aria-hidden="true" />
|
||||||
</a>
|
</a>
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if locText}
|
||||||
|
<a
|
||||||
|
href={mapsUrl(locText)}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
class="btn-outline btn-sm"
|
||||||
|
>
|
||||||
|
<Icon icon="mdi:map-marker" class="size-3.5 shrink-0" aria-hidden="true" />
|
||||||
|
{t(T.calendar_open_maps)}
|
||||||
|
</a>
|
||||||
|
{/if}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => downloadICS(toICS(item), fileSlug(title))}
|
onclick={() => downloadICS(toICS(item), fileSlug(title))}
|
||||||
class="inline-flex items-center gap-1 rounded-xs border border-stein-300 px-2 py-1 text-stein-700 hover:bg-stein-100"
|
class="btn-outline btn-sm"
|
||||||
>
|
>
|
||||||
<Icon icon="mdi:download" class="size-3.5" aria-hidden="true" />
|
<Icon icon="mdi:download" class="size-3.5" aria-hidden="true" />
|
||||||
{t(T.calendar_download_ics)}
|
{t(T.calendar_download_ics)}
|
||||||
</button>
|
</button>
|
||||||
<a
|
|
||||||
href={googleCalUrl(toICS(item))}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
class="inline-flex items-center gap-1 rounded-xs border border-stein-300 px-2 py-1 text-stein-700 hover:bg-stein-100"
|
|
||||||
>
|
|
||||||
<Icon icon="mdi:google" class="size-3.5" aria-hidden="true" />
|
|
||||||
{t(T.calendar_add_to_google)}
|
|
||||||
</a>
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onclick={() => jumpToEventInGrid(item)}
|
onclick={() => jumpToEventInGrid(item)}
|
||||||
class="inline-flex items-center gap-1 rounded-xs border border-stein-300 px-2 py-1 text-stein-700 hover:bg-stein-100"
|
class="btn-outline btn-sm"
|
||||||
>
|
>
|
||||||
<Icon icon="mdi:calendar-search" class="size-3.5" aria-hidden="true" />
|
<Icon icon="mdi:calendar-search" class="size-3.5" aria-hidden="true" />
|
||||||
{t(T.calendar_jump_to_month)}
|
{t(T.calendar_jump_to_month)}
|
||||||
@@ -597,9 +590,14 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="calendar-grid-wrapper grid w-full grid-cols-1 md:grid-cols-2 gap-0">
|
<!-- Kalender-Widget: oben, kompakt auf Mobile-Breite gecappt (sonst
|
||||||
<!-- Links: Kalender -->
|
werden aspect-square cells auf Desktop gigantisch). Monat-Nav +
|
||||||
<div class="calendar-column min-h-full p-4 flex flex-col bg-himmel-800 text-himmel-100 shadow-sm">
|
Tag-Grid. Klick auf Tag filtert Liste unten. -->
|
||||||
|
<div
|
||||||
|
bind:this={widgetEl}
|
||||||
|
class="calendar-widget bg-himmel-800 text-himmel-100 p-4"
|
||||||
|
>
|
||||||
|
<div class="mx-auto max-w-2xl">
|
||||||
<div class="flex items-center justify-between gap-2 mb-4">
|
<div class="flex items-center justify-between gap-2 mb-4">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -623,13 +621,13 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-7 gap-0.5 text-center text-sm">
|
<div class="grid grid-cols-7 gap-1.5 text-center text-sm">
|
||||||
{#each weekdays as w}
|
{#each weekdays as w}
|
||||||
<div class="py-1.5 text-xs font-medium text-himmel-100 opacity-80">{w}</div>
|
<div class="py-1.5 text-xs font-medium text-himmel-100 opacity-80">{w}</div>
|
||||||
{/each}
|
{/each}
|
||||||
{#each calendarDays as d}
|
{#each calendarDays as d}
|
||||||
{#if d === null}
|
{#if d === null}
|
||||||
<div class="aspect-square" aria-hidden="true"></div>
|
<div class="h-10" aria-hidden="true"></div>
|
||||||
{:else}
|
{:else}
|
||||||
{@const key = dayKey(d)}
|
{@const key = dayKey(d)}
|
||||||
{@const isCurrentMonth = d.getMonth() === month}
|
{@const isCurrentMonth = d.getMonth() === month}
|
||||||
@@ -641,7 +639,7 @@
|
|||||||
{#if hasEvents}
|
{#if hasEvents}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="relative aspect-square rounded-xs flex flex-col items-center justify-center min-w-0 transition-colors cursor-pointer {isCurrentMonth
|
class="relative h-10 rounded-xs flex flex-col items-center justify-center min-w-0 transition-colors cursor-pointer {isCurrentMonth
|
||||||
? 'text-himmel-100 hover:bg-himmel-700'
|
? 'text-himmel-100 hover:bg-himmel-700'
|
||||||
: 'text-himmel-100 opacity-60 hover:opacity-80'} bg-himmel-700/50 font-semibold hover:bg-himmel-600 {isSelected
|
: 'text-himmel-100 opacity-60 hover:opacity-80'} bg-himmel-700/50 font-semibold hover:bg-himmel-600 {isSelected
|
||||||
? 'ring-2 ring-himmel-400 bg-himmel-600'
|
? 'ring-2 ring-himmel-400 bg-himmel-600'
|
||||||
@@ -661,7 +659,7 @@
|
|||||||
</button>
|
</button>
|
||||||
{:else}
|
{:else}
|
||||||
<div
|
<div
|
||||||
class="relative aspect-square rounded-xs flex flex-col items-center justify-center min-w-0 cursor-default {isCurrentMonth
|
class="relative h-10 rounded-xs flex flex-col items-center justify-center min-w-0 cursor-default {isCurrentMonth
|
||||||
? 'text-himmel-100'
|
? 'text-himmel-100'
|
||||||
: 'text-himmel-100 opacity-50'} {isToday ? 'today-cell' : ''}"
|
: 'text-himmel-100 opacity-50'} {isToday ? 'today-cell' : ''}"
|
||||||
aria-current={isToday ? "date" : undefined}
|
aria-current={isToday ? "date" : undefined}
|
||||||
@@ -676,15 +674,12 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Rechts: alle zukünftigen Termine als Accordion. Datum + Headline
|
<!-- Events: voll Breite, unten. Akkordeon-Liste, in natürlichem
|
||||||
im summary, Beschreibung + Aktionen im body. Kalender-Klick
|
Page-Flow (kein internes Scrollen). Kalender-Klick filtert auf
|
||||||
filtert auf einen Tag; ohne Filter zeigt Liste alles ab heute,
|
einen Tag; ohne Filter Liste ab heute, gruppiert nach Tag. -->
|
||||||
gruppiert nach Tag mit großzügigem header. -->
|
<div class="calendar-events-panel bg-stein-0 border-t border-stein-200">
|
||||||
<div class="calendar-events-wrapper min-h-0 flex flex-col" bind:this={panelEl}>
|
|
||||||
<div
|
|
||||||
class="calendar-events-panel border-t md:border-t-0 md:border-l border-stein-200 bg-stein-0 flex flex-col overflow-hidden flex-1 min-h-0"
|
|
||||||
>
|
|
||||||
{#if items.length === 0}
|
{#if items.length === 0}
|
||||||
<p class="text-sm text-stein-500 p-4">{t(T.calendar_no_events)}</p>
|
<p class="text-sm text-stein-500 p-4">{t(T.calendar_no_events)}</p>
|
||||||
{:else if selectedDay}
|
{:else if selectedDay}
|
||||||
@@ -705,7 +700,7 @@
|
|||||||
{t(T.calendar_clear_filter)}
|
{t(T.calendar_clear_filter)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<ul class="m-0! p-0! list-none flex-1 min-h-0 overflow-auto">
|
<ul class="m-0! p-0! list-none">
|
||||||
{#each selectedDayEvents as item}
|
{#each selectedDayEvents as item}
|
||||||
{@render eventAccordion(item, true, false)}
|
{@render eventAccordion(item, true, false)}
|
||||||
{/each}
|
{/each}
|
||||||
@@ -719,7 +714,7 @@
|
|||||||
{t(T.calendar_event_count, { n: futureEvents.length })}
|
{t(T.calendar_event_count, { n: futureEvents.length })}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<ul class="m-0! p-0! list-none flex-1 min-h-0 overflow-auto">
|
<ul class="m-0! p-0! list-none">
|
||||||
{#each futureEventsByDay as group, gIdx}
|
{#each futureEventsByDay as group, gIdx}
|
||||||
<li class="day-group">
|
<li class="day-group">
|
||||||
<div class="px-3 py-1 bg-himmel-50/60 text-[11px] uppercase tracking-wide font-medium text-stein-600 sticky top-0 z-10 backdrop-blur-sm">
|
<div class="px-3 py-1 bg-himmel-50/60 text-[11px] uppercase tracking-wide font-medium text-stein-600 sticky top-0 z-10 backdrop-blur-sm">
|
||||||
@@ -741,7 +736,7 @@
|
|||||||
<span class="font-medium uppercase tracking-wide">{t(T.calendar_past_events)}</span>
|
<span class="font-medium uppercase tracking-wide">{t(T.calendar_past_events)}</span>
|
||||||
<span class="text-stein-500 tabular-nums">· {pastEvents.length}</span>
|
<span class="text-stein-500 tabular-nums">· {pastEvents.length}</span>
|
||||||
</summary>
|
</summary>
|
||||||
<ul class="m-0! p-0! list-none max-h-72 overflow-auto opacity-80">
|
<ul class="m-0! p-0! list-none opacity-80">
|
||||||
{#each pastEvents as item}
|
{#each pastEvents as item}
|
||||||
{@render eventAccordion(item, false, false)}
|
{@render eventAccordion(item, false, false)}
|
||||||
{/each}
|
{/each}
|
||||||
@@ -757,7 +752,7 @@
|
|||||||
<span class="font-medium uppercase tracking-wide">{t(T.calendar_past_events)}</span>
|
<span class="font-medium uppercase tracking-wide">{t(T.calendar_past_events)}</span>
|
||||||
<span class="text-stein-500 tabular-nums">· {pastEvents.length}</span>
|
<span class="text-stein-500 tabular-nums">· {pastEvents.length}</span>
|
||||||
</summary>
|
</summary>
|
||||||
<ul class="m-0! p-0! list-none flex-1 min-h-0 overflow-auto opacity-80">
|
<ul class="m-0! p-0! list-none opacity-80">
|
||||||
{#each pastEvents as item}
|
{#each pastEvents as item}
|
||||||
{@render eventAccordion(item, false, false)}
|
{@render eventAccordion(item, false, false)}
|
||||||
{/each}
|
{/each}
|
||||||
@@ -767,8 +762,6 @@
|
|||||||
<p class="text-sm text-stein-500 p-4">{t(T.calendar_no_future_events)}</p>
|
<p class="text-sm text-stein-500 p-4">{t(T.calendar_no_future_events)}</p>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -831,8 +824,8 @@
|
|||||||
.calendar-block .day-group + .day-group {
|
.calendar-block .day-group + .day-group {
|
||||||
border-top: 4px solid var(--color-stein-100, #ede8df);
|
border-top: 4px solid var(--color-stein-100, #ede8df);
|
||||||
}
|
}
|
||||||
/* Print: alle <details> geöffnet, Chevrons aus, Akzent-Farben in
|
/* Print: alle <details> geöffnet, Chevrons aus, Day-Groups nicht
|
||||||
Graustufen damit der Druck lesbar bleibt. */
|
splitten. */
|
||||||
@media print {
|
@media print {
|
||||||
.calendar-block details > *:not(summary) {
|
.calendar-block details > *:not(summary) {
|
||||||
display: block !important;
|
display: block !important;
|
||||||
@@ -843,9 +836,5 @@
|
|||||||
.calendar-block .day-group {
|
.calendar-block .day-group {
|
||||||
break-inside: avoid;
|
break-inside: avoid;
|
||||||
}
|
}
|
||||||
.calendar-block .calendar-events-panel {
|
|
||||||
overflow: visible !important;
|
|
||||||
max-height: none !important;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -72,9 +72,6 @@
|
|||||||
"download": {
|
"download": {
|
||||||
"body": "<path fill=\"currentColor\" d=\"M5 20h14v-2H5m14-9h-4V3H9v6H5l7 7z\"/>"
|
"body": "<path fill=\"currentColor\" d=\"M5 20h14v-2H5m14-9h-4V3H9v6H5l7 7z\"/>"
|
||||||
},
|
},
|
||||||
"google": {
|
|
||||||
"body": "<path fill=\"currentColor\" d=\"M21.35 11.1h-9.17v2.73h6.51c-.33 3.81-3.5 5.44-6.5 5.44C8.36 19.27 5 16.25 5 12c0-4.1 3.2-7.27 7.2-7.27c3.09 0 4.9 1.97 4.9 1.97L19 4.72S16.56 2 12.1 2C6.42 2 2.03 6.8 2.03 12c0 5.05 4.13 10 10.22 10c5.35 0 9.25-3.67 9.25-9.09c0-1.15-.15-1.81-.15-1.81\"/>"
|
|
||||||
},
|
|
||||||
"calendar-search": {
|
"calendar-search": {
|
||||||
"body": "<path fill=\"currentColor\" d=\"M15.5 12c2.5 0 4.5 2 4.5 4.5c0 .88-.25 1.71-.69 2.4l3.08 3.1L21 23.39l-3.12-3.07c-.69.43-1.51.68-2.38.68c-2.5 0-4.5-2-4.5-4.5s2-4.5 4.5-4.5m0 2a2.5 2.5 0 0 0-2.5 2.5a2.5 2.5 0 0 0 2.5 2.5a2.5 2.5 0 0 0 2.5-2.5a2.5 2.5 0 0 0-2.5-2.5M19 8H5v11h4.5c.31.75.76 1.42 1.31 2H5a2 2 0 0 1-2-2V5c0-1.11.89-2 2-2h1V1h2v2h8V1h2v2h1a2 2 0 0 1 2 2v8.03c-.5-.81-1.2-1.49-2-2.03z\"/>"
|
"body": "<path fill=\"currentColor\" d=\"M15.5 12c2.5 0 4.5 2 4.5 4.5c0 .88-.25 1.71-.69 2.4l3.08 3.1L21 23.39l-3.12-3.07c-.69.43-1.51.68-2.38.68c-2.5 0-4.5-2-4.5-4.5s2-4.5 4.5-4.5m0 2a2.5 2.5 0 0 0-2.5 2.5a2.5 2.5 0 0 0 2.5 2.5a2.5 2.5 0 0 0 2.5-2.5a2.5 2.5 0 0 0-2.5-2.5M19 8H5v11h4.5c.31.75.76 1.42 1.31 2H5a2 2 0 0 1-2-2V5c0-1.11.89-2 2-2h1V1h2v2h8V1h2v2h1a2 2 0 0 1 2 2v8.03c-.5-.81-1.2-1.49-2-2.03z\"/>"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -121,7 +121,6 @@ const TRANSLATION_KEYS = [
|
|||||||
"calendar_no_future_events",
|
"calendar_no_future_events",
|
||||||
"calendar_clear_filter",
|
"calendar_clear_filter",
|
||||||
"calendar_event_count", // {{n}}
|
"calendar_event_count", // {{n}}
|
||||||
"calendar_add_to_google",
|
|
||||||
"calendar_download_ics",
|
"calendar_download_ics",
|
||||||
"calendar_open_maps",
|
"calendar_open_maps",
|
||||||
"calendar_show_past",
|
"calendar_show_past",
|
||||||
|
|||||||
Reference in New Issue
Block a user