fix: calendar invitation picker clipping #250
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||||
|
import { createPortal } from 'react-dom';
|
||||||
import {
|
import {
|
||||||
ArrowRight,
|
ArrowRight,
|
||||||
Calendar,
|
Calendar,
|
||||||
@@ -371,6 +372,8 @@ export function CalendarInvitationBanner({ email }: CalendarInvitationBannerProp
|
|||||||
const [actionError, setActionError] = useState<string | null>(null);
|
const [actionError, setActionError] = useState<string | null>(null);
|
||||||
const [isProcessing, setIsProcessing] = useState(false);
|
const [isProcessing, setIsProcessing] = useState(false);
|
||||||
const [showCalendarPicker, setShowCalendarPicker] = useState(false);
|
const [showCalendarPicker, setShowCalendarPicker] = useState(false);
|
||||||
|
const [pickerPosition, setPickerPosition] = useState<{ top: number; left: number } | null>(null);
|
||||||
|
const pickerTriggerRef = useRef<HTMLButtonElement>(null);
|
||||||
const [selectedCalendarId, setSelectedCalendarId] = useState<string>('');
|
const [selectedCalendarId, setSelectedCalendarId] = useState<string>('');
|
||||||
const [rawIcsMethod, setRawIcsMethod] = useState<InvitationMethod>('unknown');
|
const [rawIcsMethod, setRawIcsMethod] = useState<InvitationMethod>('unknown');
|
||||||
const [isCollapsed, setIsCollapsed] = useState(true);
|
const [isCollapsed, setIsCollapsed] = useState(true);
|
||||||
@@ -437,6 +440,17 @@ export function CalendarInvitationBanner({ email }: CalendarInvitationBannerProp
|
|||||||
}
|
}
|
||||||
}, [calendars, selectedCalendarId]);
|
}, [calendars, selectedCalendarId]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!showCalendarPicker) return;
|
||||||
|
const close = () => setShowCalendarPicker(false);
|
||||||
|
window.addEventListener('scroll', close, true);
|
||||||
|
window.addEventListener('resize', close);
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('scroll', close, true);
|
||||||
|
window.removeEventListener('resize', close);
|
||||||
|
};
|
||||||
|
}, [showCalendarPicker]);
|
||||||
|
|
||||||
if (!attachment || !calendarInvitationParsingEnabled) return null;
|
if (!attachment || !calendarInvitationParsingEnabled) return null;
|
||||||
|
|
||||||
const detectedMethod = parsedEvent ? getInvitationMethod(parsedEvent, { email, attachment }) : 'unknown';
|
const detectedMethod = parsedEvent ? getInvitationMethod(parsedEvent, { email, attachment }) : 'unknown';
|
||||||
@@ -966,14 +980,23 @@ export function CalendarInvitationBanner({ email }: CalendarInvitationBannerProp
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{supportsCalendar && !existingEvent && allowsImport && !isResponseOnly && !isCancellation && (
|
{supportsCalendar && !existingEvent && allowsImport && !isResponseOnly && !isCancellation && (
|
||||||
<div className="relative">
|
<>
|
||||||
<button
|
<button
|
||||||
|
ref={pickerTriggerRef}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (calendars.length <= 1) {
|
if (calendars.length <= 1) {
|
||||||
handleImport();
|
handleImport();
|
||||||
} else {
|
return;
|
||||||
setShowCalendarPicker(!showCalendarPicker);
|
|
||||||
}
|
}
|
||||||
|
if (showCalendarPicker) {
|
||||||
|
setShowCalendarPicker(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (pickerTriggerRef.current) {
|
||||||
|
const rect = pickerTriggerRef.current.getBoundingClientRect();
|
||||||
|
setPickerPosition({ top: rect.bottom + 4, left: rect.left });
|
||||||
|
}
|
||||||
|
setShowCalendarPicker(true);
|
||||||
}}
|
}}
|
||||||
disabled={isProcessing}
|
disabled={isProcessing}
|
||||||
className="inline-flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground px-3 py-1.5 rounded-md border border-border hover:bg-muted transition-colors min-h-[36px] disabled:opacity-50"
|
className="inline-flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground px-3 py-1.5 rounded-md border border-border hover:bg-muted transition-colors min-h-[36px] disabled:opacity-50"
|
||||||
@@ -983,8 +1006,11 @@ export function CalendarInvitationBanner({ email }: CalendarInvitationBannerProp
|
|||||||
{calendars.length > 1 && <ChevronDown className="w-3 h-3" />}
|
{calendars.length > 1 && <ChevronDown className="w-3 h-3" />}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{showCalendarPicker && calendars.length > 1 && (
|
{showCalendarPicker && calendars.length > 1 && pickerPosition && typeof document !== 'undefined' && createPortal(
|
||||||
<div className="absolute left-0 top-full mt-1 w-52 bg-background rounded-lg shadow-lg border border-border z-10 py-1">
|
<div
|
||||||
|
className="fixed w-52 bg-background rounded-lg shadow-lg border border-border z-50 py-1"
|
||||||
|
style={{ top: pickerPosition.top, left: pickerPosition.left }}
|
||||||
|
>
|
||||||
<div className="px-3 py-1.5 text-xs font-medium text-muted-foreground">
|
<div className="px-3 py-1.5 text-xs font-medium text-muted-foreground">
|
||||||
{t('select_calendar')}
|
{t('select_calendar')}
|
||||||
</div>
|
</div>
|
||||||
@@ -1004,9 +1030,10 @@ export function CalendarInvitationBanner({ email }: CalendarInvitationBannerProp
|
|||||||
<span className="truncate text-foreground">{cal.name}</span>
|
<span className="truncate text-foreground">{cal.name}</span>
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>,
|
||||||
|
document.body,
|
||||||
)}
|
)}
|
||||||
</div>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{canApplyProposal && (
|
{canApplyProposal && (
|
||||||
|
|||||||
Reference in New Issue
Block a user