({
tasks,
today,
onOpenTask,
onToggleTask,
onRescheduleTask,
onMoveTask,
onAddTask,
dailyNotesEnabled
}: Props)
| 87 | } |
| 88 | |
| 89 | export function TasksCalendar({ |
| 90 | tasks, |
| 91 | today, |
| 92 | onOpenTask, |
| 93 | onToggleTask, |
| 94 | onRescheduleTask, |
| 95 | onMoveTask, |
| 96 | onAddTask, |
| 97 | dailyNotesEnabled |
| 98 | }: Props): JSX.Element { |
| 99 | const monthAnchorIso = useStore((s) => s.tasksCalendarMonthAnchor) |
| 100 | const setMonthAnchor = useStore((s) => s.setTasksCalendarMonthAnchor) |
| 101 | const selectedDateIso = useStore((s) => s.tasksCalendarSelectedDate) |
| 102 | const setSelectedDate = useStore((s) => s.setTasksCalendarSelectedDate) |
| 103 | const weekStart = useStore((s) => s.calendarWeekStart) |
| 104 | const applyTaskMutation = useStore((s) => s.applyTaskMutation) |
| 105 | const deleteTaskFromList = useStore((s) => s.deleteTaskFromList) |
| 106 | const rootRef = useRef<HTMLDivElement>(null) |
| 107 | // Pointer-based drag-to-reschedule. Native HTML5 DnD is flaky for these rows |
| 108 | // inside the pane, so (like the Kanban) we grab on pointerdown, float a ghost |
| 109 | // while moving, detect the day under the cursor via a `data-cal-day` attribute, |
| 110 | // and offer the move / set-due choice on release. |
| 111 | const pointerDragRef = useRef<{ |
| 112 | task: VaultTask |
| 113 | startX: number |
| 114 | startY: number |
| 115 | dragging: boolean |
| 116 | } | null>(null) |
| 117 | const suppressClickRef = useRef(false) |
| 118 | const [ghost, setGhost] = useState<{ x: number; y: number; label: string } | null>(null) |
| 119 | const [dragOverIso, setDragOverIso] = useState<string | null>(null) |
| 120 | // Shared context menu — the drop "move vs set due" choice and the right-click |
| 121 | // task actions both feed it. |
| 122 | const [menu, setMenu] = useState<{ x: number; y: number; items: ContextMenuItem[] } | null>(null) |
| 123 | // Inline task editing (right-click → Edit). |
| 124 | const [editingTaskId, setEditingTaskId] = useState<string | null>(null) |
| 125 | const [editValue, setEditValue] = useState('') |
| 126 | const cancelEditRef = useRef(false) |
| 127 | // Keyboard reschedule: which task in the selected day's list is "active". |
| 128 | const [activeTaskIndex, setActiveTaskIndex] = useState(0) |
| 129 | // Keyboard "grab & place": the task picked up with `m`. While set, grid |
| 130 | // navigation chooses a target day; Enter places it (move/set-due choice). |
| 131 | const [grabbedTask, setGrabbedTask] = useState<VaultTask | null>(null) |
| 132 | const dPending = useRef(false) |
| 133 | const dTimer = useRef<ReturnType<typeof setTimeout>>() |
| 134 | const addInputRef = useRef<HTMLInputElement>(null) |
| 135 | // Quick-add box for the selected day. |
| 136 | const [addValue, setAddValue] = useState('') |
| 137 | |
| 138 | const todayIso = useMemo(() => toIsoDateLocal(today), [today]) |
| 139 | |
| 140 | // Initialise anchor + selection lazily — first time the view mounts |
| 141 | // we land on this month with today selected. |
| 142 | const monthAnchor = useMemo( |
| 143 | () => (monthAnchorIso ? firstOfMonth(parseIsoLocal(monthAnchorIso)) : firstOfMonth(today)), |
| 144 | [monthAnchorIso, today] |
| 145 | ) |
| 146 | const selectedDate = useMemo( |
nothing calls this directly
no test coverage detected