fix(calendar): fix QuickEventOverlay z-index stacking issue

- Add portal action to move overlay to body, escaping parent stacking contexts
- Set z-index: 99999 via inline style to ensure overlay appears above all UI elements
- Remove z-index: 0 from main-content that was creating a stacking context
- Overlay now correctly displays above DateStrip, Toolbar, and InputBar

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Till-JS 2025-12-12 12:15:01 +01:00
parent 8a75b349dc
commit f51708d75a
5 changed files with 44 additions and 20 deletions

View file

@ -184,6 +184,16 @@ export class EventService {
conditions.push(inArray(events.calendarId, query.calendarIds));
}
// Search filter - search in title and description
if (query.search) {
conditions.push(
or(
ilike(events.title, `%${query.search}%`),
ilike(events.description, `%${query.search}%`)
) as any
);
}
const result = await this.db
.select({
event: events,

View file

@ -23,17 +23,22 @@ export async function getEvents(params: QueryEventsParams) {
if (params.search) {
searchParams.set('search', params.search);
}
return fetchApi<CalendarEvent[]>(`/events?${searchParams.toString()}`);
const result = await fetchApi<{ events: CalendarEvent[] }>(`/events?${searchParams.toString()}`);
if (result.error || !result.data) {
return { data: null, error: result.error };
}
return { data: result.data.events, error: null };
}
export async function searchEvents(query: string, limit: number = 10) {
// Search events within the next year
const now = new Date();
// Search events within a wide range (1 year past to 1 year future)
const oneYearAgo = new Date();
oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1);
const oneYearFromNow = new Date();
oneYearFromNow.setFullYear(oneYearFromNow.getFullYear() + 1);
return getEvents({
startDate: now.toISOString(),
startDate: oneYearAgo.toISOString(),
endDate: oneYearFromNow.toISOString(),
search: query,
});

View file

@ -8,6 +8,16 @@
import { de } from 'date-fns/locale';
import { tick, onMount, onDestroy } from 'svelte';
// Portal action - moves element to body to escape stacking contexts
function portal(node: HTMLElement) {
document.body.appendChild(node);
return {
destroy() {
node.remove();
},
};
}
interface Props {
startTime?: Date;
event?: CalendarEvent;
@ -465,9 +475,11 @@
<svelte:window onkeydown={handleKeydown} />
<!-- Overlay (no blocking backdrop - allows interaction with calendar) -->
<!-- Portal to body to escape stacking contexts -->
<div
use:portal
class="quick-event-overlay"
style={overlayStyle}
style="{overlayStyle} z-index: 99999;"
role="dialog"
aria-modal="true"
aria-label={isEditMode ? 'Termin bearbeiten' : 'Termin erstellen'}
@ -817,7 +829,7 @@
box-shadow:
0 20px 60px rgba(0, 0, 0, 0.2),
0 4px 16px rgba(0, 0, 0, 0.1);
z-index: 1001;
z-index: 99999 !important;
display: flex;
flex-direction: column;
animation: slideIn 150ms ease-out;

View file

@ -372,7 +372,6 @@
.main-content {
transition: all 300ms ease;
position: relative;
z-index: 0;
/* Space for QuickInputBar at bottom */
padding-bottom: calc(80px + env(safe-area-inset-bottom));
}

View file

@ -411,12 +411,15 @@
<style>
.quick-input-bar {
position: fixed;
bottom: 70px; /* Above PillNav on desktop */
top: 0;
left: 0;
right: 0;
z-index: 90;
padding: 0.75rem 1rem;
padding-top: calc(0.75rem + env(safe-area-inset-top));
pointer-events: none;
/* Fixed height to prevent layout shift when results appear */
height: 72px;
}
.input-container,
@ -430,7 +433,7 @@
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.5rem 1rem;
padding: 0.75rem 1.25rem;
background: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
@ -442,6 +445,8 @@
0 4px 6px -1px rgba(0, 0, 0, 0.1),
0 2px 4px -1px rgba(0, 0, 0, 0.06);
transition: all 0.2s ease;
/* Fixed height to prevent size changes */
height: 54px;
}
:global(.dark) .input-container {
@ -481,7 +486,7 @@
left: 0;
right: 0;
bottom: 0;
font-size: 1rem;
font-size: 1.125rem;
font-family: inherit;
white-space: pre;
pointer-events: none;
@ -495,7 +500,7 @@
width: 100%;
border: none;
background: transparent;
font-size: 1rem;
font-size: 1.125rem;
font-family: inherit;
color: transparent;
caret-color: hsl(var(--color-foreground));
@ -583,11 +588,11 @@
/* Results Panel */
.results-panel {
position: absolute;
bottom: 100%;
top: 100%;
left: 1rem;
right: 1rem;
max-width: 700px;
margin: 0 auto 0.5rem;
margin: 0.5rem auto 0;
max-height: 320px;
overflow-y: auto;
background: rgba(255, 255, 255, 0.95);
@ -747,11 +752,4 @@
transform: rotate(360deg);
}
}
/* Mobile adjustments */
@media (max-width: 768px) {
.quick-input-bar {
bottom: calc(70px + env(safe-area-inset-bottom));
}
}
</style>