feat: add right-click context menu on empty calendar space
This commit is contained in:
@@ -31,6 +31,7 @@ import { CalendarSidebarPanel } from "@/components/calendar/calendar-sidebar-pan
|
|||||||
import { EventModal, type PendingEventPreview } from "@/components/calendar/event-modal";
|
import { EventModal, type PendingEventPreview } from "@/components/calendar/event-modal";
|
||||||
import { EventDetailPopover } from "@/components/calendar/event-detail-popover";
|
import { EventDetailPopover } from "@/components/calendar/event-detail-popover";
|
||||||
import { EventContextMenu } from "@/components/calendar/event-context-menu";
|
import { EventContextMenu } from "@/components/calendar/event-context-menu";
|
||||||
|
import { EmptySpaceContextMenu } from "@/components/calendar/empty-space-context-menu";
|
||||||
import { useContextMenu } from "@/hooks/use-context-menu";
|
import { useContextMenu } from "@/hooks/use-context-menu";
|
||||||
import { useRefreshGesture } from "@/hooks/use-refresh-gesture";
|
import { useRefreshGesture } from "@/hooks/use-refresh-gesture";
|
||||||
import { downloadEventICS } from "@/lib/calendar-ics-export";
|
import { downloadEventICS } from "@/lib/calendar-ics-export";
|
||||||
@@ -104,6 +105,7 @@ export default function CalendarPage() {
|
|||||||
const [editEvent, setEditEvent] = useState<CalendarEvent | null>(null);
|
const [editEvent, setEditEvent] = useState<CalendarEvent | null>(null);
|
||||||
const [defaultModalDate, setDefaultModalDate] = useState<Date | undefined>();
|
const [defaultModalDate, setDefaultModalDate] = useState<Date | undefined>();
|
||||||
const [defaultModalEndDate, setDefaultModalEndDate] = useState<Date | undefined>();
|
const [defaultModalEndDate, setDefaultModalEndDate] = useState<Date | undefined>();
|
||||||
|
const [defaultModalAllDay, setDefaultModalAllDay] = useState(false);
|
||||||
const [miniMonth, setMiniMonth] = useState(new Date());
|
const [miniMonth, setMiniMonth] = useState(new Date());
|
||||||
const [pendingScopeAction, setPendingScopeAction] = useState<PendingScopeAction | null>(null);
|
const [pendingScopeAction, setPendingScopeAction] = useState<PendingScopeAction | null>(null);
|
||||||
const [detailEvent, setDetailEvent] = useState<CalendarEvent | null>(null);
|
const [detailEvent, setDetailEvent] = useState<CalendarEvent | null>(null);
|
||||||
@@ -326,11 +328,12 @@ export default function CalendarPage() {
|
|||||||
setSelectedDate(date);
|
setSelectedDate(date);
|
||||||
}, [setSelectedDate]);
|
}, [setSelectedDate]);
|
||||||
|
|
||||||
const openCreateModal = useCallback((date?: Date, endDate?: Date) => {
|
const openCreateModal = useCallback((date?: Date, endDate?: Date, allDay?: boolean) => {
|
||||||
setEditEvent(null);
|
setEditEvent(null);
|
||||||
const d = date || selectedDate;
|
const d = date || selectedDate;
|
||||||
setDefaultModalDate(d);
|
setDefaultModalDate(d);
|
||||||
setDefaultModalEndDate(endDate);
|
setDefaultModalEndDate(endDate);
|
||||||
|
setDefaultModalAllDay(allDay ?? false);
|
||||||
setSelectedDate(d);
|
setSelectedDate(d);
|
||||||
setShowEventModal(true);
|
setShowEventModal(true);
|
||||||
}, [selectedDate, setSelectedDate]);
|
}, [selectedDate, setSelectedDate]);
|
||||||
@@ -395,6 +398,21 @@ export default function CalendarPage() {
|
|||||||
openEventContextMenu(e, event);
|
openEventContextMenu(e, event);
|
||||||
}, [closeDetail, openEventContextMenu]);
|
}, [closeDetail, openEventContextMenu]);
|
||||||
|
|
||||||
|
const {
|
||||||
|
contextMenu: emptyContextMenu,
|
||||||
|
openContextMenu: openEmptyContextMenu,
|
||||||
|
closeContextMenu: closeEmptyContextMenu,
|
||||||
|
menuRef: emptyContextMenuRef,
|
||||||
|
} = useContextMenu<{ date: Date; hour?: number; allDayArea?: boolean }>();
|
||||||
|
|
||||||
|
const handleContextMenuEmpty = useCallback(
|
||||||
|
(e: React.MouseEvent, date: Date, hour?: number, allDayArea?: boolean) => {
|
||||||
|
closeDetail();
|
||||||
|
openEmptyContextMenu(e, { date, hour, allDayArea });
|
||||||
|
},
|
||||||
|
[closeDetail, openEmptyContextMenu],
|
||||||
|
);
|
||||||
|
|
||||||
const handleHoverEvent = useCallback((event: CalendarEvent, anchorRect: DOMRect) => {
|
const handleHoverEvent = useCallback((event: CalendarEvent, anchorRect: DOMRect) => {
|
||||||
if (isMobile) return;
|
if (isMobile) return;
|
||||||
if (calendarHoverPreview === 'off') return;
|
if (calendarHoverPreview === 'off') return;
|
||||||
@@ -961,6 +979,7 @@ export default function CalendarPage() {
|
|||||||
onHoverEvent={handleHoverEvent}
|
onHoverEvent={handleHoverEvent}
|
||||||
onHoverLeave={handleHoverLeave}
|
onHoverLeave={handleHoverLeave}
|
||||||
onContextMenuEvent={handleContextMenuEvent}
|
onContextMenuEvent={handleContextMenuEvent}
|
||||||
|
onContextMenuEmpty={handleContextMenuEmpty}
|
||||||
onCreateAtTime={openCreateModal}
|
onCreateAtTime={openCreateModal}
|
||||||
firstDayOfWeek={firstDayOfWeek}
|
firstDayOfWeek={firstDayOfWeek}
|
||||||
isMobile={isMobile}
|
isMobile={isMobile}
|
||||||
@@ -978,6 +997,7 @@ export default function CalendarPage() {
|
|||||||
onHoverEvent={handleHoverEvent}
|
onHoverEvent={handleHoverEvent}
|
||||||
onHoverLeave={handleHoverLeave}
|
onHoverLeave={handleHoverLeave}
|
||||||
onContextMenuEvent={handleContextMenuEvent}
|
onContextMenuEvent={handleContextMenuEvent}
|
||||||
|
onContextMenuEmpty={handleContextMenuEmpty}
|
||||||
onCreateAtTime={openCreateModal}
|
onCreateAtTime={openCreateModal}
|
||||||
firstDayOfWeek={firstDayOfWeek}
|
firstDayOfWeek={firstDayOfWeek}
|
||||||
timeFormat={timeFormat}
|
timeFormat={timeFormat}
|
||||||
@@ -997,6 +1017,7 @@ export default function CalendarPage() {
|
|||||||
onHoverEvent={handleHoverEvent}
|
onHoverEvent={handleHoverEvent}
|
||||||
onHoverLeave={handleHoverLeave}
|
onHoverLeave={handleHoverLeave}
|
||||||
onContextMenuEvent={handleContextMenuEvent}
|
onContextMenuEvent={handleContextMenuEvent}
|
||||||
|
onContextMenuEmpty={handleContextMenuEmpty}
|
||||||
onCreateAtTime={openCreateModal}
|
onCreateAtTime={openCreateModal}
|
||||||
timeFormat={timeFormat}
|
timeFormat={timeFormat}
|
||||||
isMobile={isMobile}
|
isMobile={isMobile}
|
||||||
@@ -1211,12 +1232,13 @@ export default function CalendarPage() {
|
|||||||
calendars={calendars}
|
calendars={calendars}
|
||||||
defaultDate={defaultModalDate}
|
defaultDate={defaultModalDate}
|
||||||
defaultEndDate={defaultModalEndDate}
|
defaultEndDate={defaultModalEndDate}
|
||||||
|
defaultAllDay={defaultModalAllDay}
|
||||||
defaultCalendarId={defaultCalendarIdForCreate}
|
defaultCalendarId={defaultCalendarIdForCreate}
|
||||||
onSave={handleSaveEvent}
|
onSave={handleSaveEvent}
|
||||||
onDelete={handleDeleteEvent}
|
onDelete={handleDeleteEvent}
|
||||||
onDuplicate={handleDuplicateEvent}
|
onDuplicate={handleDuplicateEvent}
|
||||||
onRsvp={handleRsvp}
|
onRsvp={handleRsvp}
|
||||||
onClose={() => { setShowEventModal(false); setEditEvent(null); setPendingPreview(null); setDefaultCalendarIdForCreate(undefined); }}
|
onClose={() => { setShowEventModal(false); setEditEvent(null); setPendingPreview(null); setDefaultCalendarIdForCreate(undefined); setDefaultModalAllDay(false); }}
|
||||||
onPreviewChange={setPendingPreview}
|
onPreviewChange={setPendingPreview}
|
||||||
currentUserEmails={currentUserEmails}
|
currentUserEmails={currentUserEmails}
|
||||||
isMobile={false}
|
isMobile={false}
|
||||||
@@ -1282,6 +1304,38 @@ export default function CalendarPage() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{emptyContextMenu.data && (() => {
|
||||||
|
const { date, hour } = emptyContextMenu.data;
|
||||||
|
return (
|
||||||
|
<EmptySpaceContextMenu
|
||||||
|
position={emptyContextMenu.position}
|
||||||
|
isOpen={emptyContextMenu.isOpen}
|
||||||
|
onClose={closeEmptyContextMenu}
|
||||||
|
menuRef={emptyContextMenuRef}
|
||||||
|
onNewEvent={() => {
|
||||||
|
const d = new Date(date);
|
||||||
|
if (typeof hour === "number") {
|
||||||
|
d.setHours(hour, 0, 0, 0);
|
||||||
|
} else {
|
||||||
|
const now = new Date();
|
||||||
|
d.setHours(now.getHours() + 1, 0, 0, 0);
|
||||||
|
}
|
||||||
|
openCreateModal(d);
|
||||||
|
}}
|
||||||
|
onNewAllDayEvent={() => {
|
||||||
|
const d = new Date(date);
|
||||||
|
d.setHours(0, 0, 0, 0);
|
||||||
|
openCreateModal(d, undefined, true);
|
||||||
|
}}
|
||||||
|
onNewTask={enableCalendarTasks ? () => {
|
||||||
|
setEditTask(null);
|
||||||
|
setShowTaskModal(true);
|
||||||
|
} : undefined}
|
||||||
|
onGoToToday={goToToday}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
|
||||||
{detailEvent && detailAnchorRect && (
|
{detailEvent && detailAnchorRect && (
|
||||||
<EventDetailPopover
|
<EventDetailPopover
|
||||||
event={detailEvent}
|
event={detailEvent}
|
||||||
@@ -1308,12 +1362,13 @@ export default function CalendarPage() {
|
|||||||
calendars={calendars}
|
calendars={calendars}
|
||||||
defaultDate={defaultModalDate}
|
defaultDate={defaultModalDate}
|
||||||
defaultEndDate={defaultModalEndDate}
|
defaultEndDate={defaultModalEndDate}
|
||||||
|
defaultAllDay={defaultModalAllDay}
|
||||||
defaultCalendarId={defaultCalendarIdForCreate}
|
defaultCalendarId={defaultCalendarIdForCreate}
|
||||||
onSave={handleSaveEvent}
|
onSave={handleSaveEvent}
|
||||||
onDelete={handleDeleteEvent}
|
onDelete={handleDeleteEvent}
|
||||||
onDuplicate={handleDuplicateEvent}
|
onDuplicate={handleDuplicateEvent}
|
||||||
onRsvp={handleRsvp}
|
onRsvp={handleRsvp}
|
||||||
onClose={() => { setShowEventModal(false); setEditEvent(null); setDefaultCalendarIdForCreate(undefined); }}
|
onClose={() => { setShowEventModal(false); setEditEvent(null); setDefaultCalendarIdForCreate(undefined); setDefaultModalAllDay(false); }}
|
||||||
currentUserEmails={currentUserEmails}
|
currentUserEmails={currentUserEmails}
|
||||||
isMobile={true}
|
isMobile={true}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ interface CalendarDayViewProps {
|
|||||||
onHoverEvent?: (event: CalendarEvent, anchorRect: DOMRect) => void;
|
onHoverEvent?: (event: CalendarEvent, anchorRect: DOMRect) => void;
|
||||||
onHoverLeave?: () => void;
|
onHoverLeave?: () => void;
|
||||||
onContextMenuEvent?: (e: React.MouseEvent, event: CalendarEvent) => void;
|
onContextMenuEvent?: (e: React.MouseEvent, event: CalendarEvent) => void;
|
||||||
|
onContextMenuEmpty?: (e: React.MouseEvent, date: Date, hour?: number, allDayArea?: boolean) => void;
|
||||||
onCreateAtTime: (date: Date, endDate?: Date) => void;
|
onCreateAtTime: (date: Date, endDate?: Date) => void;
|
||||||
timeFormat?: "12h" | "24h";
|
timeFormat?: "12h" | "24h";
|
||||||
isMobile?: boolean;
|
isMobile?: boolean;
|
||||||
@@ -39,6 +40,7 @@ export function CalendarDayView({
|
|||||||
onHoverEvent,
|
onHoverEvent,
|
||||||
onHoverLeave,
|
onHoverLeave,
|
||||||
onContextMenuEvent,
|
onContextMenuEvent,
|
||||||
|
onContextMenuEmpty,
|
||||||
onCreateAtTime,
|
onCreateAtTime,
|
||||||
timeFormat = "24h",
|
timeFormat = "24h",
|
||||||
isMobile,
|
isMobile,
|
||||||
@@ -144,7 +146,13 @@ export function CalendarDayView({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{(allDayEvents.length > 0 || dayTasks.length > 0) && (
|
{(allDayEvents.length > 0 || dayTasks.length > 0) && (
|
||||||
<div className="px-4 py-2 border-b border-border">
|
<div
|
||||||
|
className="px-4 py-2 border-b border-border"
|
||||||
|
onContextMenu={onContextMenuEmpty ? (e) => {
|
||||||
|
if ((e.target as HTMLElement).closest("[data-calendar-event],button")) return;
|
||||||
|
onContextMenuEmpty(e, selectedDate, undefined, true);
|
||||||
|
} : undefined}
|
||||||
|
>
|
||||||
{allDayEvents.length > 0 && (
|
{allDayEvents.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<div className="text-[10px] text-muted-foreground mb-1">{t("events.all_day")}</div>
|
<div className="text-[10px] text-muted-foreground mb-1">{t("events.all_day")}</div>
|
||||||
@@ -240,6 +248,7 @@ export function CalendarDayView({
|
|||||||
aria-label={formatHour(h)}
|
aria-label={formatHour(h)}
|
||||||
onClick={() => handleSlotClick(selectedDate, h)}
|
onClick={() => handleSlotClick(selectedDate, h)}
|
||||||
onDoubleClick={() => handleSlotDoubleClick(selectedDate, h)}
|
onDoubleClick={() => handleSlotDoubleClick(selectedDate, h)}
|
||||||
|
onContextMenu={onContextMenuEmpty ? (e) => onContextMenuEmpty(e, selectedDate, h, false) : undefined}
|
||||||
className="border-b border-border/50 hover:bg-muted/30 cursor-pointer transition-colors"
|
className="border-b border-border/50 hover:bg-muted/30 cursor-pointer transition-colors"
|
||||||
style={{ height: HOUR_HEIGHT }}
|
style={{ height: HOUR_HEIGHT }}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ interface CalendarMonthViewProps {
|
|||||||
onHoverEvent?: (event: CalendarEvent, anchorRect: DOMRect) => void;
|
onHoverEvent?: (event: CalendarEvent, anchorRect: DOMRect) => void;
|
||||||
onHoverLeave?: () => void;
|
onHoverLeave?: () => void;
|
||||||
onContextMenuEvent?: (e: React.MouseEvent, event: CalendarEvent) => void;
|
onContextMenuEvent?: (e: React.MouseEvent, event: CalendarEvent) => void;
|
||||||
|
onContextMenuEmpty?: (e: React.MouseEvent, date: Date, hour?: number, allDayArea?: boolean) => void;
|
||||||
onCreateAtTime?: (date: Date) => void;
|
onCreateAtTime?: (date: Date) => void;
|
||||||
firstDayOfWeek?: number;
|
firstDayOfWeek?: number;
|
||||||
isMobile?: boolean;
|
isMobile?: boolean;
|
||||||
@@ -39,6 +40,7 @@ export function CalendarMonthView({
|
|||||||
onHoverEvent,
|
onHoverEvent,
|
||||||
onHoverLeave,
|
onHoverLeave,
|
||||||
onContextMenuEvent,
|
onContextMenuEvent,
|
||||||
|
onContextMenuEmpty,
|
||||||
onCreateAtTime,
|
onCreateAtTime,
|
||||||
firstDayOfWeek = 1,
|
firstDayOfWeek = 1,
|
||||||
isMobile,
|
isMobile,
|
||||||
@@ -174,6 +176,7 @@ export function CalendarMonthView({
|
|||||||
aria-label={fullDateLabel}
|
aria-label={fullDateLabel}
|
||||||
onClick={() => onSelectDate(day)}
|
onClick={() => onSelectDate(day)}
|
||||||
onDoubleClick={() => onCreateAtTime?.(day)}
|
onDoubleClick={() => onCreateAtTime?.(day)}
|
||||||
|
onContextMenu={onContextMenuEmpty ? (e) => onContextMenuEmpty(e, day, undefined, true) : undefined}
|
||||||
onDragOver={(e) => handleCellDragOver(e, key)}
|
onDragOver={(e) => handleCellDragOver(e, key)}
|
||||||
onDragLeave={handleCellDragLeave}
|
onDragLeave={handleCellDragLeave}
|
||||||
onDrop={(e) => handleCellDrop(e, day)}
|
onDrop={(e) => handleCellDrop(e, day)}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ interface CalendarWeekViewProps {
|
|||||||
onHoverEvent?: (event: CalendarEvent, anchorRect: DOMRect) => void;
|
onHoverEvent?: (event: CalendarEvent, anchorRect: DOMRect) => void;
|
||||||
onHoverLeave?: () => void;
|
onHoverLeave?: () => void;
|
||||||
onContextMenuEvent?: (e: React.MouseEvent, event: CalendarEvent) => void;
|
onContextMenuEvent?: (e: React.MouseEvent, event: CalendarEvent) => void;
|
||||||
|
onContextMenuEmpty?: (e: React.MouseEvent, date: Date, hour?: number, allDayArea?: boolean) => void;
|
||||||
onCreateAtTime: (date: Date, endDate?: Date) => void;
|
onCreateAtTime: (date: Date, endDate?: Date) => void;
|
||||||
firstDayOfWeek?: number;
|
firstDayOfWeek?: number;
|
||||||
timeFormat?: "12h" | "24h";
|
timeFormat?: "12h" | "24h";
|
||||||
@@ -44,6 +45,7 @@ export function CalendarWeekView({
|
|||||||
onHoverEvent,
|
onHoverEvent,
|
||||||
onHoverLeave,
|
onHoverLeave,
|
||||||
onContextMenuEvent,
|
onContextMenuEvent,
|
||||||
|
onContextMenuEmpty,
|
||||||
onCreateAtTime,
|
onCreateAtTime,
|
||||||
firstDayOfWeek = 1,
|
firstDayOfWeek = 1,
|
||||||
timeFormat = "24h",
|
timeFormat = "24h",
|
||||||
@@ -219,7 +221,11 @@ export function CalendarWeekView({
|
|||||||
style={{ minHeight: Math.max(28, (allDayRowCount + taskRowCount) * 24 + 4) }}
|
style={{ minHeight: Math.max(28, (allDayRowCount + taskRowCount) * 24 + 4) }}
|
||||||
>
|
>
|
||||||
{weekDays.map((day) => (
|
{weekDays.map((day) => (
|
||||||
<div key={format(day, "yyyy-MM-dd")} className="bg-background min-h-[28px]" />
|
<div
|
||||||
|
key={format(day, "yyyy-MM-dd")}
|
||||||
|
className="bg-background min-h-[28px]"
|
||||||
|
onContextMenu={onContextMenuEmpty ? (e) => onContextMenuEmpty(e, day, undefined, true) : undefined}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<div className="absolute inset-0 pointer-events-none">
|
<div className="absolute inset-0 pointer-events-none">
|
||||||
@@ -378,6 +384,7 @@ export function CalendarWeekView({
|
|||||||
aria-label={`${intlFormatter.dateTime(day, { weekday: "short" })} ${formatHour(h)}`}
|
aria-label={`${intlFormatter.dateTime(day, { weekday: "short" })} ${formatHour(h)}`}
|
||||||
onClick={() => handleSlotClick(day, h)}
|
onClick={() => handleSlotClick(day, h)}
|
||||||
onDoubleClick={() => handleSlotDoubleClick(day, h)}
|
onDoubleClick={() => handleSlotDoubleClick(day, h)}
|
||||||
|
onContextMenu={onContextMenuEmpty ? (e) => onContextMenuEmpty(e, day, h, false) : undefined}
|
||||||
className="border-b border-border/50 hover:bg-muted/30 cursor-pointer transition-colors"
|
className="border-b border-border/50 hover:bg-muted/30 cursor-pointer transition-colors"
|
||||||
style={{ height: HOUR_HEIGHT }}
|
style={{ height: HOUR_HEIGHT }}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useTranslations } from "next-intl";
|
||||||
|
import {
|
||||||
|
ContextMenu,
|
||||||
|
ContextMenuItem,
|
||||||
|
ContextMenuSeparator,
|
||||||
|
} from "@/components/ui/context-menu";
|
||||||
|
import { Plus, CalendarDays, CheckSquare, Clock } from "lucide-react";
|
||||||
|
|
||||||
|
interface Position {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EmptySpaceContextMenuProps {
|
||||||
|
position: Position;
|
||||||
|
isOpen: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
menuRef: React.RefObject<HTMLDivElement | null>;
|
||||||
|
onNewEvent: () => void;
|
||||||
|
onNewAllDayEvent: () => void;
|
||||||
|
onNewTask?: () => void;
|
||||||
|
onGoToToday: () => void;
|
||||||
|
showAllDayOption?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function EmptySpaceContextMenu({
|
||||||
|
position,
|
||||||
|
isOpen,
|
||||||
|
onClose,
|
||||||
|
menuRef,
|
||||||
|
onNewEvent,
|
||||||
|
onNewAllDayEvent,
|
||||||
|
onNewTask,
|
||||||
|
onGoToToday,
|
||||||
|
showAllDayOption = true,
|
||||||
|
}: EmptySpaceContextMenuProps) {
|
||||||
|
const t = useTranslations("calendar");
|
||||||
|
|
||||||
|
const handle = (fn: () => void) => () => {
|
||||||
|
fn();
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ContextMenu ref={menuRef} isOpen={isOpen} position={position} onClose={onClose}>
|
||||||
|
<ContextMenuItem icon={Plus} label={t("events.new_event")} onClick={handle(onNewEvent)} />
|
||||||
|
{showAllDayOption && (
|
||||||
|
<ContextMenuItem
|
||||||
|
icon={CalendarDays}
|
||||||
|
label={t("events.new_all_day_event")}
|
||||||
|
onClick={handle(onNewAllDayEvent)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{onNewTask && (
|
||||||
|
<ContextMenuItem
|
||||||
|
icon={CheckSquare}
|
||||||
|
label={t("events.new_task")}
|
||||||
|
onClick={handle(onNewTask)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<ContextMenuSeparator />
|
||||||
|
<ContextMenuItem icon={Clock} label={t("events.go_to_today")} onClick={handle(onGoToToday)} />
|
||||||
|
</ContextMenu>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -35,6 +35,7 @@ interface EventModalProps {
|
|||||||
calendars: Calendar[];
|
calendars: Calendar[];
|
||||||
defaultDate?: Date;
|
defaultDate?: Date;
|
||||||
defaultEndDate?: Date;
|
defaultEndDate?: Date;
|
||||||
|
defaultAllDay?: boolean;
|
||||||
defaultCalendarId?: string;
|
defaultCalendarId?: string;
|
||||||
onSave: (data: Partial<CalendarEvent>, sendSchedulingMessages?: boolean) => void | Promise<void>;
|
onSave: (data: Partial<CalendarEvent>, sendSchedulingMessages?: boolean) => void | Promise<void>;
|
||||||
onDelete?: (id: string, sendSchedulingMessages?: boolean) => void;
|
onDelete?: (id: string, sendSchedulingMessages?: boolean) => void;
|
||||||
@@ -114,6 +115,7 @@ export function EventModal({
|
|||||||
calendars,
|
calendars,
|
||||||
defaultDate,
|
defaultDate,
|
||||||
defaultEndDate,
|
defaultEndDate,
|
||||||
|
defaultAllDay,
|
||||||
defaultCalendarId,
|
defaultCalendarId,
|
||||||
onSave,
|
onSave,
|
||||||
onDelete,
|
onDelete,
|
||||||
@@ -199,7 +201,7 @@ export function EventModal({
|
|||||||
const [startTime, setStartTime] = useState(formatTimeInput(getInitialStart()));
|
const [startTime, setStartTime] = useState(formatTimeInput(getInitialStart()));
|
||||||
const [endDate, setEndDate] = useState(formatDateInput(getInitialEnd()));
|
const [endDate, setEndDate] = useState(formatDateInput(getInitialEnd()));
|
||||||
const [endTime, setEndTime] = useState(formatTimeInput(getInitialEnd()));
|
const [endTime, setEndTime] = useState(formatTimeInput(getInitialEnd()));
|
||||||
const [allDay, setAllDay] = useState(event?.showWithoutTime || false);
|
const [allDay, setAllDay] = useState(event?.showWithoutTime || defaultAllDay || false);
|
||||||
const [calendarId, setCalendarId] = useState<string>(() => {
|
const [calendarId, setCalendarId] = useState<string>(() => {
|
||||||
if (event?.calendarIds) return getPrimaryCalendarId(event) || calendars[0]?.id || "";
|
if (event?.calendarIds) return getPrimaryCalendarId(event) || calendars[0]?.id || "";
|
||||||
if (defaultCalendarId && calendars.some(c => c.id === defaultCalendarId)) return defaultCalendarId;
|
if (defaultCalendarId && calendars.some(c => c.id === defaultCalendarId)) return defaultCalendarId;
|
||||||
|
|||||||
@@ -2136,7 +2136,11 @@
|
|||||||
"tomorrow_header": "Tomorrow",
|
"tomorrow_header": "Tomorrow",
|
||||||
"export_ics": "Export as .ics",
|
"export_ics": "Export as .ics",
|
||||||
"copy_title": "Copy title",
|
"copy_title": "Copy title",
|
||||||
"copy_link": "Copy meeting link"
|
"copy_link": "Copy meeting link",
|
||||||
|
"new_event": "New event",
|
||||||
|
"new_all_day_event": "New all-day event",
|
||||||
|
"new_task": "New task",
|
||||||
|
"go_to_today": "Go to today"
|
||||||
},
|
},
|
||||||
"detail": {
|
"detail": {
|
||||||
"add_note": "Add a note...",
|
"add_note": "Add a note...",
|
||||||
|
|||||||
Reference in New Issue
Block a user