gantt-lib 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/GanttChart/GanttChart.tsx","../src/utils/dateUtils.ts","../src/components/TimeScaleHeader/TimeScaleHeader.tsx","../src/components/TaskRow/TaskRow.tsx","../src/utils/geometry.ts","../src/hooks/useTaskDrag.ts","../src/components/TodayIndicator/TodayIndicator.tsx","../src/components/GridBackground/GridBackground.tsx","../src/components/DragGuideLines/DragGuideLines.tsx"],"sourcesContent":["'use client';\n\nimport React, { useMemo, useCallback, useRef, useState, useEffect } from 'react';\nimport { getMultiMonthDays } from '../../utils/dateUtils';\nimport { calculateGridWidth } from '../../utils/geometry';\nimport TimeScaleHeader from '../TimeScaleHeader';\nimport TaskRow from '../TaskRow';\nimport TodayIndicator from '../TodayIndicator';\nimport GridBackground from '../GridBackground';\nimport DragGuideLines from '../DragGuideLines/DragGuideLines';\nimport './GanttChart.css';\n\n/**\n * Task data structure for Gantt chart\n */\nexport interface Task {\n /** Unique identifier for the task */\n id: string;\n /** Display name of the task */\n name: string;\n /** Task start date (ISO string or Date object) */\n startDate: string | Date;\n /** Task end date (ISO string or Date object) */\n endDate: string | Date;\n /** Optional color for task bar visualization */\n color?: string;\n}\n\nexport interface GanttChartProps {\n /** Array of tasks to display */\n tasks: Task[];\n /** Width of each day column in pixels (default: 40) */\n dayWidth?: number;\n /** Height of each task row in pixels (default: 40) */\n rowHeight?: number;\n /** Height of the header row in pixels (default: 40) */\n headerHeight?: number;\n /** Container height in pixels (default: 600) - adds vertical scrolling when tasks exceed this height */\n containerHeight?: number;\n /** Callback when tasks are modified via drag/resize. Can receive either the new tasks array or a functional updater. */\n onChange?: (tasks: Task[] | ((currentTasks: Task[]) => Task[])) => void;\n}\n\n/**\n * GanttChart component - displays tasks on a monthly timeline with Excel-like styling\n *\n * The calendar automatically shows full months based on task date ranges.\n * For example, if tasks span from March 25 to May 5, the calendar shows\n * the complete months of March, April, and May (March 1 - May 31).\n *\n * @example\n * ```tsx\n * <GanttChart\n * tasks={[\n * { id: '1', name: 'Task 1', startDate: '2026-02-01', endDate: '2026-02-05' }\n * ]}\n * />\n * ```\n */\nexport const GanttChart: React.FC<GanttChartProps> = ({\n tasks,\n dayWidth = 40,\n rowHeight = 40,\n headerHeight = 40,\n containerHeight = 600,\n onChange,\n}) => {\n const scrollContainerRef = useRef<HTMLDivElement>(null);\n\n // Calculate multi-month date range from tasks\n const dateRange = useMemo(() => getMultiMonthDays(tasks), [tasks]);\n\n\n // Calculate grid width\n const gridWidth = useMemo(\n () => Math.round(dateRange.length * dayWidth),\n [dateRange.length, dayWidth]\n );\n\n // Calculate total grid height\n const totalGridHeight = useMemo(\n () => tasks.length * rowHeight,\n [tasks.length, rowHeight]\n );\n\n // Get month start for calculations (first day of date range)\n const monthStart = useMemo(() => {\n if (dateRange.length === 0) {\n return new Date(Date.UTC(new Date().getUTCFullYear(), new Date().getUTCMonth(), 1));\n }\n const firstDay = dateRange[0];\n return new Date(Date.UTC(firstDay.getUTCFullYear(), firstDay.getUTCMonth(), 1));\n }, [dateRange]);\n\n // Only render TodayIndicator if today is in the visible date range\n const todayInRange = useMemo(() => {\n const now = new Date();\n const today = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));\n return dateRange.some(day => day.getTime() === today.getTime());\n }, [dateRange]);\n\n // Track drag state for guide lines\n const [dragGuideLines, setDragGuideLines] = useState<{\n isDragging: boolean;\n dragMode: 'move' | 'resize-left' | 'resize-right' | null;\n left: number;\n width: number;\n } | null>(null);\n\n /**\n * Stable callback for task updates\n *\n * FIXED: No longer depends on `tasks` to avoid stale closure bugs.\n * Uses functional state update pattern: the callback receives an updater function\n * that maps over the current tasks state, ensuring we always use the latest state.\n *\n * This prevents the \"reverting\" bug where dragging a second task causes the\n * first task to revert to its original position.\n *\n * To prevent re-render storms during drag:\n * 1. The onChange callback is only called AFTER drag completes (mouseUp)\n * 2. During drag, only the dragged TaskRow re-renders (due to its internal state)\n * 3. Other TaskRows don't re-render because their props haven't changed\n *\n * The React.memo comparison in TaskRow excludes onChange from comparison,\n * relying on the fact that onChange fires only after drag completes.\n */\n const handleTaskChange = useCallback((updatedTask: Task) => {\n // Call onChange with a functional updater that receives the current tasks\n onChange?.((currentTasks) =>\n currentTasks.map((t) =>\n t.id === updatedTask.id ? updatedTask : t\n )\n );\n }, [onChange]);\n\n const handleDragStateChange = useCallback((state: {\n isDragging: boolean;\n dragMode: 'move' | 'resize-left' | 'resize-right' | null;\n left: number;\n width: number;\n }) => {\n if (state.isDragging) {\n setDragGuideLines(state);\n } else {\n setDragGuideLines(null);\n }\n }, []);\n\n // Pan (grab-scroll) on empty grid area\n const panStateRef = useRef<{ active: boolean; startX: number; startY: number; scrollX: number; scrollY: number } | null>(null);\n\n const handlePanStart = useCallback((e: React.MouseEvent) => {\n // Only pan on left click, skip if clicking on a task bar\n if (e.button !== 0) return;\n const target = e.target as HTMLElement;\n if (target.closest('[data-taskbar]')) return;\n\n const container = scrollContainerRef.current;\n if (!container) return;\n\n panStateRef.current = {\n active: true,\n startX: e.clientX,\n startY: e.clientY,\n scrollX: container.scrollLeft,\n scrollY: container.scrollTop,\n };\n container.style.cursor = 'grabbing';\n e.preventDefault();\n }, []);\n\n useEffect(() => {\n const handlePanMove = (e: MouseEvent) => {\n const pan = panStateRef.current;\n if (!pan?.active) return;\n const container = scrollContainerRef.current;\n if (!container) return;\n\n container.scrollLeft = pan.scrollX - (e.clientX - pan.startX);\n container.scrollTop = pan.scrollY - (e.clientY - pan.startY);\n };\n\n const handlePanEnd = () => {\n if (!panStateRef.current?.active) return;\n panStateRef.current = null;\n const container = scrollContainerRef.current;\n if (container) container.style.cursor = '';\n };\n\n window.addEventListener('mousemove', handlePanMove);\n window.addEventListener('mouseup', handlePanEnd);\n return () => {\n window.removeEventListener('mousemove', handlePanMove);\n window.removeEventListener('mouseup', handlePanEnd);\n };\n }, []);\n\n return (\n <div className=\"gantt-container\">\n <div\n ref={scrollContainerRef}\n className=\"gantt-scrollContainer\"\n style={{ height: `${containerHeight}px`, cursor: 'grab' }}\n onMouseDown={handlePanStart}\n >\n {/* Sticky header - stays at top during vertical scroll, scrolls with content horizontally */}\n <div className=\"gantt-stickyHeader\" style={{ width: `${gridWidth}px` }}>\n <TimeScaleHeader\n days={dateRange}\n dayWidth={dayWidth}\n headerHeight={headerHeight}\n />\n </div>\n\n {/* Task area */}\n <div\n className=\"gantt-taskArea\"\n style={{\n position: 'relative',\n width: `${gridWidth}px`,\n }}\n >\n <GridBackground\n dateRange={dateRange}\n dayWidth={dayWidth}\n totalHeight={totalGridHeight}\n />\n\n {todayInRange && <TodayIndicator monthStart={monthStart} dayWidth={dayWidth} />}\n\n {dragGuideLines && (\n <DragGuideLines\n isDragging={dragGuideLines.isDragging}\n dragMode={dragGuideLines.dragMode}\n left={dragGuideLines.left}\n width={dragGuideLines.width}\n totalHeight={totalGridHeight}\n />\n )}\n\n {tasks.map((task) => (\n <TaskRow\n key={task.id}\n task={task}\n monthStart={monthStart}\n dayWidth={dayWidth}\n rowHeight={rowHeight}\n onChange={handleTaskChange}\n onDragStateChange={handleDragStateChange}\n />\n ))}\n </div>\n </div>\n </div>\n );\n};\n\nexport default GanttChart;\n","import { parseISO, isValid } from 'date-fns';\n\n/**\n * Parse date string as UTC to prevent DST issues\n * @param date - Date string or Date object\n * @returns Date object representing UTC midnight\n * @throws Error if date string is invalid\n */\nexport const parseUTCDate = (date: string | Date): Date => {\n if (typeof date === 'string') {\n // If already an ISO string (contains 'T'), parse directly\n // Otherwise, append UTC time for simple date strings (YYYY-MM-DD)\n const dateStr = date.includes('T') ? date : `${date}T00:00:00Z`;\n const parsed = new Date(dateStr);\n if (isNaN(parsed.getTime())) {\n throw new Error(`Invalid date string: ${date}`);\n }\n return parsed;\n }\n return date;\n};\n\n/**\n * Get all days in the month of given date (UTC)\n * @param date - Reference date (any day in the target month)\n * @returns Array of Date objects for each day in the month\n */\nexport const getMonthDays = (date: Date | string): Date[] => {\n const utcDate = parseUTCDate(date);\n const year = utcDate.getUTCFullYear();\n const month = utcDate.getUTCMonth();\n\n // Get days in month (handles leap years)\n const daysInMonth = new Date(Date.UTC(year, month + 1, 0)).getUTCDate();\n\n const days: Date[] = [];\n for (let day = 1; day <= daysInMonth; day++) {\n days.push(new Date(Date.UTC(year, month, day)));\n }\n\n return days;\n};\n\n/**\n * Calculate day offset from month start (0-based)\n * @param date - The date to calculate offset for\n * @param monthStart - The start of the month as reference\n * @returns Number of days from month start (negative if date is before month start)\n */\nexport const getDayOffset = (date: Date, monthStart: Date): number => {\n const dateMs = Date.UTC(\n date.getUTCFullYear(),\n date.getUTCMonth(),\n date.getUTCDate()\n );\n const startMs = Date.UTC(\n monthStart.getUTCFullYear(),\n monthStart.getUTCMonth(),\n monthStart.getUTCDate()\n );\n return Math.round((dateMs - startMs) / (1000 * 60 * 60 * 24));\n};\n\n/**\n * Check if date is today (UTC comparison)\n * @param date - Date to check\n * @returns True if date is today, false otherwise\n */\nexport const isToday = (date: Date): boolean => {\n const now = new Date();\n const today = new Date(Date.UTC(\n now.getUTCFullYear(),\n now.getUTCMonth(),\n now.getUTCDate()\n ));\n const compareDate = new Date(Date.UTC(\n date.getUTCFullYear(),\n date.getUTCMonth(),\n date.getUTCDate()\n ));\n return today.getTime() === compareDate.getTime();\n};\n\n/**\n * Check if date is a weekend day (Saturday or Sunday)\n * @param date - Date to check\n * @returns True if date is Saturday (6) or Sunday (0), false otherwise\n */\nexport const isWeekend = (date: Date): boolean => {\n const day = date.getUTCDay();\n return day === 0 || day === 6; // Sunday (0) or Saturday (6)\n};\n\n/**\n * Calculate multi-month date range from task dates\n * Expands range to include full months (1st of first month to last day of last month)\n * @param tasks - Array of tasks with startDate and endDate\n * @returns Array of Date objects for all days in the expanded range\n */\nexport const getMultiMonthDays = (tasks: Array<{ startDate: string | Date; endDate: string | Date }>): Date[] => {\n // Handle empty task array by returning current month\n if (!tasks || tasks.length === 0) {\n return getMonthDays(new Date());\n }\n\n // Find min and max dates from all tasks\n let minDate: Date | null = null;\n let maxDate: Date | null = null;\n\n for (const task of tasks) {\n const start = parseUTCDate(task.startDate);\n const end = parseUTCDate(task.endDate);\n\n if (!minDate || start.getTime() < minDate.getTime()) {\n minDate = start;\n }\n if (!maxDate || end.getTime() > maxDate.getTime()) {\n maxDate = end;\n }\n }\n\n if (!minDate || !maxDate) {\n return getMonthDays(new Date());\n }\n\n // Extend to full months: 1st of first month to last day of last month\n const startOfMonth = new Date(Date.UTC(\n minDate.getUTCFullYear(),\n minDate.getUTCMonth(),\n 1\n ));\n\n const endOfMonth = new Date(Date.UTC(\n maxDate.getUTCFullYear(),\n maxDate.getUTCMonth() + 1,\n 0\n ));\n\n // Generate all dates in range\n const days: Date[] = [];\n const current = new Date(startOfMonth);\n\n while (current.getTime() <= endOfMonth.getTime()) {\n days.push(new Date(Date.UTC(\n current.getUTCFullYear(),\n current.getUTCMonth(),\n current.getUTCDate()\n )));\n // Move to next day\n current.setUTCDate(current.getUTCDate() + 1);\n }\n\n return days;\n};\n\n/**\n * Calculate month spans within a date range\n * @param dateRange - Array of Date objects representing the full range\n * @returns Array of month span objects with month, days count, and start index\n */\nexport const getMonthSpans = (\n dateRange: Date[]\n): Array<{ month: Date; days: number; startIndex: number }> => {\n if (dateRange.length === 0) {\n return [];\n }\n\n const spans: Array<{ month: Date; days: number; startIndex: number }> = [];\n let currentMonthYear = `${dateRange[0].getUTCFullYear()}-${dateRange[0].getUTCMonth()}`;\n let startOfMonthIndex = 0;\n\n for (let i = 0; i < dateRange.length; i++) {\n const date = dateRange[i];\n const monthYear = `${date.getUTCFullYear()}-${date.getUTCMonth()}`;\n\n // When month changes, finalize the previous span and start a new one\n if (monthYear !== currentMonthYear) {\n spans.push({\n month: new Date(Date.UTC(\n dateRange[startOfMonthIndex].getUTCFullYear(),\n dateRange[startOfMonthIndex].getUTCMonth(),\n 1\n )),\n days: i - startOfMonthIndex,\n startIndex: startOfMonthIndex\n });\n currentMonthYear = monthYear;\n startOfMonthIndex = i;\n }\n\n // Last date - finalize the last span\n if (i === dateRange.length - 1) {\n spans.push({\n month: new Date(Date.UTC(\n date.getUTCFullYear(),\n date.getUTCMonth(),\n 1\n )),\n days: i - startOfMonthIndex + 1,\n startIndex: startOfMonthIndex\n });\n }\n }\n\n return spans;\n};\n\n/**\n * Format date as DD.MM (e.g., 25.03 for March 25th)\n * @param date - Date to format\n * @returns Formatted date string in DD.MM format\n */\nexport const formatDateLabel = (date: Date | string): string => {\n const parsed = parseUTCDate(date);\n const day = String(parsed.getUTCDate()).padStart(2, '0');\n const month = String(parsed.getUTCMonth() + 1).padStart(2, '0');\n return `${day}.${month}`;\n};\n","'use client';\n\nimport React, { useMemo } from 'react';\nimport { format } from 'date-fns';\nimport { ru } from 'date-fns/locale';\nimport { getMonthSpans } from '../../utils/dateUtils';\nimport type { MonthSpan } from '../../types';\nimport './TimeScaleHeader.css';\n\nexport interface TimeScaleHeaderProps {\n /** Array of dates to display (from getMultiMonthDays) */\n days: Date[];\n /** Width of each day column in pixels */\n dayWidth: number;\n /** Height of the header row in pixels */\n headerHeight: number;\n}\n\n/**\n * TimeScaleHeader component - displays two-row date headers for the Gantt chart\n *\n * Top row: Month names (Russian, left-aligned) spanning multiple day columns\n * Bottom row: Day numbers (centered) in individual columns\n */\nconst TimeScaleHeader: React.FC<TimeScaleHeaderProps> = ({\n days,\n dayWidth,\n headerHeight,\n}) => {\n // Calculate month spans using the utility from dateUtils\n const monthSpans = useMemo(() => getMonthSpans(days), [days]);\n\n // Split header height evenly between two rows\n const rowHeight = headerHeight / 2;\n\n // Calculate grid template for day row\n const dayGridTemplate = useMemo(\n () => `repeat(${days.length}, ${dayWidth}px)`,\n [days.length, dayWidth]\n );\n\n return (\n <div\n className=\"gantt-tsh-header\"\n style={{ height: `${headerHeight}px` }}\n >\n {/* Month row - top */}\n <div\n className=\"gantt-tsh-monthRow\"\n style={{ height: `${rowHeight}px` }}\n >\n {monthSpans.map((span: MonthSpan, index: number) => (\n <div\n key={`month-${index}`}\n className=\"gantt-tsh-monthCell\"\n style={{ width: `${span.days * dayWidth}px` }}\n >\n {format(span.month, 'LLLL yyyy', { locale: ru }).replace(/^./, (c) => c.toUpperCase())}\n </div>\n ))}\n </div>\n\n {/* Day row - bottom */}\n <div\n className=\"gantt-tsh-dayRow\"\n style={{\n height: `${rowHeight}px`,\n gridTemplateColumns: dayGridTemplate,\n }}\n >\n {days.map((day, index) => {\n const isWeekend = day.getDay() === 0 || day.getDay() === 6;\n const prevDay = days[index - 1];\n const isMonthBoundary = index > 0 && prevDay && prevDay.getMonth() !== day.getMonth();\n // Use local date comparison for \"today\" (user's current date)\n const now = new Date();\n const isTodayDate =\n day.getUTCFullYear() === now.getFullYear() &&\n day.getUTCMonth() === now.getMonth() &&\n day.getUTCDate() === now.getDate();\n return (\n <div key={`day-${index}`} className={`gantt-tsh-dayCell ${isWeekend ? 'gantt-tsh-weekendDay' : ''} ${isMonthBoundary ? 'gantt-tsh-monthBoundary' : ''} ${isTodayDate ? 'gantt-tsh-today' : ''}`}>\n <span className=\"gantt-tsh-dayLabel\">{format(day, 'd')}</span>\n </div>\n );\n })}\n </div>\n </div>\n );\n};\n\nexport default TimeScaleHeader;\n","'use client';\n\nimport React, { useMemo, useState, useEffect, useRef } from 'react';\nimport { parseUTCDate, formatDateLabel } from '../../utils/dateUtils';\nimport { calculateTaskBar, pixelsToDate } from '../../utils/geometry';\nimport { useTaskDrag } from '../../hooks/useTaskDrag';\nimport type { Task } from '../GanttChart';\nimport './TaskRow.css';\n\nexport interface TaskRowProps {\n /** Task data to render */\n task: Task;\n /** Start of the month for positioning calculations */\n monthStart: Date;\n /** Width of each day column in pixels */\n dayWidth: number;\n /** Height of the task row in pixels */\n rowHeight: number;\n /** Callback when task is modified via drag/resize */\n onChange?: (updatedTask: Task) => void;\n /** Callback when task drag state changes (for rendering guide lines) */\n onDragStateChange?: (state: {\n isDragging: boolean;\n dragMode: 'move' | 'resize-left' | 'resize-right' | null;\n left: number;\n width: number;\n }) => void;\n}\n\n/**\n * Custom comparison function for React.memo\n *\n * Performance optimization: Only re-renders if task properties that affect rendering change.\n *\n * NOTE: onChange is intentionally excluded from this comparison because:\n * 1. The parent (GanttChart) wraps onChange in useCallback for referential stability\n * 2. onChange is only called AFTER drag completes (not during drag)\n * 3. During drag, only the dragged TaskRow re-renders due to its internal drag state\n * 4. Other TaskRows don't need to re-render when one task is dragged\n *\n * NOTE: monthStart MUST be included because task positions are calculated relative to it.\n * When the grid expands (e.g., dragging a task left beyond the boundary), monthStart changes\n * and all tasks need to re-render to update their positions.\n *\n * Excluding onChange prevents re-render storms when dragging tasks with ~100 tasks.\n */\nconst arePropsEqual = (prevProps: TaskRowProps, nextProps: TaskRowProps) => {\n return (\n prevProps.task.id === nextProps.task.id &&\n prevProps.task.name === nextProps.task.name &&\n prevProps.task.startDate === nextProps.task.startDate &&\n prevProps.task.endDate === nextProps.task.endDate &&\n prevProps.task.color === nextProps.task.color &&\n prevProps.monthStart.getTime() === nextProps.monthStart.getTime() &&\n prevProps.dayWidth === nextProps.dayWidth &&\n prevProps.rowHeight === nextProps.rowHeight\n // onChange excluded - see note above\n );\n};\n\n/**\n * TaskRow component - renders a single task row with a task bar\n *\n * Uses React.memo for performance optimization (QL-01).\n * The task bar is positioned absolutely based on start/end dates.\n */\nconst TaskRow: React.FC<TaskRowProps> = React.memo(\n ({ task, monthStart, dayWidth, rowHeight, onChange, onDragStateChange }) => {\n // Parse dates as UTC\n const taskStartDate = useMemo(() => parseUTCDate(task.startDate), [task.startDate]);\n const taskEndDate = useMemo(() => parseUTCDate(task.endDate), [task.endDate]);\n\n // Calculate task bar position and dimensions\n const { left, width } = useMemo(\n () => calculateTaskBar(taskStartDate, taskEndDate, monthStart, dayWidth),\n [taskStartDate, taskEndDate, monthStart, dayWidth]\n );\n\n // Determine task bar color\n const barColor = task.color || 'var(--gantt-task-bar-default-color)';\n\n // Handle drag end - call onChange with updated task\n const handleDragEnd = (result: { id: string; startDate: Date; endDate: Date }) => {\n const updatedTask: Task = {\n ...task,\n startDate: result.startDate.toISOString(),\n endDate: result.endDate.toISOString(),\n };\n onChange?.(updatedTask);\n };\n\n // Use drag hook for interactive drag/resize\n const {\n isDragging,\n dragMode,\n currentLeft,\n currentWidth,\n dragHandleProps,\n } = useTaskDrag({\n taskId: task.id,\n initialStartDate: taskStartDate,\n initialEndDate: taskEndDate,\n monthStart,\n dayWidth,\n onDragEnd: handleDragEnd,\n onDragStateChange,\n edgeZoneWidth: 20,\n });\n\n // Use dynamic position during drag\n const displayLeft = isDragging ? currentLeft : left;\n const displayWidth = isDragging ? currentWidth : width;\n\n // Format date labels for display - update in real-time during drag\n const currentStartDate = isDragging\n ? pixelsToDate(displayLeft, monthStart, dayWidth)\n : taskStartDate;\n const currentEndDate = isDragging\n ? pixelsToDate(displayLeft + displayWidth - dayWidth, monthStart, dayWidth)\n : taskEndDate;\n\n const startDateLabel = formatDateLabel(currentStartDate);\n const endDateLabel = formatDateLabel(currentEndDate);\n\n // Calculate duration in days\n const durationDays = Math.round(\n (currentEndDate.getTime() - currentStartDate.getTime()) / (1000 * 60 * 60 * 24)\n ) + 1;\n\n // Detect if task name overflows the bar\n const [isNameOverflow, setIsNameOverflow] = useState(false);\n const taskNameRef = useRef<HTMLSpanElement>(null);\n\n useEffect(() => {\n const nameEl = taskNameRef.current;\n if (nameEl) {\n // Check if task name is wider than available space\n // Reserved space for dates, duration, separator, and handles\n const reservedWidth = 120;\n const availableWidth = displayWidth - reservedWidth;\n setIsNameOverflow(nameEl.scrollWidth > availableWidth);\n }\n }, [displayWidth, task.name]);\n\n return (\n <div\n className=\"gantt-tr-row\"\n style={{ height: `${rowHeight}px` }}\n >\n <div className=\"gantt-tr-taskContainer\">\n <div\n data-taskbar\n className={`gantt-tr-taskBar ${isDragging ? 'gantt-tr-dragging' : ''}`}\n style={{\n left: `${displayLeft}px`,\n width: `${displayWidth}px`,\n backgroundColor: barColor,\n height: 'var(--gantt-task-bar-height)',\n cursor: dragHandleProps.style.cursor,\n userSelect: dragHandleProps.style.userSelect,\n }}\n onMouseDown={dragHandleProps.onMouseDown}\n >\n <div className=\"gantt-tr-resizeHandle gantt-tr-resizeHandleLeft\" />\n <span className=\"gantt-tr-taskDuration\">\n {durationDays} д\n </span>\n <span\n ref={taskNameRef}\n className={`gantt-tr-taskName ${isNameOverflow ? 'gantt-tr-taskNameHidden' : ''}`}\n >\n — {task.name}\n </span>\n <div className=\"gantt-tr-resizeHandle gantt-tr-resizeHandleRight\" />\n </div>\n <div\n className=\"gantt-tr-leftLabels\"\n style={{\n left: `${displayLeft}px`\n }}\n >\n <span className=\"gantt-tr-dateLabel gantt-tr-dateLabelLeft\">\n {startDateLabel}–{endDateLabel}\n </span>\n </div>\n <div\n className=\"gantt-tr-rightLabels\"\n style={{\n left: `${displayLeft + displayWidth}px`,\n }}\n >\n {isNameOverflow && (\n <span className=\"gantt-tr-externalTaskName\">\n {task.name}\n </span>\n )}\n </div>\n </div>\n </div>\n );\n },\n arePropsEqual\n);\n\nTaskRow.displayName = 'TaskRow';\n\nexport default TaskRow;\n","/**\n * Calculate day difference in UTC\n */\nconst getUTCDayDifference = (date1: Date, date2: Date): number => {\n const ms1 = Date.UTC(\n date1.getUTCFullYear(),\n date1.getUTCMonth(),\n date1.getUTCDate()\n );\n const ms2 = Date.UTC(\n date2.getUTCFullYear(),\n date2.getUTCMonth(),\n date2.getUTCDate()\n );\n return Math.round((ms1 - ms2) / (1000 * 60 * 60 * 24));\n};\n\n/**\n * Calculate task bar positioning and dimensions\n * @param taskStartDate - Start date of the task\n * @param taskEndDate - End date of the task\n * @param monthStart - Start of the month/visible range\n * @param dayWidth - Width of each day in pixels\n * @returns Object with left position and width in pixels\n */\nexport const calculateTaskBar = (\n taskStartDate: Date,\n taskEndDate: Date,\n monthStart: Date,\n dayWidth: number\n): { left: number; width: number } => {\n const startOffset = getUTCDayDifference(taskStartDate, monthStart);\n const duration = getUTCDayDifference(taskEndDate, taskStartDate);\n\n // Round to avoid sub-pixel rendering issues\n const left = Math.round(startOffset * dayWidth);\n const width = Math.round((duration + 1) * dayWidth); // +1 to include end date\n\n return { left, width };\n};\n\n/**\n * Convert pixel position to date (inverse of calculateTaskBar)\n * @param pixels - Position in pixels (left or width)\n * @param monthStart - Start of the month/visible range\n * @param dayWidth - Width of each day in pixels\n * @returns Date calculated from pixel position\n */\nexport const pixelsToDate = (pixels: number, monthStart: Date, dayWidth: number): Date => {\n const days = Math.round(pixels / dayWidth);\n return new Date(Date.UTC(\n monthStart.getUTCFullYear(),\n monthStart.getUTCMonth(),\n monthStart.getUTCDate() + days\n ));\n};\n\n/**\n * Calculate total width for month grid\n * @param daysInMonth - Number of days in the month\n * @param dayWidth - Width of each day in pixels\n * @returns Total grid width in pixels\n */\nexport const calculateGridWidth = (daysInMonth: number, dayWidth: number): number => {\n return Math.round(daysInMonth * dayWidth);\n};\n\n/**\n * Detect which edge zone the cursor is in on a task bar\n * @param clientX - Mouse X coordinate relative to viewport\n * @param taskBarElement - The task bar DOM element\n * @param edgeZoneWidth - Width of edge zones in pixels (default: 12px)\n * @returns 'left' if in left edge, 'right' if in right edge, 'move' if in middle\n */\nexport const detectEdgeZone = (\n clientX: number,\n taskBarElement: HTMLElement,\n edgeZoneWidth: number = 12\n): 'left' | 'right' | 'move' => {\n const rect = taskBarElement.getBoundingClientRect();\n const relativeX = Math.round(clientX - rect.left);\n\n // Check left edge zone\n if (relativeX >= 0 && relativeX <= edgeZoneWidth) {\n return 'left';\n }\n\n // Check right edge zone\n const width = Math.round(rect.width);\n if (relativeX >= width - edgeZoneWidth && relativeX <= width) {\n return 'right';\n }\n\n // Middle area - move mode\n return 'move';\n};\n\n/**\n * Get appropriate cursor style for drag position\n * @param position - The drag position (left edge, right edge, or move)\n * @returns CSS cursor string for the position\n */\nexport const getCursorForPosition = (position: 'left' | 'right' | 'move'): string => {\n switch (position) {\n case 'left':\n case 'right':\n return 'ew-resize';\n case 'move':\n return 'grab';\n default:\n return 'default';\n }\n};\n\n/**\n * Calculate grid line positions for a date range\n * @param dateRange - Array of Date objects representing the visible range\n * @param dayWidth - Width of each day column in pixels\n * @returns Array of grid line objects with x position and flags\n */\nexport const calculateGridLines = (\n dateRange: Date[],\n dayWidth: number\n): Array<{ x: number; isMonthStart: boolean; isWeekStart: boolean }> => {\n const lines: Array<{ x: number; isMonthStart: boolean; isWeekStart: boolean }> = [];\n\n for (let i = 0; i < dateRange.length; i++) {\n const date = dateRange[i];\n const x = Math.round(i * dayWidth);\n const isMonthStart = date.getUTCDate() === 1;\n const isWeekStart = date.getUTCDay() === 1; // Monday\n\n lines.push({ x, isMonthStart, isWeekStart });\n }\n\n // Add final line at the end of the range\n if (dateRange.length > 0) {\n lines.push({\n x: Math.round(dateRange.length * dayWidth),\n isMonthStart: false,\n isWeekStart: false\n });\n }\n\n return lines;\n};\n\n/**\n * Calculate weekend background blocks for a date range\n * @param dateRange - Array of Date objects representing the visible range\n * @param dayWidth - Width of each day column in pixels\n * @returns Array of weekend block objects with left position and width\n */\nexport const calculateWeekendBlocks = (\n dateRange: Date[],\n dayWidth: number\n): Array<{ left: number; width: number }> => {\n const blocks: Array<{ left: number; width: number }> = [];\n let inWeekend = false;\n let weekendStartIndex = -1;\n\n for (let i = 0; i < dateRange.length; i++) {\n const date = dateRange[i];\n const dayOfWeek = date.getUTCDay();\n const isWeekend = dayOfWeek === 0 || dayOfWeek === 6; // Sunday or Saturday\n\n if (isWeekend && !inWeekend) {\n // Start of a weekend block\n inWeekend = true;\n weekendStartIndex = i;\n } else if (!isWeekend && inWeekend) {\n // End of a weekend block\n inWeekend = false;\n const left = Math.round(weekendStartIndex * dayWidth);\n const width = Math.round((i - weekendStartIndex) * dayWidth);\n blocks.push({ left, width });\n }\n }\n\n // Handle case where range ends on a weekend\n if (inWeekend && weekendStartIndex >= 0) {\n const left = Math.round(weekendStartIndex * dayWidth);\n const width = Math.round((dateRange.length - weekendStartIndex) * dayWidth);\n blocks.push({ left, width });\n }\n\n return blocks;\n};\n","'use client';\n\nimport { useEffect, useRef, useState, useCallback } from 'react';\nimport { detectEdgeZone } from '../utils/geometry';\n\n/**\n * Global drag manager that persists across HMR\n *\n * This singleton manages active drag operations at the module level,\n * ensuring that drag state survives React Fast Refresh (HMR).\n *\n * The key insight: When HMR occurs during a drag operation:\n * 1. The component unmounts and its useEffect cleanup removes window listeners\n * 2. The component remounts with fresh refs (isDraggingRef = false)\n * 3. But the user is still holding the mouse button!\n * 4. Without module-level state, the drag operation is orphaned\n *\n * Solution: Store active drag state in module-level singleton and\n * use a global cleanup effect to always handle mouseup/mousemove.\n */\ninterface ActiveDragState {\n taskId: string;\n mode: 'move' | 'resize-left' | 'resize-right';\n startX: number;\n initialLeft: number;\n initialWidth: number;\n currentLeft: number;\n currentWidth: number;\n dayWidth: number;\n monthStart: Date;\n onProgress: (left: number, width: number) => void;\n onComplete: (finalLeft: number, finalWidth: number) => void;\n onCancel: () => void;\n}\n\nlet globalActiveDrag: ActiveDragState | null = null;\nlet globalRafId: number | null = null;\n\n/**\n * Complete the active drag operation\n */\nfunction completeDrag() {\n if (globalRafId !== null) {\n cancelAnimationFrame(globalRafId);\n globalRafId = null;\n }\n\n if (globalActiveDrag) {\n const { onComplete, currentLeft, currentWidth } = globalActiveDrag;\n const drag = globalActiveDrag;\n globalActiveDrag = null;\n onComplete(currentLeft, currentWidth);\n }\n}\n\n/**\n * Cancel the active drag operation\n */\nfunction cancelDrag() {\n if (globalRafId !== null) {\n cancelAnimationFrame(globalRafId);\n globalRafId = null;\n }\n\n if (globalActiveDrag) {\n const { onCancel } = globalActiveDrag;\n globalActiveDrag = null;\n onCancel();\n }\n}\n\n/**\n * Snap pixel value to grid (day boundaries)\n */\nfunction snapToGrid(pixels: number, dayWidth: number): number {\n return Math.round(pixels / dayWidth) * dayWidth;\n}\n\n/**\n * Global mouse move handler - attached once and persists across HMR\n */\nfunction handleGlobalMouseMove(e: MouseEvent) {\n if (!globalActiveDrag || globalRafId !== null) {\n return;\n }\n\n globalRafId = requestAnimationFrame(() => {\n if (!globalActiveDrag) {\n globalRafId = null;\n return;\n }\n\n const { startX, initialLeft, initialWidth, mode, dayWidth, onProgress } = globalActiveDrag;\n const deltaX = e.clientX - startX;\n\n let newLeft = initialLeft;\n let newWidth = initialWidth;\n\n switch (mode) {\n case 'move':\n newLeft = snapToGrid(initialLeft + deltaX, dayWidth);\n break;\n case 'resize-left':\n const snappedLeft = snapToGrid(initialLeft + deltaX, dayWidth);\n newLeft = snappedLeft;\n const rightEdge = initialLeft + initialWidth;\n newWidth = Math.max(dayWidth, rightEdge - snappedLeft);\n break;\n case 'resize-right':\n const snappedWidth = snapToGrid(initialWidth + deltaX, dayWidth);\n newWidth = Math.max(dayWidth, snappedWidth);\n break;\n }\n\n // Update current values in global state for completion\n globalActiveDrag.currentLeft = newLeft;\n globalActiveDrag.currentWidth = newWidth;\n\n onProgress(newLeft, newWidth);\n globalRafId = null;\n });\n}\n\n/**\n * Global mouse up handler - attached once and persists across HMR\n */\nfunction handleGlobalMouseUp() {\n if (globalActiveDrag) {\n completeDrag();\n }\n}\n\n/**\n * Track whether global listeners are attached\n */\nlet globalListenersAttached = false;\n\n/**\n * Ensure global listeners are attached (idempotent)\n */\nfunction ensureGlobalListeners() {\n if (!globalListenersAttached) {\n window.addEventListener('mousemove', handleGlobalMouseMove);\n window.addEventListener('mouseup', handleGlobalMouseUp);\n globalListenersAttached = true;\n }\n}\n\n/**\n * Cleanup global listeners - called when no components are using drag\n * Note: In practice with HMR, we keep these attached for safety\n */\nfunction cleanupGlobalListeners() {\n // We keep global listeners attached to handle orphaned drags after HMR\n // They will be cleaned up when the page is refreshed\n}\n\n/**\n * Options for useTaskDrag hook\n */\nexport interface UseTaskDragOptions {\n /** Unique identifier for the task */\n taskId: string;\n /** Initial start date of the task */\n initialStartDate: Date;\n /** Initial end date of the task */\n initialEndDate: Date;\n /** Start of the visible range (e.g., month start) */\n monthStart: Date;\n /** Width of each day in pixels */\n dayWidth: number;\n /** Callback when drag operation completes */\n onDragEnd?: (result: { id: string; startDate: Date; endDate: Date }) => void;\n /** Callback for drag state changes (for parent components to render guide lines) */\n onDragStateChange?: (state: {\n isDragging: boolean;\n dragMode: 'move' | 'resize-left' | 'resize-right' | null;\n left: number;\n width: number;\n }) => void;\n /** Width of edge zones for resize detection (default: 12px) */\n edgeZoneWidth?: number;\n}\n\n/**\n * Return value from useTaskDrag hook\n */\nexport interface UseTaskDragReturn {\n /** Whether a drag operation is in progress */\n isDragging: boolean;\n /** Current drag mode (null when not dragging) */\n dragMode: 'move' | 'resize-left' | 'resize-right' | null;\n /** Current left position in pixels (updated during drag) */\n currentLeft: number;\n /** Current width in pixels (updated during drag) */\n currentWidth: number;\n /** Props to spread on the drag handle element */\n dragHandleProps: {\n onMouseDown: (e: React.MouseEvent) => void;\n style: React.CSSProperties;\n };\n}\n\n/**\n * Custom hook for managing task drag interactions\n *\n * HMR-SAFE: Uses module-level singleton to ensure drag state survives\n * React Fast Refresh. Window event listeners are attached once at module\n * level rather than per component instance.\n */\nexport const useTaskDrag = (options: UseTaskDragOptions): UseTaskDragReturn => {\n const {\n taskId,\n initialStartDate,\n initialEndDate,\n monthStart,\n dayWidth,\n onDragEnd,\n onDragStateChange,\n edgeZoneWidth = 12,\n } = options;\n\n // Track if this hook instance owns the current global drag\n const isOwnerRef = useRef<boolean>(false);\n\n // Display state (triggers re-renders only when needed)\n const [isDragging, setIsDragging] = useState<boolean>(false);\n const [dragMode, setDragMode] = useState<'move' | 'resize-left' | 'resize-right' | null>(null);\n const [currentLeft, setCurrentLeft] = useState<number>(0);\n const [currentWidth, setCurrentWidth] = useState<number>(0);\n\n /**\n * Calculate initial pixel position from dates\n */\n const getInitialPosition = useCallback((): { left: number; width: number } => {\n const getUTCDayDifference = (date1: Date, date2: Date): number => {\n const ms1 = Date.UTC(\n date1.getUTCFullYear(),\n date1.getUTCMonth(),\n date1.getUTCDate()\n );\n const ms2 = Date.UTC(\n date2.getUTCFullYear(),\n date2.getUTCMonth(),\n date2.getUTCDate()\n );\n return Math.round((ms1 - ms2) / (1000 * 60 * 60 * 24));\n };\n\n const startOffset = getUTCDayDifference(initialStartDate, monthStart);\n const duration = getUTCDayDifference(initialEndDate, initialStartDate);\n\n const left = Math.round(startOffset * dayWidth);\n const width = Math.round((duration + 1) * dayWidth); // +1 to include end date\n\n return { left, width };\n }, [initialStartDate, initialEndDate, monthStart, dayWidth]);\n\n /**\n * Initialize position when dates or dayWidth changes\n */\n useEffect(() => {\n const { left, width } = getInitialPosition();\n setCurrentLeft(left);\n setCurrentWidth(width);\n }, [getInitialPosition]);\n\n /**\n * Handle drag progress callback from global manager\n */\n const handleProgress = useCallback((left: number, width: number) => {\n setCurrentLeft(left);\n setCurrentWidth(width);\n\n if (onDragStateChange && isOwnerRef.current) {\n const mode = globalActiveDrag?.mode || null;\n onDragStateChange({\n isDragging: true,\n dragMode: mode,\n left,\n width,\n });\n }\n }, [onDragStateChange]);\n\n /**\n * Handle drag completion from global manager\n */\n const handleComplete = useCallback((finalLeft: number, finalWidth: number) => {\n const wasOwner = isOwnerRef.current;\n isOwnerRef.current = false;\n\n // Calculate new dates from final pixel values\n const dayOffset = Math.round(finalLeft / dayWidth);\n const durationDays = Math.round(finalWidth / dayWidth) - 1; // -1 because width includes end date\n\n const newStartDate = new Date(Date.UTC(\n monthStart.getUTCFullYear(),\n monthStart.getUTCMonth(),\n monthStart.getUTCDate() + dayOffset\n ));\n\n const newEndDate = new Date(Date.UTC(\n monthStart.getUTCFullYear(),\n monthStart.getUTCMonth(),\n monthStart.getUTCDate() + dayOffset + durationDays\n ));\n\n // Reset local state\n setIsDragging(false);\n setDragMode(null);\n\n // Notify parent of drag end\n if (onDragStateChange) {\n onDragStateChange({\n isDragging: false,\n dragMode: null,\n left: finalLeft,\n width: finalWidth,\n });\n }\n\n // Notify parent of drag completion (only if we were the owner)\n if (onDragEnd && wasOwner) {\n onDragEnd({\n id: taskId,\n startDate: newStartDate,\n endDate: newEndDate,\n });\n }\n }, [dayWidth, monthStart, onDragEnd, onDragStateChange, taskId]);\n\n /**\n * Handle drag cancellation (e.g., if HMR orphaned the drag)\n */\n const handleCancel = useCallback(() => {\n isOwnerRef.current = false;\n setIsDragging(false);\n setDragMode(null);\n\n if (onDragStateChange) {\n onDragStateChange({\n isDragging: false,\n dragMode: null,\n left: currentLeft,\n width: currentWidth,\n });\n }\n }, [onDragStateChange, currentLeft, currentWidth]);\n\n /**\n * Cleanup on unmount - if this instance owns the drag, cancel it\n */\n useEffect(() => {\n return () => {\n if (isOwnerRef.current && globalActiveDrag) {\n // We're unmounting while owning the drag - cancel it\n cancelDrag();\n }\n };\n }, []);\n\n /**\n * Handle mouse down on drag handle\n */\n const handleMouseDown = useCallback((e: React.MouseEvent) => {\n const target = e.currentTarget as HTMLElement;\n const edgeZone = detectEdgeZone(e.clientX, target, edgeZoneWidth);\n\n // Determine drag mode from edge zone\n let mode: 'move' | 'resize-left' | 'resize-right' | null = null;\n switch (edgeZone) {\n case 'left':\n mode = 'resize-left';\n break;\n case 'right':\n mode = 'resize-right';\n break;\n case 'move':\n mode = 'move';\n break;\n }\n\n if (!mode) {\n return;\n }\n\n // Get current position from state (this is what we see on screen)\n const initialLeft = currentLeft;\n const initialWidth = currentWidth;\n\n // Mark this instance as the drag owner\n isOwnerRef.current = true;\n\n // Update display state\n setIsDragging(true);\n setDragMode(mode);\n\n // Notify parent of drag start\n if (onDragStateChange) {\n onDragStateChange({\n isDragging: true,\n dragMode: mode,\n left: initialLeft,\n width: initialWidth,\n });\n }\n\n // Ensure global listeners are attached (idempotent)\n ensureGlobalListeners();\n\n // Store drag state in global singleton\n globalActiveDrag = {\n taskId,\n mode,\n startX: e.clientX,\n initialLeft,\n initialWidth,\n currentLeft: initialLeft, // Initially same as initial\n currentWidth: initialWidth, // Initially same as initial\n dayWidth,\n monthStart,\n onProgress: handleProgress,\n onComplete: handleComplete,\n onCancel: handleCancel,\n };\n }, [edgeZoneWidth, currentLeft, currentWidth, dayWidth, monthStart, taskId, onDragStateChange, handleProgress, handleComplete, handleCancel]);\n\n /**\n * Get cursor style based on current position\n */\n const getCursorStyle = useCallback((): string => {\n if (isDragging) {\n return 'grabbing';\n }\n return 'grab';\n }, [isDragging]);\n\n return {\n isDragging,\n dragMode,\n currentLeft,\n currentWidth,\n dragHandleProps: {\n onMouseDown: handleMouseDown,\n style: {\n cursor: getCursorStyle(),\n userSelect: 'none',\n } as React.CSSProperties,\n },\n };\n};\n","'use client';\n\nimport React, { useMemo } from 'react';\nimport { getDayOffset, isToday } from '../../utils/dateUtils';\nimport './TodayIndicator.css';\n\nexport interface TodayIndicatorProps {\n /** Start of the month for positioning calculations */\n monthStart: Date;\n /** Width of each day column in pixels */\n dayWidth: number;\n}\n\n/**\n * TodayIndicator component - displays a vertical line at the current date\n *\n * Only renders when the current date is within the visible month range.\n * Satisfies REND-04 requirement for visual today indicator.\n */\nconst TodayIndicator: React.FC<TodayIndicatorProps> = ({ monthStart, dayWidth }) => {\n // Use local date for \"today\" (not UTC) - user's current date matters\n const today = new Date();\n const todayLocal = new Date(Date.UTC(\n today.getFullYear(),\n today.getMonth(),\n today.getDate()\n ));\n\n // Check if today is within the current month (UTC comparison with local today)\n const isInMonth = useMemo(() => {\n return (\n todayLocal.getUTCFullYear() === monthStart.getUTCFullYear() &&\n todayLocal.getUTCMonth() === monthStart.getUTCMonth()\n );\n }, [monthStart, todayLocal]);\n\n // Calculate position if today is in the month\n const position = useMemo(() => {\n if (!isInMonth) return null;\n\n const offset = getDayOffset(todayLocal, monthStart);\n return Math.round(offset * dayWidth);\n }, [isInMonth, monthStart, dayWidth, todayLocal]);\n\n if (!isInMonth || position === null) {\n return null;\n }\n\n return (\n <div\n className=\"gantt-ti-indicator\"\n style={{\n left: `${position}px`,\n width: 'var(--gantt-today-indicator-width)',\n backgroundColor: 'var(--gantt-today-indicator-color)',\n }}\n aria-label=\"Today\"\n />\n );\n};\n\nexport default TodayIndicator;\n","'use client';\n\nimport React, { useMemo } from 'react';\nimport { calculateGridLines, calculateWeekendBlocks } from '../../utils/geometry';\nimport type { GridLine } from '../../types';\nimport './GridBackground.css';\n\nexport interface GridBackgroundProps {\n /** Array of dates to display (from getMultiMonthDays) */\n dateRange: Date[];\n /** Width of each day column in pixels */\n dayWidth: number;\n /** Total height of the grid area in pixels */\n totalHeight: number;\n}\n\n/**\n * Custom comparison function for React.memo\n *\n * Performance optimization: Only re-renders if dateRange or dayWidth change.\n * totalHeight is excluded because it only affects container height, not grid calculations.\n */\nconst arePropsEqual = (prevProps: GridBackgroundProps, nextProps: GridBackgroundProps) => {\n return (\n prevProps.dayWidth === nextProps.dayWidth &&\n prevProps.dateRange.length === nextProps.dateRange.length &&\n prevProps.totalHeight !== nextProps.totalHeight // totalHeight changes still trigger update\n );\n};\n\n/**\n * GridBackground component - renders vertical grid lines and weekend background highlighting\n *\n * This component provides the visual grid structure that runs behind task rows.\n * It separates grid rendering from task rendering for better performance and cleaner code.\n *\n * Features:\n * - Vertical grid lines at month/week/day boundaries\n * - Pink background highlighting for weekend days\n * - React.memo optimization for performance\n * - Pointer events disabled (clicks pass through to tasks)\n */\nconst GridBackground: React.FC<GridBackgroundProps> = React.memo(\n ({ dateRange, dayWidth, totalHeight }) => {\n // Calculate grid line positions\n const gridLines = useMemo<GridLine[]>(() => {\n return calculateGridLines(dateRange, dayWidth);\n }, [dateRange, dayWidth]);\n\n // Calculate weekend background blocks\n const weekendBlocks = useMemo(() => {\n return calculateWeekendBlocks(dateRange, dayWidth);\n }, [dateRange, dayWidth]);\n\n // Calculate total grid width\n const gridWidth = useMemo(() => {\n return Math.round(dateRange.length * dayWidth);\n }, [dateRange.length, dayWidth]);\n\n return (\n <div\n className=\"gantt-gb-gridBackground\"\n style={{\n width: `${gridWidth}px`,\n height: `${totalHeight}px`,\n }}\n >\n {/* Weekend backgrounds (rendered first, behind lines) */}\n {weekendBlocks.map((block, index) => (\n <div\n key={`weekend-${index}`}\n className=\"gantt-gb-weekendBlock\"\n style={{\n left: `${block.left}px`,\n width: `${block.width}px`,\n }}\n />\n ))}\n\n {/* Vertical grid lines */}\n {gridLines.map((line, index) => {\n // Determine line type class based on flags\n const lineClass = line.isMonthStart\n ? 'gantt-gb-monthSeparator'\n : line.isWeekStart\n ? 'gantt-gb-weekSeparator'\n : 'gantt-gb-dayLine';\n\n return (\n <div\n key={`gridline-${index}`}\n className={`gantt-gb-gridLine ${lineClass}`}\n style={{\n left: `${line.x}px`,\n }}\n />\n );\n })}\n </div>\n );\n },\n arePropsEqual\n);\n\nGridBackground.displayName = 'GridBackground';\n\nexport default GridBackground;\n","'use client';\n\nimport React from 'react';\nimport './DragGuideLines.css';\n\nexport interface DragGuideLinesProps {\n isDragging: boolean;\n dragMode: 'move' | 'resize-left' | 'resize-right' | null;\n left: number;\n width: number;\n totalHeight: number;\n}\n\nconst DragGuideLines: React.FC<DragGuideLinesProps> = ({\n isDragging,\n dragMode,\n left,\n width,\n totalHeight,\n}) => {\n if (!isDragging || !dragMode) {\n return null;\n }\n\n // Determine which lines to show based on drag mode\n const showLeftLine = dragMode === 'move' || dragMode === 'resize-left';\n const showRightLine = dragMode === 'move' || dragMode === 'resize-right';\n\n return (\n <>\n {showLeftLine && (\n <div\n className=\"gantt-dgl-guideLine\"\n style={{\n left: `${left}px`,\n height: `${totalHeight}px`,\n }}\n />\n )}\n {showRightLine && (\n <div\n className=\"gantt-dgl-guideLine\"\n style={{\n left: `${left + width}px`,\n height: `${totalHeight}px`,\n }}\n />\n )}\n </>\n );\n};\n\nexport default DragGuideLines;\n"],"mappings":"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","names":["useMemo","useCallback","useRef","useState","useEffect","isWeekend","React","useMemo","useState","useEffect","useRef","isWeekend","getUTCDayDifference","jsx","jsxs","React","useMemo","useState","useRef","useEffect","useMemo","jsx","useMemo","React","useMemo","jsx","jsxs","arePropsEqual","React","useMemo","jsx","jsxs","jsx","jsxs","useRef","useMemo","useState","useCallback","useEffect"]}
1
+ {"version":3,"sources":["../src/components/GanttChart/GanttChart.tsx","../src/utils/dateUtils.ts","../src/utils/dependencyUtils.ts","../src/components/TimeScaleHeader/TimeScaleHeader.tsx","../src/components/TaskRow/TaskRow.tsx","../src/utils/geometry.ts","../src/hooks/useTaskDrag.ts","../src/components/TodayIndicator/TodayIndicator.tsx","../src/components/GridBackground/GridBackground.tsx","../src/components/DragGuideLines/DragGuideLines.tsx","../src/components/DependencyLines/DependencyLines.tsx"],"sourcesContent":["'use client';\n\nimport React, { useMemo, useCallback, useRef, useState, useEffect } from 'react';\nimport { getMultiMonthDays } from '../../utils/dateUtils';\nimport { calculateGridWidth } from '../../utils/geometry';\nimport { validateDependencies } from '../../utils/dependencyUtils';\nimport type { ValidationResult } from '../../types';\nimport TimeScaleHeader from '../TimeScaleHeader';\nimport TaskRow from '../TaskRow';\nimport TodayIndicator from '../TodayIndicator';\nimport GridBackground from '../GridBackground';\nimport DragGuideLines from '../DragGuideLines/DragGuideLines';\nimport { DependencyLines } from '../DependencyLines';\nimport './GanttChart.css';\n\n/**\n * Task data structure for Gantt chart\n */\nexport interface Task {\n /** Unique identifier for the task */\n id: string;\n /** Display name of the task */\n name: string;\n /** Task start date (ISO string or Date object) */\n startDate: string | Date;\n /** Task end date (ISO string or Date object) */\n endDate: string | Date;\n /** Optional color for task bar visualization */\n color?: string;\n /**\n * Optional progress value from 0-100\n * - Decimal values are allowed and rounded to nearest integer for display\n * - Values are clamped to 0-100 range\n * - Undefined or 0 means no progress is displayed\n * - Progress is visual-only, no user interaction\n */\n progress?: number;\n /**\n * Optional flag indicating if task is accepted\n * - Only meaningful when progress is 100%\n * - Affects the color of the progress bar (green for accepted, yellow for completed)\n */\n accepted?: boolean;\n /**\n * Optional array of task dependencies\n * - Each dependency references a predecessor task by ID\n * - Supports 4 link types: FS (finish-to-start), SS (start-to-start), FF (finish-to-finish), SF (start-to-finish)\n * - Lag is optional and defaults to 0 (positive = delay, negative = overlap)\n */\n dependencies?: TaskDependency[];\n}\n\n/**\n * Task dependency definition\n */\nexport interface TaskDependency {\n /** ID of the predecessor task */\n taskId: string;\n /** Link type: FS, SS, FF, or SF */\n type: 'FS' | 'SS' | 'FF' | 'SF';\n /** Optional lag in days (default: 0) */\n lag?: number;\n}\n\nexport interface GanttChartProps {\n /** Array of tasks to display */\n tasks: Task[];\n /** Width of each day column in pixels (default: 40) */\n dayWidth?: number;\n /** Height of each task row in pixels (default: 40) */\n rowHeight?: number;\n /** Height of the header row in pixels (default: 40) */\n headerHeight?: number;\n /** Container height in pixels (default: 600) - adds vertical scrolling when tasks exceed this height */\n containerHeight?: number;\n /** Callback when tasks are modified via drag/resize. Can receive either the new tasks array or a functional updater. */\n onChange?: (tasks: Task[] | ((currentTasks: Task[]) => Task[])) => void;\n /** Optional callback for dependency validation results */\n onValidateDependencies?: (result: ValidationResult) => void;\n /** Enable automatic shifting of dependent tasks when predecessor moves (default: false) */\n enableAutoSchedule?: boolean;\n /** Disable dependency constraint checking during drag (default: false) */\n disableConstraints?: boolean;\n /** Called when a cascade drag completes; receives all shifted tasks (including dragged task) in hard mode */\n onCascade?: (tasks: Task[]) => void;\n}\n\n/**\n * GanttChart component - displays tasks on a monthly timeline with Excel-like styling\n *\n * The calendar automatically shows full months based on task date ranges.\n * For example, if tasks span from March 25 to May 5, the calendar shows\n * the complete months of March, April, and May (March 1 - May 31).\n *\n * @example\n * ```tsx\n * <GanttChart\n * tasks={[\n * { id: '1', name: 'Task 1', startDate: '2026-02-01', endDate: '2026-02-05' }\n * ]}\n * />\n * ```\n */\nexport const GanttChart: React.FC<GanttChartProps> = ({\n tasks,\n dayWidth = 40,\n rowHeight = 40,\n headerHeight = 40,\n containerHeight = 600,\n onChange,\n onValidateDependencies,\n enableAutoSchedule,\n disableConstraints,\n onCascade,\n}) => {\n const scrollContainerRef = useRef<HTMLDivElement>(null);\n\n // Calculate multi-month date range from tasks\n const dateRange = useMemo(() => getMultiMonthDays(tasks), [tasks]);\n\n // Track dependency validation results\n const [validationResult, setValidationResult] = useState<ValidationResult | null>(null);\n\n // Cascade override positions for non-dragged chain members\n const [cascadeOverrides, setCascadeOverrides] = useState<Map<string, { left: number; width: number }>>(new Map());\n\n // Calculate grid width\n const gridWidth = useMemo(\n () => Math.round(dateRange.length * dayWidth),\n [dateRange.length, dayWidth]\n );\n\n // Calculate total grid height\n const totalGridHeight = useMemo(\n () => tasks.length * rowHeight,\n [tasks.length, rowHeight]\n );\n\n // Get month start for calculations (first day of date range)\n const monthStart = useMemo(() => {\n if (dateRange.length === 0) {\n return new Date(Date.UTC(new Date().getUTCFullYear(), new Date().getUTCMonth(), 1));\n }\n const firstDay = dateRange[0];\n return new Date(Date.UTC(firstDay.getUTCFullYear(), firstDay.getUTCMonth(), 1));\n }, [dateRange]);\n\n // Only render TodayIndicator if today is in the visible date range\n const todayInRange = useMemo(() => {\n const now = new Date();\n const today = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));\n return dateRange.some(day => day.getTime() === today.getTime());\n }, [dateRange]);\n\n // Track drag state for guide lines\n const [dragGuideLines, setDragGuideLines] = useState<{\n isDragging: boolean;\n dragMode: 'move' | 'resize-left' | 'resize-right' | null;\n left: number;\n width: number;\n } | null>(null);\n\n // Track currently-dragged task's pixel position for real-time dependency line updates\n const [draggedTaskOverride, setDraggedTaskOverride] = useState<{ taskId: string; left: number; width: number } | null>(null);\n\n // Validate dependencies when tasks change\n useEffect(() => {\n const result = validateDependencies(tasks);\n setValidationResult(result);\n onValidateDependencies?.(result);\n }, [tasks, onValidateDependencies]);\n\n /**\n * Stable callback for task updates\n *\n * FIXED: No longer depends on `tasks` to avoid stale closure bugs.\n * Uses functional state update pattern: the callback receives an updater function\n * that maps over the current tasks state, ensuring we always use the latest state.\n *\n * This prevents the \"reverting\" bug where dragging a second task causes the\n * first task to revert to its original position.\n *\n * To prevent re-render storms during drag:\n * 1. The onChange callback is only called AFTER drag completes (mouseUp)\n * 2. During drag, only the dragged TaskRow re-renders (due to its internal state)\n * 3. Other TaskRows don't re-render because their props haven't changed\n *\n * The React.memo comparison in TaskRow excludes onChange from comparison,\n * relying on the fact that onChange fires only after drag completes.\n */\n const handleTaskChange = useCallback((updatedTask: Task) => {\n // Call onChange with a functional updater that receives the current tasks\n onChange?.((currentTasks) =>\n currentTasks.map((t) =>\n t.id === updatedTask.id ? updatedTask : t\n )\n );\n }, [onChange]);\n\n // Build merged pixel overrides for DependencyLines: dragged task + cascade chain members\n const dependencyOverrides = useMemo(() => {\n const map = new Map(cascadeOverrides);\n if (draggedTaskOverride) {\n map.set(draggedTaskOverride.taskId, {\n left: draggedTaskOverride.left,\n width: draggedTaskOverride.width,\n });\n }\n return map;\n }, [cascadeOverrides, draggedTaskOverride]);\n\n /**\n * Handle real-time cascade progress — updates cascadeOverrides state each RAF\n * so non-dragged chain members re-render with their preview positions.\n * new Map() forces React to detect the state change.\n */\n const handleCascadeProgress = useCallback((overrides: Map<string, { left: number; width: number }>) => {\n setCascadeOverrides(new Map(overrides));\n }, []);\n\n /**\n * Handle cascade completion — updates all shifted tasks via onChange (functional updater)\n * and notifies the external onCascade consumer.\n */\n const handleCascade = useCallback((cascadedTasks: Task[]) => {\n // Update state by merging cascaded tasks into current tasks\n onChange?.((currentTasks) => {\n const cascadeMap = new Map(cascadedTasks.map(t => [t.id, t]));\n return currentTasks.map(t => cascadeMap.get(t.id) ?? t);\n });\n // Notify external consumer\n onCascade?.(cascadedTasks);\n }, [onChange, onCascade]);\n\n // Pan (grab-scroll) on empty grid area\n const panStateRef = useRef<{ active: boolean; startX: number; startY: number; scrollX: number; scrollY: number } | null>(null);\n\n const handlePanStart = useCallback((e: React.MouseEvent) => {\n // Only pan on left click, skip if clicking on a task bar\n if (e.button !== 0) return;\n const target = e.target as HTMLElement;\n if (target.closest('[data-taskbar]')) return;\n\n const container = scrollContainerRef.current;\n if (!container) return;\n\n panStateRef.current = {\n active: true,\n startX: e.clientX,\n startY: e.clientY,\n scrollX: container.scrollLeft,\n scrollY: container.scrollTop,\n };\n container.style.cursor = 'grabbing';\n e.preventDefault();\n }, []);\n\n useEffect(() => {\n const handlePanMove = (e: MouseEvent) => {\n const pan = panStateRef.current;\n if (!pan?.active) return;\n const container = scrollContainerRef.current;\n if (!container) return;\n\n container.scrollLeft = pan.scrollX - (e.clientX - pan.startX);\n container.scrollTop = pan.scrollY - (e.clientY - pan.startY);\n };\n\n const handlePanEnd = () => {\n if (!panStateRef.current?.active) return;\n panStateRef.current = null;\n const container = scrollContainerRef.current;\n if (container) container.style.cursor = '';\n };\n\n window.addEventListener('mousemove', handlePanMove);\n window.addEventListener('mouseup', handlePanEnd);\n return () => {\n window.removeEventListener('mousemove', handlePanMove);\n window.removeEventListener('mouseup', handlePanEnd);\n };\n }, []);\n\n return (\n <div className=\"gantt-container\">\n <div\n ref={scrollContainerRef}\n className=\"gantt-scrollContainer\"\n style={{ height: `${containerHeight}px`, cursor: 'grab' }}\n onMouseDown={handlePanStart}\n >\n {/* Sticky header - stays at top during vertical scroll, scrolls with content horizontally */}\n <div className=\"gantt-stickyHeader\" style={{ width: `${gridWidth}px` }}>\n <TimeScaleHeader\n days={dateRange}\n dayWidth={dayWidth}\n headerHeight={headerHeight}\n />\n </div>\n\n {/* Task area */}\n <div\n className=\"gantt-taskArea\"\n style={{\n position: 'relative',\n width: `${gridWidth}px`,\n }}\n >\n <GridBackground\n dateRange={dateRange}\n dayWidth={dayWidth}\n totalHeight={totalGridHeight}\n />\n\n {todayInRange && <TodayIndicator monthStart={monthStart} dayWidth={dayWidth} />}\n\n {/* Dependency lines SVG overlay */}\n <DependencyLines\n tasks={tasks}\n monthStart={monthStart}\n dayWidth={dayWidth}\n rowHeight={rowHeight}\n gridWidth={gridWidth}\n dragOverrides={dependencyOverrides}\n />\n\n {dragGuideLines && (\n <DragGuideLines\n isDragging={dragGuideLines.isDragging}\n dragMode={dragGuideLines.dragMode}\n left={dragGuideLines.left}\n width={dragGuideLines.width}\n totalHeight={totalGridHeight}\n />\n )}\n\n {tasks.map((task, index) => (\n <TaskRow\n key={task.id}\n task={task}\n monthStart={monthStart}\n dayWidth={dayWidth}\n rowHeight={rowHeight}\n onChange={handleTaskChange}\n onDragStateChange={(state) => {\n if (state.isDragging) {\n setDragGuideLines(state);\n setDraggedTaskOverride({ taskId: task.id, left: state.left, width: state.width });\n } else {\n setDragGuideLines(null);\n setDraggedTaskOverride(null);\n }\n }}\n rowIndex={index}\n allTasks={tasks}\n enableAutoSchedule={enableAutoSchedule ?? false}\n disableConstraints={disableConstraints ?? false}\n overridePosition={cascadeOverrides.get(task.id)}\n onCascadeProgress={handleCascadeProgress}\n onCascade={handleCascade}\n />\n ))}\n </div>\n </div>\n </div>\n );\n};\n\nexport default GanttChart;\n","import { parseISO, isValid } from 'date-fns';\n\n/**\n * Parse date string as UTC to prevent DST issues\n * @param date - Date string or Date object\n * @returns Date object representing UTC midnight\n * @throws Error if date string is invalid\n */\nexport const parseUTCDate = (date: string | Date): Date => {\n if (typeof date === 'string') {\n // If already an ISO string (contains 'T'), parse directly\n // Otherwise, append UTC time for simple date strings (YYYY-MM-DD)\n const dateStr = date.includes('T') ? date : `${date}T00:00:00Z`;\n const parsed = new Date(dateStr);\n if (isNaN(parsed.getTime())) {\n throw new Error(`Invalid date string: ${date}`);\n }\n return parsed;\n }\n return date;\n};\n\n/**\n * Get all days in the month of given date (UTC)\n * @param date - Reference date (any day in the target month)\n * @returns Array of Date objects for each day in the month\n */\nexport const getMonthDays = (date: Date | string): Date[] => {\n const utcDate = parseUTCDate(date);\n const year = utcDate.getUTCFullYear();\n const month = utcDate.getUTCMonth();\n\n // Get days in month (handles leap years)\n const daysInMonth = new Date(Date.UTC(year, month + 1, 0)).getUTCDate();\n\n const days: Date[] = [];\n for (let day = 1; day <= daysInMonth; day++) {\n days.push(new Date(Date.UTC(year, month, day)));\n }\n\n return days;\n};\n\n/**\n * Calculate day offset from month start (0-based)\n * @param date - The date to calculate offset for\n * @param monthStart - The start of the month as reference\n * @returns Number of days from month start (negative if date is before month start)\n */\nexport const getDayOffset = (date: Date, monthStart: Date): number => {\n const dateMs = Date.UTC(\n date.getUTCFullYear(),\n date.getUTCMonth(),\n date.getUTCDate()\n );\n const startMs = Date.UTC(\n monthStart.getUTCFullYear(),\n monthStart.getUTCMonth(),\n monthStart.getUTCDate()\n );\n return Math.round((dateMs - startMs) / (1000 * 60 * 60 * 24));\n};\n\n/**\n * Check if date is today (UTC comparison)\n * @param date - Date to check\n * @returns True if date is today, false otherwise\n */\nexport const isToday = (date: Date): boolean => {\n const now = new Date();\n const today = new Date(Date.UTC(\n now.getUTCFullYear(),\n now.getUTCMonth(),\n now.getUTCDate()\n ));\n const compareDate = new Date(Date.UTC(\n date.getUTCFullYear(),\n date.getUTCMonth(),\n date.getUTCDate()\n ));\n return today.getTime() === compareDate.getTime();\n};\n\n/**\n * Check if date is a weekend day (Saturday or Sunday)\n * @param date - Date to check\n * @returns True if date is Saturday (6) or Sunday (0), false otherwise\n */\nexport const isWeekend = (date: Date): boolean => {\n const day = date.getUTCDay();\n return day === 0 || day === 6; // Sunday (0) or Saturday (6)\n};\n\n/**\n * Calculate multi-month date range from task dates\n * Expands range to include full months (1st of first month to last day of last month)\n * @param tasks - Array of tasks with startDate and endDate\n * @returns Array of Date objects for all days in the expanded range\n */\nexport const getMultiMonthDays = (tasks: Array<{ startDate: string | Date; endDate: string | Date }>): Date[] => {\n // Handle empty task array by returning current month\n if (!tasks || tasks.length === 0) {\n return getMonthDays(new Date());\n }\n\n // Find min and max dates from all tasks\n let minDate: Date | null = null;\n let maxDate: Date | null = null;\n\n for (const task of tasks) {\n const start = parseUTCDate(task.startDate);\n const end = parseUTCDate(task.endDate);\n\n if (!minDate || start.getTime() < minDate.getTime()) {\n minDate = start;\n }\n if (!maxDate || end.getTime() > maxDate.getTime()) {\n maxDate = end;\n }\n }\n\n if (!minDate || !maxDate) {\n return getMonthDays(new Date());\n }\n\n // Extend to full months: 1st of first month to last day of last month\n const startOfMonth = new Date(Date.UTC(\n minDate.getUTCFullYear(),\n minDate.getUTCMonth(),\n 1\n ));\n\n const endOfMonth = new Date(Date.UTC(\n maxDate.getUTCFullYear(),\n maxDate.getUTCMonth() + 1,\n 0\n ));\n\n // Generate all dates in range\n const days: Date[] = [];\n const current = new Date(startOfMonth);\n\n while (current.getTime() <= endOfMonth.getTime()) {\n days.push(new Date(Date.UTC(\n current.getUTCFullYear(),\n current.getUTCMonth(),\n current.getUTCDate()\n )));\n // Move to next day\n current.setUTCDate(current.getUTCDate() + 1);\n }\n\n return days;\n};\n\n/**\n * Calculate month spans within a date range\n * @param dateRange - Array of Date objects representing the full range\n * @returns Array of month span objects with month, days count, and start index\n */\nexport const getMonthSpans = (\n dateRange: Date[]\n): Array<{ month: Date; days: number; startIndex: number }> => {\n if (dateRange.length === 0) {\n return [];\n }\n\n const spans: Array<{ month: Date; days: number; startIndex: number }> = [];\n let currentMonthYear = `${dateRange[0].getUTCFullYear()}-${dateRange[0].getUTCMonth()}`;\n let startOfMonthIndex = 0;\n\n for (let i = 0; i < dateRange.length; i++) {\n const date = dateRange[i];\n const monthYear = `${date.getUTCFullYear()}-${date.getUTCMonth()}`;\n\n // When month changes, finalize the previous span and start a new one\n if (monthYear !== currentMonthYear) {\n spans.push({\n month: new Date(Date.UTC(\n dateRange[startOfMonthIndex].getUTCFullYear(),\n dateRange[startOfMonthIndex].getUTCMonth(),\n 1\n )),\n days: i - startOfMonthIndex,\n startIndex: startOfMonthIndex\n });\n currentMonthYear = monthYear;\n startOfMonthIndex = i;\n }\n\n // Last date - finalize the last span\n if (i === dateRange.length - 1) {\n spans.push({\n month: new Date(Date.UTC(\n date.getUTCFullYear(),\n date.getUTCMonth(),\n 1\n )),\n days: i - startOfMonthIndex + 1,\n startIndex: startOfMonthIndex\n });\n }\n }\n\n return spans;\n};\n\n/**\n * Format date as DD.MM (e.g., 25.03 for March 25th)\n * @param date - Date to format\n * @returns Formatted date string in DD.MM format\n */\nexport const formatDateLabel = (date: Date | string): string => {\n const parsed = parseUTCDate(date);\n const day = String(parsed.getUTCDate()).padStart(2, '0');\n const month = String(parsed.getUTCMonth() + 1).padStart(2, '0');\n return `${day}.${month}`;\n};\n","import { Task, TaskDependency, LinkType, ValidationResult, DependencyError } from '../types';\n\n/**\n * Build adjacency list for dependency graph (task -> successors)\n */\nexport function buildAdjacencyList(tasks: Task[]): Map<string, string[]> {\n const taskMap = new Map(tasks.map(t => [t.id, t]));\n const graph = new Map<string, string[]>();\n\n for (const task of tasks) {\n const successors: string[] = [];\n\n // Find all tasks that depend on this task (this task is a predecessor)\n for (const otherTask of tasks) {\n if (otherTask.dependencies) {\n for (const dep of otherTask.dependencies) {\n if (dep.taskId === task.id) {\n successors.push(otherTask.id);\n break;\n }\n }\n }\n }\n\n graph.set(task.id, successors);\n }\n\n return graph;\n}\n\n/**\n * Detect circular dependencies using depth-first search\n */\nexport function detectCycles(tasks: Task[]): { hasCycle: boolean; cyclePath?: string[] } {\n const graph = buildAdjacencyList(tasks);\n const visiting = new Set<string>();\n const visited = new Set<string>();\n const path: string[] = [];\n\n function dfs(taskId: string): boolean {\n if (visiting.has(taskId)) {\n // Found cycle - current task is already in recursion stack\n return true;\n }\n if (visited.has(taskId)) {\n return false;\n }\n\n visiting.add(taskId);\n path.push(taskId);\n\n const successors = graph.get(taskId) || [];\n for (const successor of successors) {\n if (dfs(successor)) {\n return true;\n }\n }\n\n visiting.delete(taskId);\n path.pop();\n visited.add(taskId);\n return false;\n }\n\n for (const task of tasks) {\n if (dfs(task.id)) {\n return { hasCycle: true, cyclePath: [...path] };\n }\n }\n\n return { hasCycle: false };\n}\n\n/**\n * Calculate successor date based on predecessor dates, link type, and lag\n *\n * Link type semantics:\n * - FS: Successor start = Predecessor end + lag\n * - SS: Successor start = Predecessor start + lag\n * - FF: Successor end = Predecessor end + lag\n * - SF: Successor end = Predecessor start + lag\n */\nexport function calculateSuccessorDate(\n predecessorStart: Date,\n predecessorEnd: Date,\n linkType: LinkType,\n lag: number = 0\n): Date {\n // Base date: predecessor end for F* types, predecessor start for S* types\n const baseDate = linkType.startsWith('F') ? predecessorEnd : predecessorStart;\n\n // Apply lag (in days, converted to milliseconds)\n const lagMs = lag * 24 * 60 * 60 * 1000;\n const resultDate = new Date(baseDate.getTime() + lagMs);\n\n return resultDate;\n}\n\n/**\n * Validate all dependencies in the task list\n */\nexport function validateDependencies(tasks: Task[]): ValidationResult {\n const errors: DependencyError[] = [];\n const taskIds = new Set(tasks.map(t => t.id));\n\n // Check for missing predecessor references\n for (const task of tasks) {\n if (task.dependencies) {\n for (const dep of task.dependencies) {\n if (!taskIds.has(dep.taskId)) {\n errors.push({\n type: 'missing-task',\n taskId: task.id,\n message: `Dependency references non-existent task: ${dep.taskId}`,\n relatedTaskIds: [dep.taskId],\n });\n }\n }\n }\n }\n\n // Check for cycles\n const cycleResult = detectCycles(tasks);\n if (cycleResult.hasCycle && cycleResult.cyclePath) {\n errors.push({\n type: 'cycle',\n taskId: cycleResult.cyclePath[0],\n message: 'Circular dependency detected',\n relatedTaskIds: cycleResult.cyclePath,\n });\n }\n\n return {\n isValid: errors.length === 0,\n errors,\n };\n}\n\n/**\n * Get all FS successor tasks of a dragged task using BFS (FS edges only, Phase 7).\n *\n * Returns tasks in breadth-first order (direct successors first, then their successors).\n * The dragged task itself is NOT included in the returned array.\n *\n * The visited set prevents infinite loops in case of cycles (cycle detection already\n * prevents cycles in valid data, but the guard adds safety during cascade computation).\n */\nexport function getSuccessorChain(\n draggedTaskId: string,\n allTasks: Task[]\n): Task[] {\n // Build FS-only successor map: predecessor -> [successors]\n const successorMap = new Map<string, string[]>();\n for (const task of allTasks) {\n successorMap.set(task.id, []);\n }\n for (const task of allTasks) {\n if (!task.dependencies) continue;\n for (const dep of task.dependencies) {\n if (dep.type === 'FS') {\n const list = successorMap.get(dep.taskId) ?? [];\n list.push(task.id);\n successorMap.set(dep.taskId, list);\n }\n }\n }\n\n const taskById = new Map(allTasks.map(t => [t.id, t]));\n const visited = new Set<string>();\n const queue: string[] = [draggedTaskId];\n const chain: Task[] = [];\n visited.add(draggedTaskId); // seed — not added to chain\n\n while (queue.length > 0) {\n const current = queue.shift()!;\n const successors = successorMap.get(current) ?? [];\n for (const sid of successors) {\n if (!visited.has(sid)) {\n visited.add(sid);\n const t = taskById.get(sid);\n if (t) {\n chain.push(t);\n queue.push(sid);\n }\n }\n }\n }\n\n return chain; // excludes dragged task\n}\n\n/**\n * Get all dependency edges for rendering\n * Returns array of { predecessorId, successorId, type, lag }\n */\nexport function getAllDependencyEdges(tasks: Task[]): Array<{\n predecessorId: string;\n successorId: string;\n type: LinkType;\n lag: number;\n}> {\n const edges: Array<{ predecessorId: string; successorId: string; type: LinkType; lag: number }> = [];\n\n for (const task of tasks) {\n if (task.dependencies) {\n for (const dep of task.dependencies) {\n edges.push({\n predecessorId: dep.taskId,\n successorId: task.id,\n type: dep.type,\n lag: dep.lag ?? 0,\n });\n }\n }\n }\n\n return edges;\n}\n","'use client';\n\nimport React, { useMemo } from 'react';\nimport { format } from 'date-fns';\nimport { ru } from 'date-fns/locale';\nimport { getMonthSpans } from '../../utils/dateUtils';\nimport type { MonthSpan } from '../../types';\nimport './TimeScaleHeader.css';\n\nexport interface TimeScaleHeaderProps {\n /** Array of dates to display (from getMultiMonthDays) */\n days: Date[];\n /** Width of each day column in pixels */\n dayWidth: number;\n /** Height of the header row in pixels */\n headerHeight: number;\n}\n\n/**\n * TimeScaleHeader component - displays two-row date headers for the Gantt chart\n *\n * Top row: Month names (Russian, left-aligned) spanning multiple day columns\n * Bottom row: Day numbers (centered) in individual columns\n */\nconst TimeScaleHeader: React.FC<TimeScaleHeaderProps> = ({\n days,\n dayWidth,\n headerHeight,\n}) => {\n // Calculate month spans using the utility from dateUtils\n const monthSpans = useMemo(() => getMonthSpans(days), [days]);\n\n // Split header height evenly between two rows\n const rowHeight = headerHeight / 2;\n\n // Calculate grid template for day row\n const dayGridTemplate = useMemo(\n () => `repeat(${days.length}, ${dayWidth}px)`,\n [days.length, dayWidth]\n );\n\n return (\n <div\n className=\"gantt-tsh-header\"\n style={{ height: `${headerHeight}px` }}\n >\n {/* Month row - top */}\n <div\n className=\"gantt-tsh-monthRow\"\n style={{ height: `${rowHeight}px` }}\n >\n {monthSpans.map((span: MonthSpan, index: number) => (\n <div\n key={`month-${index}`}\n className=\"gantt-tsh-monthCell\"\n style={{ width: `${span.days * dayWidth}px` }}\n >\n {format(span.month, 'LLLL yyyy', { locale: ru }).replace(/^./, (c) => c.toUpperCase())}\n </div>\n ))}\n </div>\n\n {/* Day row - bottom */}\n <div\n className=\"gantt-tsh-dayRow\"\n style={{\n height: `${rowHeight}px`,\n gridTemplateColumns: dayGridTemplate,\n }}\n >\n {days.map((day, index) => {\n const isWeekend = day.getDay() === 0 || day.getDay() === 6;\n const prevDay = days[index - 1];\n const isMonthBoundary = index > 0 && prevDay && prevDay.getMonth() !== day.getMonth();\n // Use local date comparison for \"today\" (user's current date)\n const now = new Date();\n const isTodayDate =\n day.getUTCFullYear() === now.getFullYear() &&\n day.getUTCMonth() === now.getMonth() &&\n day.getUTCDate() === now.getDate();\n return (\n <div key={`day-${index}`} className={`gantt-tsh-dayCell ${isWeekend ? 'gantt-tsh-weekendDay' : ''} ${isMonthBoundary ? 'gantt-tsh-monthBoundary' : ''} ${isTodayDate ? 'gantt-tsh-today' : ''}`}>\n <span className=\"gantt-tsh-dayLabel\">{format(day, 'd')}</span>\n </div>\n );\n })}\n </div>\n </div>\n );\n};\n\nexport default TimeScaleHeader;\n","'use client';\n\nimport React, { useMemo } from 'react';\nimport { parseUTCDate, formatDateLabel } from '../../utils/dateUtils';\nimport { calculateTaskBar, pixelsToDate } from '../../utils/geometry';\nimport { useTaskDrag } from '../../hooks/useTaskDrag';\nimport type { Task } from '../GanttChart';\nimport './TaskRow.css';\n\nexport interface TaskRowProps {\n /** Task data to render */\n task: Task;\n /** Start of the month for positioning calculations */\n monthStart: Date;\n /** Width of each day column in pixels */\n dayWidth: number;\n /** Height of the task row in pixels */\n rowHeight: number;\n /** Callback when task is modified via drag/resize */\n onChange?: (updatedTask: Task) => void;\n /** Callback when task drag state changes (for rendering guide lines) */\n onDragStateChange?: (state: {\n isDragging: boolean;\n dragMode: 'move' | 'resize-left' | 'resize-right' | null;\n left: number;\n width: number;\n }) => void;\n /** Index of the task row (used for dependency rendering) */\n rowIndex?: number;\n /** All tasks in the chart (used for dependency validation) */\n allTasks?: Task[];\n /** Whether auto-scheduling is enabled */\n enableAutoSchedule?: boolean;\n /** Whether to disable constraint checking during drag */\n disableConstraints?: boolean;\n /** Position override for cascade preview — when set, overrides both static and drag position */\n overridePosition?: { left: number; width: number };\n /** Called each RAF during cascade drag with override positions for non-dragged chain tasks */\n onCascadeProgress?: (overrides: Map<string, { left: number; width: number }>) => void;\n /** Called when cascade drag completes; receives all shifted tasks including dragged task */\n onCascade?: (tasks: Task[]) => void;\n}\n\n/**\n * Custom comparison function for React.memo\n *\n * Performance optimization: Only re-renders if task properties that affect rendering change.\n *\n * NOTE: onChange is intentionally excluded from this comparison because:\n * 1. The parent (GanttChart) wraps onChange in useCallback for referential stability\n * 2. onChange is only called AFTER drag completes (not during drag)\n * 3. During drag, only the dragged TaskRow re-renders due to its internal drag state\n * 4. Other TaskRows don't need to re-render when one task is dragged\n *\n * NOTE: monthStart MUST be included because task positions are calculated relative to it.\n * When the grid expands (e.g., dragging a task left beyond the boundary), monthStart changes\n * and all tasks need to re-render to update their positions.\n *\n * NOTE: onCascadeProgress and onCascade are excluded from comparison (same pattern as onChange —\n * callbacks excluded from comparison because GanttChart wraps them in useCallback).\n *\n * Excluding onChange prevents re-render storms when dragging tasks with ~100 tasks.\n */\nconst arePropsEqual = (prevProps: TaskRowProps, nextProps: TaskRowProps) => {\n return (\n prevProps.task.id === nextProps.task.id &&\n prevProps.task.name === nextProps.task.name &&\n prevProps.task.startDate === nextProps.task.startDate &&\n prevProps.task.endDate === nextProps.task.endDate &&\n prevProps.task.color === nextProps.task.color &&\n prevProps.task.progress === nextProps.task.progress &&\n prevProps.task.accepted === nextProps.task.accepted &&\n prevProps.monthStart.getTime() === nextProps.monthStart.getTime() &&\n prevProps.dayWidth === nextProps.dayWidth &&\n prevProps.rowHeight === nextProps.rowHeight &&\n prevProps.overridePosition?.left === nextProps.overridePosition?.left &&\n prevProps.overridePosition?.width === nextProps.overridePosition?.width &&\n prevProps.allTasks === nextProps.allTasks &&\n prevProps.disableConstraints === nextProps.disableConstraints\n // onChange, onCascadeProgress, onCascade excluded - see note above\n );\n};\n\n/**\n * TaskRow component - renders a single task row with a task bar\n *\n * Uses React.memo for performance optimization (QL-01).\n * The task bar is positioned absolutely based on start/end dates.\n */\nconst TaskRow: React.FC<TaskRowProps> = React.memo(\n ({ task, monthStart, dayWidth, rowHeight, onChange, onDragStateChange, rowIndex, allTasks, enableAutoSchedule, disableConstraints, overridePosition, onCascadeProgress, onCascade }) => {\n // Parse dates as UTC\n const taskStartDate = useMemo(() => parseUTCDate(task.startDate), [task.startDate]);\n const taskEndDate = useMemo(() => parseUTCDate(task.endDate), [task.endDate]);\n\n // Calculate task bar position and dimensions\n const { left, width } = useMemo(\n () => calculateTaskBar(taskStartDate, taskEndDate, monthStart, dayWidth),\n [taskStartDate, taskEndDate, monthStart, dayWidth]\n );\n\n // Determine task bar color\n const barColor = task.color || 'var(--gantt-task-bar-default-color)';\n\n // Calculate clamped and rounded progress width\n const progressWidth = useMemo(() => {\n if (task.progress === undefined || task.progress <= 0) return 0;\n return Math.min(100, Math.max(0, Math.round(task.progress)));\n }, [task.progress]);\n\n // Determine progress color based on completion status\n const progressColor = useMemo(() => {\n if (progressWidth === 100) {\n return task.accepted\n ? 'var(--gantt-progress-accepted, #22c55e)' // Green for accepted\n : 'var(--gantt-progress-completed, #fbbf24)'; // Yellow for completed\n }\n // Darker semi-transparent shade using color-mix() or fallback\n return task.color\n ? `color-mix(in srgb, ${task.color} 40%, black)`\n : 'var(--gantt-progress-color, rgba(0, 0, 0, 0.2))';\n }, [progressWidth, task.accepted, task.color]);\n\n // Handle drag end - call onChange with updated task\n const handleDragEnd = (result: { id: string; startDate: Date; endDate: Date; updatedDependencies?: Task['dependencies'] }) => {\n const updatedTask: Task = {\n ...task,\n startDate: result.startDate.toISOString(),\n endDate: result.endDate.toISOString(),\n ...(result.updatedDependencies !== undefined && { dependencies: result.updatedDependencies }),\n };\n onChange?.(updatedTask);\n };\n\n // Use drag hook for interactive drag/resize\n const {\n isDragging,\n dragMode,\n currentLeft,\n currentWidth,\n dragHandleProps,\n } = useTaskDrag({\n taskId: task.id,\n initialStartDate: taskStartDate,\n initialEndDate: taskEndDate,\n monthStart,\n dayWidth,\n onDragEnd: handleDragEnd,\n onDragStateChange,\n edgeZoneWidth: 20,\n allTasks,\n rowIndex,\n enableAutoSchedule,\n disableConstraints,\n onCascadeProgress,\n onCascade,\n });\n\n // Use override position (for cascade preview) with fallback to drag or static position\n const displayLeft = overridePosition?.left ?? (isDragging ? currentLeft : left);\n const displayWidth = overridePosition?.width ?? (isDragging ? currentWidth : width);\n\n // Format date labels for display - update in real-time during drag\n const currentStartDate = isDragging\n ? pixelsToDate(displayLeft, monthStart, dayWidth)\n : taskStartDate;\n const currentEndDate = isDragging\n ? pixelsToDate(displayLeft + displayWidth - dayWidth, monthStart, dayWidth)\n : taskEndDate;\n\n const startDateLabel = formatDateLabel(currentStartDate);\n const endDateLabel = formatDateLabel(currentEndDate);\n\n // Calculate duration in days\n const durationDays = Math.round(\n (currentEndDate.getTime() - currentStartDate.getTime()) / (1000 * 60 * 60 * 24)\n ) + 1;\n\n return (\n <div\n className=\"gantt-tr-row\"\n style={{ height: `${rowHeight}px` }}\n >\n <div className=\"gantt-tr-taskContainer\">\n <div\n data-taskbar\n className={`gantt-tr-taskBar ${isDragging ? 'gantt-tr-dragging' : ''}`}\n style={{\n left: `${displayLeft}px`,\n width: `${displayWidth}px`,\n backgroundColor: barColor,\n height: 'var(--gantt-task-bar-height)',\n cursor: dragHandleProps.style.cursor,\n userSelect: dragHandleProps.style.userSelect,\n }}\n onMouseDown={dragHandleProps.onMouseDown}\n >\n {progressWidth > 0 && (\n <div\n className=\"gantt-tr-progressBar\"\n style={{\n width: `${progressWidth}%`,\n backgroundColor: progressColor,\n }}\n />\n )}\n <div className=\"gantt-tr-resizeHandle gantt-tr-resizeHandleLeft\" />\n <span className=\"gantt-tr-taskDuration\">\n {durationDays} д\n </span>\n <div className=\"gantt-tr-resizeHandle gantt-tr-resizeHandleRight\" />\n </div>\n <div\n className=\"gantt-tr-leftLabels\"\n style={{\n left: `${displayLeft}px`\n }}\n >\n <span className=\"gantt-tr-dateLabel gantt-tr-dateLabelLeft\">\n {startDateLabel}–{endDateLabel}\n </span>\n </div>\n <div\n className=\"gantt-tr-rightLabels\"\n style={{\n left: `${displayLeft + displayWidth}px`,\n }}\n >\n <span className=\"gantt-tr-externalTaskName\">\n {task.name}\n </span>\n </div>\n </div>\n </div>\n );\n },\n arePropsEqual\n);\n\nTaskRow.displayName = 'TaskRow';\n\nexport default TaskRow;\n","/**\n * Calculate day difference in UTC\n */\nconst getUTCDayDifference = (date1: Date, date2: Date): number => {\n const ms1 = Date.UTC(\n date1.getUTCFullYear(),\n date1.getUTCMonth(),\n date1.getUTCDate()\n );\n const ms2 = Date.UTC(\n date2.getUTCFullYear(),\n date2.getUTCMonth(),\n date2.getUTCDate()\n );\n return Math.round((ms1 - ms2) / (1000 * 60 * 60 * 24));\n};\n\n/**\n * Calculate task bar positioning and dimensions\n * @param taskStartDate - Start date of the task\n * @param taskEndDate - End date of the task\n * @param monthStart - Start of the month/visible range\n * @param dayWidth - Width of each day in pixels\n * @returns Object with left position and width in pixels\n */\nexport const calculateTaskBar = (\n taskStartDate: Date,\n taskEndDate: Date,\n monthStart: Date,\n dayWidth: number\n): { left: number; width: number } => {\n const startOffset = getUTCDayDifference(taskStartDate, monthStart);\n const duration = getUTCDayDifference(taskEndDate, taskStartDate);\n\n // Round to avoid sub-pixel rendering issues\n const left = Math.round(startOffset * dayWidth);\n const width = Math.round((duration + 1) * dayWidth); // +1 to include end date\n\n return { left, width };\n};\n\n/**\n * Convert pixel position to date (inverse of calculateTaskBar)\n * @param pixels - Position in pixels (left or width)\n * @param monthStart - Start of the month/visible range\n * @param dayWidth - Width of each day in pixels\n * @returns Date calculated from pixel position\n */\nexport const pixelsToDate = (pixels: number, monthStart: Date, dayWidth: number): Date => {\n const days = Math.round(pixels / dayWidth);\n return new Date(Date.UTC(\n monthStart.getUTCFullYear(),\n monthStart.getUTCMonth(),\n monthStart.getUTCDate() + days\n ));\n};\n\n/**\n * Calculate total width for month grid\n * @param daysInMonth - Number of days in the month\n * @param dayWidth - Width of each day in pixels\n * @returns Total grid width in pixels\n */\nexport const calculateGridWidth = (daysInMonth: number, dayWidth: number): number => {\n return Math.round(daysInMonth * dayWidth);\n};\n\n/**\n * Detect which edge zone the cursor is in on a task bar\n * @param clientX - Mouse X coordinate relative to viewport\n * @param taskBarElement - The task bar DOM element\n * @param edgeZoneWidth - Width of edge zones in pixels (default: 12px)\n * @returns 'left' if in left edge, 'right' if in right edge, 'move' if in middle\n */\nexport const detectEdgeZone = (\n clientX: number,\n taskBarElement: HTMLElement,\n edgeZoneWidth: number = 12\n): 'left' | 'right' | 'move' => {\n const rect = taskBarElement.getBoundingClientRect();\n const relativeX = Math.round(clientX - rect.left);\n\n // Check left edge zone\n if (relativeX >= 0 && relativeX <= edgeZoneWidth) {\n return 'left';\n }\n\n // Check right edge zone\n const width = Math.round(rect.width);\n if (relativeX >= width - edgeZoneWidth && relativeX <= width) {\n return 'right';\n }\n\n // Middle area - move mode\n return 'move';\n};\n\n/**\n * Get appropriate cursor style for drag position\n * @param position - The drag position (left edge, right edge, or move)\n * @returns CSS cursor string for the position\n */\nexport const getCursorForPosition = (position: 'left' | 'right' | 'move'): string => {\n switch (position) {\n case 'left':\n case 'right':\n return 'ew-resize';\n case 'move':\n return 'grab';\n default:\n return 'default';\n }\n};\n\n/**\n * Calculate grid line positions for a date range\n * @param dateRange - Array of Date objects representing the visible range\n * @param dayWidth - Width of each day column in pixels\n * @returns Array of grid line objects with x position and flags\n */\nexport const calculateGridLines = (\n dateRange: Date[],\n dayWidth: number\n): Array<{ x: number; isMonthStart: boolean; isWeekStart: boolean }> => {\n const lines: Array<{ x: number; isMonthStart: boolean; isWeekStart: boolean }> = [];\n\n for (let i = 0; i < dateRange.length; i++) {\n const date = dateRange[i];\n const x = Math.round(i * dayWidth);\n const isMonthStart = date.getUTCDate() === 1;\n const isWeekStart = date.getUTCDay() === 1; // Monday\n\n lines.push({ x, isMonthStart, isWeekStart });\n }\n\n // Add final line at the end of the range\n if (dateRange.length > 0) {\n lines.push({\n x: Math.round(dateRange.length * dayWidth),\n isMonthStart: false,\n isWeekStart: false\n });\n }\n\n return lines;\n};\n\n/**\n * Calculate weekend background blocks for a date range\n * @param dateRange - Array of Date objects representing the visible range\n * @param dayWidth - Width of each day column in pixels\n * @returns Array of weekend block objects with left position and width\n */\nexport const calculateWeekendBlocks = (\n dateRange: Date[],\n dayWidth: number\n): Array<{ left: number; width: number }> => {\n const blocks: Array<{ left: number; width: number }> = [];\n let inWeekend = false;\n let weekendStartIndex = -1;\n\n for (let i = 0; i < dateRange.length; i++) {\n const date = dateRange[i];\n const dayOfWeek = date.getUTCDay();\n const isWeekend = dayOfWeek === 0 || dayOfWeek === 6; // Sunday or Saturday\n\n if (isWeekend && !inWeekend) {\n // Start of a weekend block\n inWeekend = true;\n weekendStartIndex = i;\n } else if (!isWeekend && inWeekend) {\n // End of a weekend block\n inWeekend = false;\n const left = Math.round(weekendStartIndex * dayWidth);\n const width = Math.round((i - weekendStartIndex) * dayWidth);\n blocks.push({ left, width });\n }\n }\n\n // Handle case where range ends on a weekend\n if (inWeekend && weekendStartIndex >= 0) {\n const left = Math.round(weekendStartIndex * dayWidth);\n const width = Math.round((dateRange.length - weekendStartIndex) * dayWidth);\n blocks.push({ left, width });\n }\n\n return blocks;\n};\n\n/**\n * Calculate SVG cubic Bezier curve path for dependency lines\n * @param from - Start point {x, y} (right edge of predecessor)\n * @param to - End point {x, y} (left edge of successor)\n * @returns SVG path string for cubic Bezier curve\n */\nexport const calculateBezierPath = (\n from: { x: number; y: number },\n to: { x: number; y: number }\n): string => {\n // Control points create smooth vertical curve\n // Offset is proportional to vertical distance for natural-looking curves\n const verticalDistance = Math.abs(to.y - from.y);\n const cpOffset = Math.max(verticalDistance * 0.5, 20); // Minimum 20px for same-row connections\n\n // For same-row connections, use arc above the task bars\n if (from.y === to.y) {\n const arcHeight = 20;\n const midX = (from.x + to.x) / 2;\n return `M ${from.x} ${from.y} Q ${midX} ${from.y - arcHeight} ${to.x} ${to.y}`;\n }\n\n // Standard cubic Bezier for multi-row connections\n const cp1x = from.x;\n const cp1y = from.y + (to.y > from.y ? cpOffset : -cpOffset);\n const cp2x = to.x;\n const cp2y = to.y - (to.y > from.y ? cpOffset : -cpOffset);\n\n return `M ${Math.round(from.x)} ${Math.round(from.y)} C ${Math.round(cp1x)} ${Math.round(cp1y)}, ${Math.round(cp2x)} ${Math.round(cp2y)}, ${Math.round(to.x)} ${Math.round(to.y)}`;\n};\n\n/**\n * Calculate SVG Г-shaped (L-shaped) path for FS dependency lines.\n * Goes vertically from the right edge of the predecessor bar, then horizontally\n * to the left edge of the successor bar. Supports negative lag (overlap).\n * @param from - Start point {x, y} (right edge of predecessor, vertical center)\n * @param to - End point {x, y} (left edge of successor, vertical center)\n * @returns SVG path string\n */\nexport const calculateOrthogonalPath = (\n from: { x: number; y: number },\n to: { x: number; y: number }\n): string => {\n const fx = Math.round(from.x);\n const fy = Math.round(from.y);\n const tx = Math.round(to.x);\n const ty = Math.round(to.y);\n\n // Same row: straight horizontal line\n if (fy === ty) {\n return `M ${fx} ${fy} H ${tx}`;\n }\n\n const C = 2; // chamfer size\n const goingDown = ty > fy;\n const goingRight = tx >= fx;\n const dirY = goingDown ? 1 : -1;\n const dirX = goingRight ? 1 : -1;\n\n // Shape: horizontal exit → chamfer → vertical arrival (arrow points down/up)\n if (Math.abs(ty - fy) >= C && Math.abs(tx - fx) >= C) {\n return [\n `M ${fx} ${fy}`,\n `H ${tx - dirX * C}`,\n `L ${tx} ${fy + dirY * C}`,\n `V ${ty}`,\n ].join(' ');\n }\n\n // Sharp corner fallback (very short segments)\n return `M ${fx} ${fy} H ${tx} V ${ty}`;\n};\n","'use client';\n\nimport { useEffect, useRef, useState, useCallback } from 'react';\nimport { detectEdgeZone } from '../utils/geometry';\nimport type { Task, TaskDependency } from '../types';\nimport { calculateSuccessorDate, getSuccessorChain } from '../utils/dependencyUtils';\n\n/**\n * Global drag manager that persists across HMR\n *\n * This singleton manages active drag operations at the module level,\n * ensuring that drag state survives React Fast Refresh (HMR).\n *\n * The key insight: When HMR occurs during a drag operation:\n * 1. The component unmounts and its useEffect cleanup removes window listeners\n * 2. The component remounts with fresh refs (isDraggingRef = false)\n * 3. But the user is still holding the mouse button!\n * 4. Without module-level state, the drag operation is orphaned\n *\n * Solution: Store active drag state in module-level singleton and\n * use a global cleanup effect to always handle mouseup/mousemove.\n */\ninterface ActiveDragState {\n taskId: string;\n mode: 'move' | 'resize-left' | 'resize-right';\n startX: number;\n initialLeft: number;\n initialWidth: number;\n currentLeft: number;\n currentWidth: number;\n dayWidth: number;\n monthStart: Date;\n onProgress: (left: number, width: number) => void;\n onComplete: (finalLeft: number, finalWidth: number) => void;\n onCancel: () => void;\n allTasks: Task[];\n disableConstraints?: boolean;\n cascadeChain: Task[]; // FS successors of dragged task (Phase 7)\n onCascadeProgress?: (overrides: Map<string, { left: number; width: number }>) => void;\n}\n\nlet globalActiveDrag: ActiveDragState | null = null;\nlet globalRafId: number | null = null;\n\n/**\n * Complete the active drag operation\n */\nfunction completeDrag() {\n if (globalRafId !== null) {\n cancelAnimationFrame(globalRafId);\n globalRafId = null;\n }\n\n if (globalActiveDrag) {\n // Clear cascade overrides before completing (avoids stale preview positions)\n globalActiveDrag.onCascadeProgress?.(new Map());\n const { onComplete, currentLeft, currentWidth } = globalActiveDrag;\n globalActiveDrag = null;\n onComplete(currentLeft, currentWidth);\n }\n}\n\n/**\n * Cancel the active drag operation\n */\nfunction cancelDrag() {\n if (globalRafId !== null) {\n cancelAnimationFrame(globalRafId);\n globalRafId = null;\n }\n\n if (globalActiveDrag) {\n const { onCancel } = globalActiveDrag;\n globalActiveDrag = null;\n onCancel();\n }\n}\n\n/**\n * Snap pixel value to grid (day boundaries)\n */\nfunction snapToGrid(pixels: number, dayWidth: number): number {\n return Math.round(pixels / dayWidth) * dayWidth;\n}\n\n/**\n * Check if a task move would violate dependency constraints\n * Only blocks move operations, not resize (per requirements)\n */\nfunction canMoveTask(\n task: Task,\n newStartDate: Date,\n newEndDate: Date,\n allTasks: Task[]\n): { allowed: boolean; reason?: string } {\n if (!task.dependencies || task.dependencies.length === 0) {\n return { allowed: true };\n }\n\n // For each predecessor, check if the new position respects the constraint\n for (const dep of task.dependencies) {\n const predecessor = allTasks.find(t => t.id === dep.taskId);\n if (!predecessor) continue;\n\n const predecessorStart = new Date(predecessor.startDate);\n const predecessorEnd = new Date(predecessor.endDate);\n\n // Calculate expected date based on link type\n const expectedDate = calculateSuccessorDate(\n predecessorStart,\n predecessorEnd,\n dep.type,\n dep.lag ?? 0\n );\n\n // Check constraint based on link type\n const targetIsStart = dep.type.endsWith('S');\n const targetDate = targetIsStart ? newStartDate : newEndDate;\n\n // Allow move if target date is on or after expected date\n // (give 1-day tolerance for rounding)\n const dayDiff = (targetDate.getTime() - expectedDate.getTime()) / (24 * 60 * 60 * 1000);\n\n if (dayDiff < -1) {\n return {\n allowed: false,\n reason: `Would violate ${dep.type} dependency from \"${predecessor.name}\"`\n };\n }\n }\n\n return { allowed: true };\n}\n\n/**\n * Recalculate lag for all FS dependencies linking predecessor(s) to this task.\n * Used in soft mode (disableConstraints=true) to update lag on drag complete.\n * Returns updated TaskDependency array with new lag values.\n */\nfunction recalculateIncomingLags(\n task: Task,\n newStartDate: Date,\n allTasks: Task[]\n): NonNullable<Task['dependencies']> {\n if (!task.dependencies) return [];\n const taskById = new Map(allTasks.map(t => [t.id, t]));\n\n return task.dependencies.map(dep => {\n if (dep.type !== 'FS') return dep;\n const predecessor = taskById.get(dep.taskId);\n if (!predecessor) return dep;\n const predEnd = new Date(predecessor.endDate as string);\n const lagMs = Date.UTC(\n newStartDate.getUTCFullYear(),\n newStartDate.getUTCMonth(),\n newStartDate.getUTCDate()\n ) - Date.UTC(\n predEnd.getUTCFullYear(),\n predEnd.getUTCMonth(),\n predEnd.getUTCDate()\n );\n const lagDays = Math.round(lagMs / (24 * 60 * 60 * 1000));\n return { ...dep, lag: lagDays };\n });\n}\n\n/**\n * Global mouse move handler - attached once and persists across HMR\n */\nfunction handleGlobalMouseMove(e: MouseEvent) {\n if (!globalActiveDrag || globalRafId !== null) {\n return;\n }\n\n globalRafId = requestAnimationFrame(() => {\n if (!globalActiveDrag) {\n globalRafId = null;\n return;\n }\n\n const { startX, initialLeft, initialWidth, mode, dayWidth, onProgress, allTasks } = globalActiveDrag;\n const deltaX = e.clientX - startX;\n\n let newLeft = initialLeft;\n let newWidth = initialWidth;\n\n switch (mode) {\n case 'move':\n newLeft = snapToGrid(initialLeft + deltaX, dayWidth);\n break;\n case 'resize-left':\n const snappedLeft = snapToGrid(initialLeft + deltaX, dayWidth);\n newLeft = snappedLeft;\n const rightEdge = initialLeft + initialWidth;\n newWidth = Math.max(dayWidth, rightEdge - snappedLeft);\n break;\n case 'resize-right':\n const snappedWidth = snapToGrid(initialWidth + deltaX, dayWidth);\n newWidth = Math.max(dayWidth, snappedWidth);\n break;\n }\n\n // Hard mode: check left-move boundary against predecessor.startDate (Phase 7)\n // Child can move left until its startDate would go before predecessor.startDate\n if (mode === 'move' && allTasks.length > 0 && !globalActiveDrag.disableConstraints) {\n const currentTask = allTasks.find(t => t.id === globalActiveDrag?.taskId);\n if (currentTask && currentTask.dependencies && currentTask.dependencies.length > 0) {\n let minAllowedLeft = 0; // in pixels from monthStart\n for (const dep of currentTask.dependencies) {\n if (dep.type !== 'FS') continue; // Phase 7: FS only\n const predecessor = globalActiveDrag.allTasks.find(t => t.id === dep.taskId);\n if (!predecessor) continue;\n // Boundary: child.startDate >= predecessor.startDate (allows negative lag)\n const predStart = new Date(predecessor.startDate as string);\n const predStartOffset = Math.round(\n (Date.UTC(predStart.getUTCFullYear(), predStart.getUTCMonth(), predStart.getUTCDate()) -\n Date.UTC(\n globalActiveDrag.monthStart.getUTCFullYear(),\n globalActiveDrag.monthStart.getUTCMonth(),\n globalActiveDrag.monthStart.getUTCDate()\n )) / (24 * 60 * 60 * 1000)\n );\n const predStartLeft = Math.round(predStartOffset * globalActiveDrag.dayWidth);\n minAllowedLeft = Math.max(minAllowedLeft, predStartLeft);\n }\n // Clamp: don't let task go left of boundary\n newLeft = Math.max(minAllowedLeft, newLeft);\n }\n }\n\n // Hard mode cascade: emit position overrides for all FS successor chain members\n if ((mode === 'move' || mode === 'resize-right') && !globalActiveDrag.disableConstraints &&\n globalActiveDrag.cascadeChain.length > 0 && globalActiveDrag.onCascadeProgress) {\n // For move: delta is based on how far the start (left) has shifted.\n // For resize-right: start is fixed; delta is based on how much the width (end date) changed.\n const deltaDays = mode === 'resize-right'\n ? Math.round((newWidth - globalActiveDrag.initialWidth) / globalActiveDrag.dayWidth)\n : Math.round((newLeft - globalActiveDrag.initialLeft) / globalActiveDrag.dayWidth);\n const overrides = new Map<string, { left: number; width: number }>();\n for (const chainTask of globalActiveDrag.cascadeChain) {\n const chainStart = new Date(chainTask.startDate as string);\n const chainEnd = new Date(chainTask.endDate as string);\n const chainStartOffset = Math.round(\n (Date.UTC(chainStart.getUTCFullYear(), chainStart.getUTCMonth(), chainStart.getUTCDate()) -\n Date.UTC(\n globalActiveDrag.monthStart.getUTCFullYear(),\n globalActiveDrag.monthStart.getUTCMonth(),\n globalActiveDrag.monthStart.getUTCDate()\n )) / (24 * 60 * 60 * 1000)\n );\n const chainDuration = Math.round(\n (Date.UTC(chainEnd.getUTCFullYear(), chainEnd.getUTCMonth(), chainEnd.getUTCDate()) -\n Date.UTC(chainStart.getUTCFullYear(), chainStart.getUTCMonth(), chainStart.getUTCDate())\n ) / (24 * 60 * 60 * 1000)\n );\n const chainLeft = Math.round((chainStartOffset + deltaDays) * globalActiveDrag.dayWidth);\n const chainWidth = Math.round((chainDuration + 1) * globalActiveDrag.dayWidth); // +1 inclusive\n overrides.set(chainTask.id, { left: chainLeft, width: chainWidth });\n }\n globalActiveDrag.onCascadeProgress(overrides);\n }\n\n // Update current values in global state for completion\n globalActiveDrag.currentLeft = newLeft;\n globalActiveDrag.currentWidth = newWidth;\n\n onProgress(newLeft, newWidth);\n globalRafId = null;\n });\n}\n\n/**\n * Global mouse up handler - attached once and persists across HMR\n */\nfunction handleGlobalMouseUp() {\n if (globalActiveDrag) {\n completeDrag();\n }\n}\n\n/**\n * Track whether global listeners are attached\n */\nlet globalListenersAttached = false;\n\n/**\n * Ensure global listeners are attached (idempotent)\n */\nfunction ensureGlobalListeners() {\n if (!globalListenersAttached) {\n window.addEventListener('mousemove', handleGlobalMouseMove);\n window.addEventListener('mouseup', handleGlobalMouseUp);\n globalListenersAttached = true;\n }\n}\n\n/**\n * Cleanup global listeners - called when no components are using drag\n * Note: In practice with HMR, we keep these attached for safety\n */\nfunction cleanupGlobalListeners() {\n // We keep global listeners attached to handle orphaned drags after HMR\n // They will be cleaned up when the page is refreshed\n}\n\n/**\n * Options for useTaskDrag hook\n */\nexport interface UseTaskDragOptions {\n /** Unique identifier for the task */\n taskId: string;\n /** Initial start date of the task */\n initialStartDate: Date;\n /** Initial end date of the task */\n initialEndDate: Date;\n /** Start of the visible range (e.g., month start) */\n monthStart: Date;\n /** Width of each day in pixels */\n dayWidth: number;\n /** Callback when drag operation completes */\n onDragEnd?: (result: { id: string; startDate: Date; endDate: Date; updatedDependencies?: Task['dependencies'] }) => void;\n /** Callback for drag state changes (for parent components to render guide lines) */\n onDragStateChange?: (state: {\n isDragging: boolean;\n dragMode: 'move' | 'resize-left' | 'resize-right' | null;\n left: number;\n width: number;\n }) => void;\n /** Width of edge zones for resize detection (default: 12px) */\n edgeZoneWidth?: number;\n /** Array of all tasks for dependency validation */\n allTasks?: Task[];\n /** Row index of this task (for task lookup) */\n rowIndex?: number;\n /** Enable automatic scheduling of dependent tasks */\n enableAutoSchedule?: boolean;\n /** When true, dependency constraint checking is skipped during drag (default: false) */\n disableConstraints?: boolean;\n /** Callback for real-time cascade preview — called each RAF with non-dragged chain member positions */\n onCascadeProgress?: (overrides: Map<string, { left: number; width: number }>) => void;\n /** Callback when cascade completes — receives all shifted tasks including dragged task */\n onCascade?: (tasks: Task[]) => void;\n}\n\n/**\n * Return value from useTaskDrag hook\n */\nexport interface UseTaskDragReturn {\n /** Whether a drag operation is in progress */\n isDragging: boolean;\n /** Current drag mode (null when not dragging) */\n dragMode: 'move' | 'resize-left' | 'resize-right' | null;\n /** Current left position in pixels (updated during drag) */\n currentLeft: number;\n /** Current width in pixels (updated during drag) */\n currentWidth: number;\n /** Props to spread on the drag handle element */\n dragHandleProps: {\n onMouseDown: (e: React.MouseEvent) => void;\n style: React.CSSProperties;\n };\n}\n\n/**\n * Custom hook for managing task drag interactions\n *\n * HMR-SAFE: Uses module-level singleton to ensure drag state survives\n * React Fast Refresh. Window event listeners are attached once at module\n * level rather than per component instance.\n */\nexport const useTaskDrag = (options: UseTaskDragOptions): UseTaskDragReturn => {\n const {\n taskId,\n initialStartDate,\n initialEndDate,\n monthStart,\n dayWidth,\n onDragEnd,\n onDragStateChange,\n edgeZoneWidth = 12,\n allTasks = [],\n rowIndex,\n enableAutoSchedule = false,\n disableConstraints = false,\n onCascadeProgress,\n onCascade,\n } = options;\n\n // Track if this hook instance owns the current global drag\n const isOwnerRef = useRef<boolean>(false);\n\n // Display state (triggers re-renders only when needed)\n const [isDragging, setIsDragging] = useState<boolean>(false);\n const [dragMode, setDragMode] = useState<'move' | 'resize-left' | 'resize-right' | null>(null);\n const [currentLeft, setCurrentLeft] = useState<number>(0);\n const [currentWidth, setCurrentWidth] = useState<number>(0);\n\n /**\n * Calculate initial pixel position from dates\n */\n const getInitialPosition = useCallback((): { left: number; width: number } => {\n const getUTCDayDifference = (date1: Date, date2: Date): number => {\n const ms1 = Date.UTC(\n date1.getUTCFullYear(),\n date1.getUTCMonth(),\n date1.getUTCDate()\n );\n const ms2 = Date.UTC(\n date2.getUTCFullYear(),\n date2.getUTCMonth(),\n date2.getUTCDate()\n );\n return Math.round((ms1 - ms2) / (1000 * 60 * 60 * 24));\n };\n\n const startOffset = getUTCDayDifference(initialStartDate, monthStart);\n const duration = getUTCDayDifference(initialEndDate, initialStartDate);\n\n const left = Math.round(startOffset * dayWidth);\n const width = Math.round((duration + 1) * dayWidth); // +1 to include end date\n\n return { left, width };\n }, [initialStartDate, initialEndDate, monthStart, dayWidth]);\n\n /**\n * Initialize position when dates or dayWidth changes\n */\n useEffect(() => {\n const { left, width } = getInitialPosition();\n setCurrentLeft(left);\n setCurrentWidth(width);\n }, [getInitialPosition]);\n\n /**\n * Handle drag progress callback from global manager\n */\n const handleProgress = useCallback((left: number, width: number) => {\n setCurrentLeft(left);\n setCurrentWidth(width);\n\n if (onDragStateChange && isOwnerRef.current) {\n const mode = globalActiveDrag?.mode || null;\n onDragStateChange({\n isDragging: true,\n dragMode: mode,\n left,\n width,\n });\n }\n }, [onDragStateChange]);\n\n /**\n * Handle drag completion from global manager\n */\n const handleComplete = useCallback((finalLeft: number, finalWidth: number) => {\n const wasOwner = isOwnerRef.current;\n isOwnerRef.current = false;\n\n // Calculate new dates from final pixel values\n const dayOffset = Math.round(finalLeft / dayWidth);\n const durationDays = Math.round(finalWidth / dayWidth) - 1; // -1 because width includes end date\n\n const newStartDate = new Date(Date.UTC(\n monthStart.getUTCFullYear(),\n monthStart.getUTCMonth(),\n monthStart.getUTCDate() + dayOffset\n ));\n\n const newEndDate = new Date(Date.UTC(\n monthStart.getUTCFullYear(),\n monthStart.getUTCMonth(),\n monthStart.getUTCDate() + dayOffset + durationDays\n ));\n\n // Reset local state\n setIsDragging(false);\n setDragMode(null);\n\n // Notify parent of drag end\n if (onDragStateChange) {\n onDragStateChange({\n isDragging: false,\n dragMode: null,\n left: finalLeft,\n width: finalWidth,\n });\n }\n\n if (wasOwner) {\n if (!disableConstraints && onCascade && allTasks.length > 0) {\n // Hard mode with onCascade: compute cascade and call onCascade\n const chain = getSuccessorChain(taskId, allTasks);\n if (chain.length > 0) {\n // Compute delta from end-date change — correct for both move (where\n // end shifts by the same amount as start) and resize-right (where only\n // end changes while start stays fixed).\n const origEndMs = Date.UTC(\n initialEndDate.getUTCFullYear(),\n initialEndDate.getUTCMonth(),\n initialEndDate.getUTCDate()\n );\n const newEndMs = Date.UTC(\n newEndDate.getUTCFullYear(),\n newEndDate.getUTCMonth(),\n newEndDate.getUTCDate()\n );\n const deltaDays = Math.round((newEndMs - origEndMs) / (24 * 60 * 60 * 1000));\n\n const draggedTaskData = allTasks.find(t => t.id === taskId);\n const cascadedTasks: Task[] = [\n {\n ...(draggedTaskData ?? { id: taskId, name: '', startDate: '', endDate: '' }),\n startDate: newStartDate.toISOString(),\n endDate: newEndDate.toISOString(),\n ...(draggedTaskData?.dependencies && {\n dependencies: recalculateIncomingLags(draggedTaskData, newStartDate, allTasks),\n }),\n },\n ...chain.map(chainTask => {\n const origStart = new Date(chainTask.startDate as string);\n const origEnd = new Date(chainTask.endDate as string);\n const newStart = new Date(Date.UTC(\n origStart.getUTCFullYear(), origStart.getUTCMonth(), origStart.getUTCDate() + deltaDays\n ));\n const newEnd = new Date(Date.UTC(\n origEnd.getUTCFullYear(), origEnd.getUTCMonth(), origEnd.getUTCDate() + deltaDays\n ));\n return { ...chainTask, startDate: newStart.toISOString(), endDate: newEnd.toISOString() };\n }),\n ];\n onCascade(cascadedTasks);\n return; // Don't call onDragEnd — cascade covers the dragged task too\n }\n }\n\n // Soft mode OR hard mode with no FS successors: call onDragEnd\n // Always recalculate lag so hard-mode drags (chain.length===0) also persist the new lag\n if (allTasks.length > 0 && onDragEnd) {\n const currentTaskData = allTasks.find(t => t.id === taskId);\n const updatedDependencies = currentTaskData?.dependencies\n ? recalculateIncomingLags(currentTaskData, newStartDate, allTasks)\n : undefined;\n onDragEnd({ id: taskId, startDate: newStartDate, endDate: newEndDate, updatedDependencies });\n } else if (onDragEnd) {\n onDragEnd({ id: taskId, startDate: newStartDate, endDate: newEndDate });\n }\n }\n }, [dayWidth, monthStart, onDragEnd, onDragStateChange, taskId, disableConstraints, onCascade, allTasks, initialStartDate, initialEndDate]);\n\n /**\n * Handle drag cancellation (e.g., if HMR orphaned the drag)\n */\n const handleCancel = useCallback(() => {\n isOwnerRef.current = false;\n setIsDragging(false);\n setDragMode(null);\n\n if (onDragStateChange) {\n onDragStateChange({\n isDragging: false,\n dragMode: null,\n left: currentLeft,\n width: currentWidth,\n });\n }\n }, [onDragStateChange, currentLeft, currentWidth]);\n\n /**\n * Cleanup on unmount - if this instance owns the drag, cancel it\n */\n useEffect(() => {\n return () => {\n if (isOwnerRef.current && globalActiveDrag) {\n // We're unmounting while owning the drag - cancel it\n cancelDrag();\n }\n };\n }, []);\n\n /**\n * Handle mouse down on drag handle\n */\n const handleMouseDown = useCallback((e: React.MouseEvent) => {\n const target = e.currentTarget as HTMLElement;\n const edgeZone = detectEdgeZone(e.clientX, target, edgeZoneWidth);\n\n // Determine drag mode from edge zone\n let mode: 'move' | 'resize-left' | 'resize-right' | null = null;\n switch (edgeZone) {\n case 'left':\n mode = 'resize-left';\n break;\n case 'right':\n mode = 'resize-right';\n break;\n case 'move':\n mode = 'move';\n break;\n }\n\n if (!mode) {\n return;\n }\n\n // Get current position from state (this is what we see on screen)\n const initialLeft = currentLeft;\n const initialWidth = currentWidth;\n\n // Mark this instance as the drag owner\n isOwnerRef.current = true;\n\n // Update display state\n setIsDragging(true);\n setDragMode(mode);\n\n // Notify parent of drag start\n if (onDragStateChange) {\n onDragStateChange({\n isDragging: true,\n dragMode: mode,\n left: initialLeft,\n width: initialWidth,\n });\n }\n\n // Ensure global listeners are attached (idempotent)\n ensureGlobalListeners();\n\n // Store drag state in global singleton\n globalActiveDrag = {\n taskId,\n mode,\n startX: e.clientX,\n initialLeft,\n initialWidth,\n currentLeft: initialLeft, // Initially same as initial\n currentWidth: initialWidth, // Initially same as initial\n dayWidth,\n monthStart,\n onProgress: handleProgress,\n onComplete: handleComplete,\n onCancel: handleCancel,\n allTasks,\n disableConstraints,\n cascadeChain: (mode === 'move' || mode === 'resize-right') && !disableConstraints\n ? getSuccessorChain(taskId, allTasks)\n : [],\n onCascadeProgress,\n };\n }, [edgeZoneWidth, currentLeft, currentWidth, dayWidth, monthStart, taskId, onDragStateChange, handleProgress, handleComplete, handleCancel, allTasks, disableConstraints, onCascadeProgress, onCascade]);\n\n /**\n * Get cursor style based on current position\n */\n const getCursorStyle = useCallback((): string => {\n if (isDragging) {\n return 'grabbing';\n }\n return 'grab';\n }, [isDragging]);\n\n return {\n isDragging,\n dragMode,\n currentLeft,\n currentWidth,\n dragHandleProps: {\n onMouseDown: handleMouseDown,\n style: {\n cursor: getCursorStyle(),\n userSelect: 'none',\n } as React.CSSProperties,\n },\n };\n};\n","'use client';\n\nimport React, { useMemo } from 'react';\nimport { getDayOffset, isToday } from '../../utils/dateUtils';\nimport './TodayIndicator.css';\n\nexport interface TodayIndicatorProps {\n /** Start of the month for positioning calculations */\n monthStart: Date;\n /** Width of each day column in pixels */\n dayWidth: number;\n}\n\n/**\n * TodayIndicator component - displays a vertical line at the current date\n *\n * Only renders when the current date is within the visible month range.\n * Satisfies REND-04 requirement for visual today indicator.\n */\nconst TodayIndicator: React.FC<TodayIndicatorProps> = ({ monthStart, dayWidth }) => {\n // Use local date for \"today\" (not UTC) - user's current date matters\n const today = new Date();\n const todayLocal = new Date(Date.UTC(\n today.getFullYear(),\n today.getMonth(),\n today.getDate()\n ));\n\n // Check if today is within the current month (UTC comparison with local today)\n const isInMonth = useMemo(() => {\n return (\n todayLocal.getUTCFullYear() === monthStart.getUTCFullYear() &&\n todayLocal.getUTCMonth() === monthStart.getUTCMonth()\n );\n }, [monthStart, todayLocal]);\n\n // Calculate position if today is in the month\n const position = useMemo(() => {\n if (!isInMonth) return null;\n\n const offset = getDayOffset(todayLocal, monthStart);\n return Math.round(offset * dayWidth);\n }, [isInMonth, monthStart, dayWidth, todayLocal]);\n\n if (!isInMonth || position === null) {\n return null;\n }\n\n return (\n <div\n className=\"gantt-ti-indicator\"\n style={{\n left: `${position}px`,\n width: 'var(--gantt-today-indicator-width)',\n backgroundColor: 'var(--gantt-today-indicator-color)',\n }}\n aria-label=\"Today\"\n />\n );\n};\n\nexport default TodayIndicator;\n","'use client';\n\nimport React, { useMemo } from 'react';\nimport { calculateGridLines, calculateWeekendBlocks } from '../../utils/geometry';\nimport type { GridLine } from '../../types';\nimport './GridBackground.css';\n\nexport interface GridBackgroundProps {\n /** Array of dates to display (from getMultiMonthDays) */\n dateRange: Date[];\n /** Width of each day column in pixels */\n dayWidth: number;\n /** Total height of the grid area in pixels */\n totalHeight: number;\n}\n\n/**\n * Custom comparison function for React.memo\n *\n * Performance optimization: Only re-renders if dateRange or dayWidth change.\n * totalHeight is excluded because it only affects container height, not grid calculations.\n */\nconst arePropsEqual = (prevProps: GridBackgroundProps, nextProps: GridBackgroundProps) => {\n return (\n prevProps.dayWidth === nextProps.dayWidth &&\n prevProps.dateRange.length === nextProps.dateRange.length &&\n prevProps.totalHeight !== nextProps.totalHeight // totalHeight changes still trigger update\n );\n};\n\n/**\n * GridBackground component - renders vertical grid lines and weekend background highlighting\n *\n * This component provides the visual grid structure that runs behind task rows.\n * It separates grid rendering from task rendering for better performance and cleaner code.\n *\n * Features:\n * - Vertical grid lines at month/week/day boundaries\n * - Pink background highlighting for weekend days\n * - React.memo optimization for performance\n * - Pointer events disabled (clicks pass through to tasks)\n */\nconst GridBackground: React.FC<GridBackgroundProps> = React.memo(\n ({ dateRange, dayWidth, totalHeight }) => {\n // Calculate grid line positions\n const gridLines = useMemo<GridLine[]>(() => {\n return calculateGridLines(dateRange, dayWidth);\n }, [dateRange, dayWidth]);\n\n // Calculate weekend background blocks\n const weekendBlocks = useMemo(() => {\n return calculateWeekendBlocks(dateRange, dayWidth);\n }, [dateRange, dayWidth]);\n\n // Calculate total grid width\n const gridWidth = useMemo(() => {\n return Math.round(dateRange.length * dayWidth);\n }, [dateRange.length, dayWidth]);\n\n return (\n <div\n className=\"gantt-gb-gridBackground\"\n style={{\n width: `${gridWidth}px`,\n height: `${totalHeight}px`,\n }}\n >\n {/* Weekend backgrounds (rendered first, behind lines) */}\n {weekendBlocks.map((block, index) => (\n <div\n key={`weekend-${index}`}\n className=\"gantt-gb-weekendBlock\"\n style={{\n left: `${block.left}px`,\n width: `${block.width}px`,\n }}\n />\n ))}\n\n {/* Vertical grid lines */}\n {gridLines.map((line, index) => {\n // Determine line type class based on flags\n const lineClass = line.isMonthStart\n ? 'gantt-gb-monthSeparator'\n : line.isWeekStart\n ? 'gantt-gb-weekSeparator'\n : 'gantt-gb-dayLine';\n\n return (\n <div\n key={`gridline-${index}`}\n className={`gantt-gb-gridLine ${lineClass}`}\n style={{\n left: `${line.x}px`,\n }}\n />\n );\n })}\n </div>\n );\n },\n arePropsEqual\n);\n\nGridBackground.displayName = 'GridBackground';\n\nexport default GridBackground;\n","'use client';\n\nimport React from 'react';\nimport './DragGuideLines.css';\n\nexport interface DragGuideLinesProps {\n isDragging: boolean;\n dragMode: 'move' | 'resize-left' | 'resize-right' | null;\n left: number;\n width: number;\n totalHeight: number;\n}\n\nconst DragGuideLines: React.FC<DragGuideLinesProps> = ({\n isDragging,\n dragMode,\n left,\n width,\n totalHeight,\n}) => {\n if (!isDragging || !dragMode) {\n return null;\n }\n\n // Determine which lines to show based on drag mode\n const showLeftLine = dragMode === 'move' || dragMode === 'resize-left';\n const showRightLine = dragMode === 'move' || dragMode === 'resize-right';\n\n return (\n <>\n {showLeftLine && (\n <div\n className=\"gantt-dgl-guideLine\"\n style={{\n left: `${left}px`,\n height: `${totalHeight}px`,\n }}\n />\n )}\n {showRightLine && (\n <div\n className=\"gantt-dgl-guideLine\"\n style={{\n left: `${left + width}px`,\n height: `${totalHeight}px`,\n }}\n />\n )}\n </>\n );\n};\n\nexport default DragGuideLines;\n","'use client';\n\nimport React, { useMemo } from 'react';\nimport { Task } from '../../types';\nimport { calculateTaskBar } from '../../utils/geometry';\nimport { calculateOrthogonalPath } from '../../utils/geometry';\nimport { getAllDependencyEdges, detectCycles } from '../../utils/dependencyUtils';\nimport './DependencyLines.css';\n\nexport interface DependencyLinesProps {\n /** All tasks in the chart */\n tasks: Task[];\n /** Start of the visible range (e.g., month start) */\n monthStart: Date;\n /** Width of each day column in pixels */\n dayWidth: number;\n /** Height of each task row in pixels */\n rowHeight: number;\n /** Total width of the grid in pixels */\n gridWidth: number;\n /** Real-time pixel overrides for task positions during drag (taskId -> {left, width}) */\n dragOverrides?: Map<string, { left: number; width: number }>;\n}\n\n/**\n * SVG overlay component rendering dependency lines as orthogonal paths with rounded corners\n *\n * Lines connect from the right edge of predecessor tasks to the left edge\n * of successor tasks using horizontal/vertical lines with arc rounded corners.\n * Circular dependencies are highlighted in red.\n *\n * Performance: Uses React.memo to prevent re-renders when dependencies haven't changed.\n */\nexport const DependencyLines: React.FC<DependencyLinesProps> = React.memo(({\n tasks,\n monthStart,\n dayWidth,\n rowHeight,\n gridWidth,\n dragOverrides,\n}) => {\n // Create a lookup map for task positions and their indices\n const { taskPositions, taskIndices } = useMemo(() => {\n const positions = new Map<string, { left: number; right: number; rowTop: number }>();\n const indices = new Map<string, number>();\n\n tasks.forEach((task, index) => {\n const startDate = new Date(task.startDate);\n const endDate = new Date(task.endDate);\n const computed = calculateTaskBar(startDate, endDate, monthStart, dayWidth);\n\n // Use real-time pixel override if available (during drag)\n const override = dragOverrides?.get(task.id);\n const resolvedLeft = override?.left ?? computed.left;\n const resolvedWidth = override?.width ?? computed.width;\n\n indices.set(task.id, index);\n positions.set(task.id, {\n left: resolvedLeft + 10,\n right: resolvedLeft + resolvedWidth,\n rowTop: index * rowHeight,\n });\n });\n\n return { taskPositions: positions, taskIndices: indices };\n }, [tasks, monthStart, dayWidth, rowHeight, dragOverrides]);\n\n // Detect cycles for highlighting\n const cycleInfo = useMemo(() => {\n const result = detectCycles(tasks);\n const cycleTaskIds = new Set(result.cyclePath || []);\n return cycleTaskIds;\n }, [tasks]);\n\n // Calculate all dependency line paths\n const lines = useMemo(() => {\n const edges = getAllDependencyEdges(tasks);\n const lines: Array<{\n id: string;\n path: string;\n hasCycle: boolean;\n }> = [];\n\n for (const edge of edges) {\n const predecessor = taskPositions.get(edge.predecessorId);\n const successor = taskPositions.get(edge.successorId);\n const predecessorIndex = taskIndices.get(edge.predecessorId);\n const successorIndex = taskIndices.get(edge.successorId);\n\n if (!predecessor || !successor || predecessorIndex === undefined || successorIndex === undefined) {\n continue; // Skip if task not found (shouldn't happen with validation)\n }\n\n // Determine if tasks are in reverse order (predecessor appears below successor)\n const reverseOrder = predecessorIndex > successorIndex;\n\n // Calculate direction-specific Y coordinates\n let fromY: number;\n let toY: number;\n\n if (reverseOrder) {\n // Arrow goes UP: exit from top of parent bar, enter at bottom of child bar\n fromY = predecessor.rowTop + 10; // 8px from top of parent bar\n toY = successor.rowTop + rowHeight - 6; // 8px from bottom of child bar\n } else {\n // Arrow goes DOWN: exit from bottom of parent bar, enter at top of child bar\n fromY = predecessor.rowTop + rowHeight - 10; // 8px from bottom of parent bar\n toY = successor.rowTop + 6; // 8px from top of child bar\n }\n\n const from = { x: predecessor.right, y: fromY };\n const to = { x: successor.left, y: toY };\n\n const path = calculateOrthogonalPath(from, to);\n\n // Check if this edge is part of a cycle\n const hasCycle = cycleInfo.has(edge.predecessorId) || cycleInfo.has(edge.successorId);\n\n lines.push({\n id: `${edge.predecessorId}-${edge.successorId}`,\n path,\n hasCycle,\n });\n }\n\n return lines;\n }, [tasks, taskPositions, taskIndices, cycleInfo]);\n\n return (\n <svg\n className=\"gantt-dependencies-svg\"\n width={gridWidth}\n height={tasks.length * rowHeight}\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <defs>\n {/* Arrow marker for dependency lines */}\n <marker\n id=\"arrowhead\"\n markerWidth=\"8\"\n markerHeight=\"6\"\n markerUnits=\"userSpaceOnUse\"\n refX=\"7\"\n refY=\"3\"\n orient=\"auto\"\n >\n <polygon\n points=\"0 0, 8 3, 0 6\"\n fill=\"var(--gantt-dependency-line-color, #666666)\"\n />\n </marker>\n\n {/* Red arrow marker for circular dependencies */}\n <marker\n id=\"arrowhead-cycle\"\n markerWidth=\"8\"\n markerHeight=\"6\"\n markerUnits=\"userSpaceOnUse\"\n refX=\"7\"\n refY=\"3\"\n orient=\"auto\"\n >\n <polygon\n points=\"0 0, 8 3, 0 6\"\n fill=\"var(--gantt-dependency-cycle-color, #ef4444)\"\n />\n </marker>\n </defs>\n\n {lines.map(({ id, path, hasCycle }) => (\n <path\n key={id}\n d={path}\n className={hasCycle ? 'gantt-dependency-path gantt-dependency-cycle' : 'gantt-dependency-path'}\n markerEnd={hasCycle ? 'url(#arrowhead-cycle)' : 'url(#arrowhead)'}\n />\n ))}\n </svg>\n );\n});\n\nDependencyLines.displayName = 'DependencyLines';\n\nexport default DependencyLines;\n"],"mappings":";;;AAEA,SAAgB,WAAAA,UAAS,eAAAC,cAAa,UAAAC,SAAQ,YAAAC,WAAU,aAAAC,kBAAiB;;;ACMlE,IAAM,eAAe,CAAC,SAA8B;AACzD,MAAI,OAAO,SAAS,UAAU;AAG5B,UAAM,UAAU,KAAK,SAAS,GAAG,IAAI,OAAO,GAAG,IAAI;AACnD,UAAM,SAAS,IAAI,KAAK,OAAO;AAC/B,QAAI,MAAM,OAAO,QAAQ,CAAC,GAAG;AAC3B,YAAM,IAAI,MAAM,wBAAwB,IAAI,EAAE;AAAA,IAChD;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAOO,IAAM,eAAe,CAAC,SAAgC;AAC3D,QAAM,UAAU,aAAa,IAAI;AACjC,QAAM,OAAO,QAAQ,eAAe;AACpC,QAAM,QAAQ,QAAQ,YAAY;AAGlC,QAAM,cAAc,IAAI,KAAK,KAAK,IAAI,MAAM,QAAQ,GAAG,CAAC,CAAC,EAAE,WAAW;AAEtE,QAAM,OAAe,CAAC;AACtB,WAAS,MAAM,GAAG,OAAO,aAAa,OAAO;AAC3C,SAAK,KAAK,IAAI,KAAK,KAAK,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;AAAA,EAChD;AAEA,SAAO;AACT;AAQO,IAAM,eAAe,CAAC,MAAY,eAA6B;AACpE,QAAM,SAAS,KAAK;AAAA,IAClB,KAAK,eAAe;AAAA,IACpB,KAAK,YAAY;AAAA,IACjB,KAAK,WAAW;AAAA,EAClB;AACA,QAAM,UAAU,KAAK;AAAA,IACnB,WAAW,eAAe;AAAA,IAC1B,WAAW,YAAY;AAAA,IACvB,WAAW,WAAW;AAAA,EACxB;AACA,SAAO,KAAK,OAAO,SAAS,YAAY,MAAO,KAAK,KAAK,GAAG;AAC9D;AAOO,IAAM,UAAU,CAAC,SAAwB;AAC9C,QAAM,MAAM,oBAAI,KAAK;AACrB,QAAM,QAAQ,IAAI,KAAK,KAAK;AAAA,IAC1B,IAAI,eAAe;AAAA,IACnB,IAAI,YAAY;AAAA,IAChB,IAAI,WAAW;AAAA,EACjB,CAAC;AACD,QAAM,cAAc,IAAI,KAAK,KAAK;AAAA,IAChC,KAAK,eAAe;AAAA,IACpB,KAAK,YAAY;AAAA,IACjB,KAAK,WAAW;AAAA,EAClB,CAAC;AACD,SAAO,MAAM,QAAQ,MAAM,YAAY,QAAQ;AACjD;AAOO,IAAM,YAAY,CAAC,SAAwB;AAChD,QAAM,MAAM,KAAK,UAAU;AAC3B,SAAO,QAAQ,KAAK,QAAQ;AAC9B;AAQO,IAAM,oBAAoB,CAAC,UAA+E;AAE/G,MAAI,CAAC,SAAS,MAAM,WAAW,GAAG;AAChC,WAAO,aAAa,oBAAI,KAAK,CAAC;AAAA,EAChC;AAGA,MAAI,UAAuB;AAC3B,MAAI,UAAuB;AAE3B,aAAW,QAAQ,OAAO;AACxB,UAAM,QAAQ,aAAa,KAAK,SAAS;AACzC,UAAM,MAAM,aAAa,KAAK,OAAO;AAErC,QAAI,CAAC,WAAW,MAAM,QAAQ,IAAI,QAAQ,QAAQ,GAAG;AACnD,gBAAU;AAAA,IACZ;AACA,QAAI,CAAC,WAAW,IAAI,QAAQ,IAAI,QAAQ,QAAQ,GAAG;AACjD,gBAAU;AAAA,IACZ;AAAA,EACF;AAEA,MAAI,CAAC,WAAW,CAAC,SAAS;AACxB,WAAO,aAAa,oBAAI,KAAK,CAAC;AAAA,EAChC;AAGA,QAAM,eAAe,IAAI,KAAK,KAAK;AAAA,IACjC,QAAQ,eAAe;AAAA,IACvB,QAAQ,YAAY;AAAA,IACpB;AAAA,EACF,CAAC;AAED,QAAM,aAAa,IAAI,KAAK,KAAK;AAAA,IAC/B,QAAQ,eAAe;AAAA,IACvB,QAAQ,YAAY,IAAI;AAAA,IACxB;AAAA,EACF,CAAC;AAGD,QAAM,OAAe,CAAC;AACtB,QAAM,UAAU,IAAI,KAAK,YAAY;AAErC,SAAO,QAAQ,QAAQ,KAAK,WAAW,QAAQ,GAAG;AAChD,SAAK,KAAK,IAAI,KAAK,KAAK;AAAA,MACtB,QAAQ,eAAe;AAAA,MACvB,QAAQ,YAAY;AAAA,MACpB,QAAQ,WAAW;AAAA,IACrB,CAAC,CAAC;AAEF,YAAQ,WAAW,QAAQ,WAAW,IAAI,CAAC;AAAA,EAC7C;AAEA,SAAO;AACT;AAOO,IAAM,gBAAgB,CAC3B,cAC6D;AAC7D,MAAI,UAAU,WAAW,GAAG;AAC1B,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,QAAkE,CAAC;AACzE,MAAI,mBAAmB,GAAG,UAAU,CAAC,EAAE,eAAe,CAAC,IAAI,UAAU,CAAC,EAAE,YAAY,CAAC;AACrF,MAAI,oBAAoB;AAExB,WAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AACzC,UAAM,OAAO,UAAU,CAAC;AACxB,UAAM,YAAY,GAAG,KAAK,eAAe,CAAC,IAAI,KAAK,YAAY,CAAC;AAGhE,QAAI,cAAc,kBAAkB;AAClC,YAAM,KAAK;AAAA,QACT,OAAO,IAAI,KAAK,KAAK;AAAA,UACnB,UAAU,iBAAiB,EAAE,eAAe;AAAA,UAC5C,UAAU,iBAAiB,EAAE,YAAY;AAAA,UACzC;AAAA,QACF,CAAC;AAAA,QACD,MAAM,IAAI;AAAA,QACV,YAAY;AAAA,MACd,CAAC;AACD,yBAAmB;AACnB,0BAAoB;AAAA,IACtB;AAGA,QAAI,MAAM,UAAU,SAAS,GAAG;AAC9B,YAAM,KAAK;AAAA,QACT,OAAO,IAAI,KAAK,KAAK;AAAA,UACnB,KAAK,eAAe;AAAA,UACpB,KAAK,YAAY;AAAA,UACjB;AAAA,QACF,CAAC;AAAA,QACD,MAAM,IAAI,oBAAoB;AAAA,QAC9B,YAAY;AAAA,MACd,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AAOO,IAAM,kBAAkB,CAAC,SAAgC;AAC9D,QAAM,SAAS,aAAa,IAAI;AAChC,QAAM,MAAM,OAAO,OAAO,WAAW,CAAC,EAAE,SAAS,GAAG,GAAG;AACvD,QAAM,QAAQ,OAAO,OAAO,YAAY,IAAI,CAAC,EAAE,SAAS,GAAG,GAAG;AAC9D,SAAO,GAAG,GAAG,IAAI,KAAK;AACxB;;;ACpNO,SAAS,mBAAmB,OAAsC;AACvE,QAAM,UAAU,IAAI,IAAI,MAAM,IAAI,OAAK,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AACjD,QAAM,QAAQ,oBAAI,IAAsB;AAExC,aAAW,QAAQ,OAAO;AACxB,UAAM,aAAuB,CAAC;AAG9B,eAAW,aAAa,OAAO;AAC7B,UAAI,UAAU,cAAc;AAC1B,mBAAW,OAAO,UAAU,cAAc;AACxC,cAAI,IAAI,WAAW,KAAK,IAAI;AAC1B,uBAAW,KAAK,UAAU,EAAE;AAC5B;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,UAAM,IAAI,KAAK,IAAI,UAAU;AAAA,EAC/B;AAEA,SAAO;AACT;AAKO,SAAS,aAAa,OAA4D;AACvF,QAAM,QAAQ,mBAAmB,KAAK;AACtC,QAAM,WAAW,oBAAI,IAAY;AACjC,QAAM,UAAU,oBAAI,IAAY;AAChC,QAAM,OAAiB,CAAC;AAExB,WAAS,IAAI,QAAyB;AACpC,QAAI,SAAS,IAAI,MAAM,GAAG;AAExB,aAAO;AAAA,IACT;AACA,QAAI,QAAQ,IAAI,MAAM,GAAG;AACvB,aAAO;AAAA,IACT;AAEA,aAAS,IAAI,MAAM;AACnB,SAAK,KAAK,MAAM;AAEhB,UAAM,aAAa,MAAM,IAAI,MAAM,KAAK,CAAC;AACzC,eAAW,aAAa,YAAY;AAClC,UAAI,IAAI,SAAS,GAAG;AAClB,eAAO;AAAA,MACT;AAAA,IACF;AAEA,aAAS,OAAO,MAAM;AACtB,SAAK,IAAI;AACT,YAAQ,IAAI,MAAM;AAClB,WAAO;AAAA,EACT;AAEA,aAAW,QAAQ,OAAO;AACxB,QAAI,IAAI,KAAK,EAAE,GAAG;AAChB,aAAO,EAAE,UAAU,MAAM,WAAW,CAAC,GAAG,IAAI,EAAE;AAAA,IAChD;AAAA,EACF;AAEA,SAAO,EAAE,UAAU,MAAM;AAC3B;AAWO,SAAS,uBACd,kBACA,gBACA,UACA,MAAc,GACR;AAEN,QAAM,WAAW,SAAS,WAAW,GAAG,IAAI,iBAAiB;AAG7D,QAAM,QAAQ,MAAM,KAAK,KAAK,KAAK;AACnC,QAAM,aAAa,IAAI,KAAK,SAAS,QAAQ,IAAI,KAAK;AAEtD,SAAO;AACT;AAKO,SAAS,qBAAqB,OAAiC;AACpE,QAAM,SAA4B,CAAC;AACnC,QAAM,UAAU,IAAI,IAAI,MAAM,IAAI,OAAK,EAAE,EAAE,CAAC;AAG5C,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,cAAc;AACrB,iBAAW,OAAO,KAAK,cAAc;AACnC,YAAI,CAAC,QAAQ,IAAI,IAAI,MAAM,GAAG;AAC5B,iBAAO,KAAK;AAAA,YACV,MAAM;AAAA,YACN,QAAQ,KAAK;AAAA,YACb,SAAS,4CAA4C,IAAI,MAAM;AAAA,YAC/D,gBAAgB,CAAC,IAAI,MAAM;AAAA,UAC7B,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,QAAM,cAAc,aAAa,KAAK;AACtC,MAAI,YAAY,YAAY,YAAY,WAAW;AACjD,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,QAAQ,YAAY,UAAU,CAAC;AAAA,MAC/B,SAAS;AAAA,MACT,gBAAgB,YAAY;AAAA,IAC9B,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL,SAAS,OAAO,WAAW;AAAA,IAC3B;AAAA,EACF;AACF;AAWO,SAAS,kBACd,eACA,UACQ;AAER,QAAM,eAAe,oBAAI,IAAsB;AAC/C,aAAW,QAAQ,UAAU;AAC3B,iBAAa,IAAI,KAAK,IAAI,CAAC,CAAC;AAAA,EAC9B;AACA,aAAW,QAAQ,UAAU;AAC3B,QAAI,CAAC,KAAK,aAAc;AACxB,eAAW,OAAO,KAAK,cAAc;AACnC,UAAI,IAAI,SAAS,MAAM;AACrB,cAAM,OAAO,aAAa,IAAI,IAAI,MAAM,KAAK,CAAC;AAC9C,aAAK,KAAK,KAAK,EAAE;AACjB,qBAAa,IAAI,IAAI,QAAQ,IAAI;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AAEA,QAAM,WAAW,IAAI,IAAI,SAAS,IAAI,OAAK,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AACrD,QAAM,UAAU,oBAAI,IAAY;AAChC,QAAM,QAAkB,CAAC,aAAa;AACtC,QAAM,QAAgB,CAAC;AACvB,UAAQ,IAAI,aAAa;AAEzB,SAAO,MAAM,SAAS,GAAG;AACvB,UAAM,UAAU,MAAM,MAAM;AAC5B,UAAM,aAAa,aAAa,IAAI,OAAO,KAAK,CAAC;AACjD,eAAW,OAAO,YAAY;AAC5B,UAAI,CAAC,QAAQ,IAAI,GAAG,GAAG;AACrB,gBAAQ,IAAI,GAAG;AACf,cAAM,IAAI,SAAS,IAAI,GAAG;AAC1B,YAAI,GAAG;AACL,gBAAM,KAAK,CAAC;AACZ,gBAAM,KAAK,GAAG;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAMO,SAAS,sBAAsB,OAKnC;AACD,QAAM,QAA4F,CAAC;AAEnG,aAAW,QAAQ,OAAO;AACxB,QAAI,KAAK,cAAc;AACrB,iBAAW,OAAO,KAAK,cAAc;AACnC,cAAM,KAAK;AAAA,UACT,eAAe,IAAI;AAAA,UACnB,aAAa,KAAK;AAAA,UAClB,MAAM,IAAI;AAAA,UACV,KAAK,IAAI,OAAO;AAAA,QAClB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;;;ACvNA,SAAgB,eAAe;AAC/B,SAAS,cAAc;AACvB,SAAS,UAAU;AAsCf,SAUM,KAVN;AAlBJ,IAAM,kBAAkD,CAAC;AAAA,EACvD;AAAA,EACA;AAAA,EACA;AACF,MAAM;AAEJ,QAAM,aAAa,QAAQ,MAAM,cAAc,IAAI,GAAG,CAAC,IAAI,CAAC;AAG5D,QAAM,YAAY,eAAe;AAGjC,QAAM,kBAAkB;AAAA,IACtB,MAAM,UAAU,KAAK,MAAM,KAAK,QAAQ;AAAA,IACxC,CAAC,KAAK,QAAQ,QAAQ;AAAA,EACxB;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,OAAO,EAAE,QAAQ,GAAG,YAAY,KAAK;AAAA,MAGrC;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,OAAO,EAAE,QAAQ,GAAG,SAAS,KAAK;AAAA,YAEjC,qBAAW,IAAI,CAAC,MAAiB,UAChC;AAAA,cAAC;AAAA;AAAA,gBAEC,WAAU;AAAA,gBACV,OAAO,EAAE,OAAO,GAAG,KAAK,OAAO,QAAQ,KAAK;AAAA,gBAE3C,iBAAO,KAAK,OAAO,aAAa,EAAE,QAAQ,GAAG,CAAC,EAAE,QAAQ,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC;AAAA;AAAA,cAJhF,SAAS,KAAK;AAAA,YAKrB,CACD;AAAA;AAAA,QACH;AAAA,QAGA;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,OAAO;AAAA,cACL,QAAQ,GAAG,SAAS;AAAA,cACpB,qBAAqB;AAAA,YACvB;AAAA,YAEC,eAAK,IAAI,CAAC,KAAK,UAAU;AACxB,oBAAMC,aAAY,IAAI,OAAO,MAAM,KAAK,IAAI,OAAO,MAAM;AACzD,oBAAM,UAAU,KAAK,QAAQ,CAAC;AAC9B,oBAAM,kBAAkB,QAAQ,KAAK,WAAW,QAAQ,SAAS,MAAM,IAAI,SAAS;AAEpF,oBAAM,MAAM,oBAAI,KAAK;AACrB,oBAAM,cACJ,IAAI,eAAe,MAAM,IAAI,YAAY,KACzC,IAAI,YAAY,MAAM,IAAI,SAAS,KACnC,IAAI,WAAW,MAAM,IAAI,QAAQ;AACnC,qBACE,oBAAC,SAAyB,WAAW,qBAAqBA,aAAY,yBAAyB,EAAE,IAAI,kBAAkB,4BAA4B,EAAE,IAAI,cAAc,oBAAoB,EAAE,IAC3L,8BAAC,UAAK,WAAU,sBAAsB,iBAAO,KAAK,GAAG,GAAE,KAD/C,OAAO,KAAK,EAEtB;AAAA,YAEJ,CAAC;AAAA;AAAA,QACH;AAAA;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,0BAAQ;;;ACzFf,OAAOC,UAAS,WAAAC,gBAAe;;;ACC/B,IAAM,sBAAsB,CAAC,OAAa,UAAwB;AAChE,QAAM,MAAM,KAAK;AAAA,IACf,MAAM,eAAe;AAAA,IACrB,MAAM,YAAY;AAAA,IAClB,MAAM,WAAW;AAAA,EACnB;AACA,QAAM,MAAM,KAAK;AAAA,IACf,MAAM,eAAe;AAAA,IACrB,MAAM,YAAY;AAAA,IAClB,MAAM,WAAW;AAAA,EACnB;AACA,SAAO,KAAK,OAAO,MAAM,QAAQ,MAAO,KAAK,KAAK,GAAG;AACvD;AAUO,IAAM,mBAAmB,CAC9B,eACA,aACA,YACA,aACoC;AACpC,QAAM,cAAc,oBAAoB,eAAe,UAAU;AACjE,QAAM,WAAW,oBAAoB,aAAa,aAAa;AAG/D,QAAM,OAAO,KAAK,MAAM,cAAc,QAAQ;AAC9C,QAAM,QAAQ,KAAK,OAAO,WAAW,KAAK,QAAQ;AAElD,SAAO,EAAE,MAAM,MAAM;AACvB;AASO,IAAM,eAAe,CAAC,QAAgB,YAAkB,aAA2B;AACxF,QAAM,OAAO,KAAK,MAAM,SAAS,QAAQ;AACzC,SAAO,IAAI,KAAK,KAAK;AAAA,IACnB,WAAW,eAAe;AAAA,IAC1B,WAAW,YAAY;AAAA,IACvB,WAAW,WAAW,IAAI;AAAA,EAC5B,CAAC;AACH;AAQO,IAAM,qBAAqB,CAAC,aAAqB,aAA6B;AACnF,SAAO,KAAK,MAAM,cAAc,QAAQ;AAC1C;AASO,IAAM,iBAAiB,CAC5B,SACA,gBACA,gBAAwB,OACM;AAC9B,QAAM,OAAO,eAAe,sBAAsB;AAClD,QAAM,YAAY,KAAK,MAAM,UAAU,KAAK,IAAI;AAGhD,MAAI,aAAa,KAAK,aAAa,eAAe;AAChD,WAAO;AAAA,EACT;AAGA,QAAM,QAAQ,KAAK,MAAM,KAAK,KAAK;AACnC,MAAI,aAAa,QAAQ,iBAAiB,aAAa,OAAO;AAC5D,WAAO;AAAA,EACT;AAGA,SAAO;AACT;AAOO,IAAM,uBAAuB,CAAC,aAAgD;AACnF,UAAQ,UAAU;AAAA,IAChB,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;AAQO,IAAM,qBAAqB,CAChC,WACA,aACsE;AACtE,QAAM,QAA2E,CAAC;AAElF,WAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AACzC,UAAM,OAAO,UAAU,CAAC;AACxB,UAAM,IAAI,KAAK,MAAM,IAAI,QAAQ;AACjC,UAAM,eAAe,KAAK,WAAW,MAAM;AAC3C,UAAM,cAAc,KAAK,UAAU,MAAM;AAEzC,UAAM,KAAK,EAAE,GAAG,cAAc,YAAY,CAAC;AAAA,EAC7C;AAGA,MAAI,UAAU,SAAS,GAAG;AACxB,UAAM,KAAK;AAAA,MACT,GAAG,KAAK,MAAM,UAAU,SAAS,QAAQ;AAAA,MACzC,cAAc;AAAA,MACd,aAAa;AAAA,IACf,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAQO,IAAM,yBAAyB,CACpC,WACA,aAC2C;AAC3C,QAAM,SAAiD,CAAC;AACxD,MAAI,YAAY;AAChB,MAAI,oBAAoB;AAExB,WAAS,IAAI,GAAG,IAAI,UAAU,QAAQ,KAAK;AACzC,UAAM,OAAO,UAAU,CAAC;AACxB,UAAM,YAAY,KAAK,UAAU;AACjC,UAAMC,aAAY,cAAc,KAAK,cAAc;AAEnD,QAAIA,cAAa,CAAC,WAAW;AAE3B,kBAAY;AACZ,0BAAoB;AAAA,IACtB,WAAW,CAACA,cAAa,WAAW;AAElC,kBAAY;AACZ,YAAM,OAAO,KAAK,MAAM,oBAAoB,QAAQ;AACpD,YAAM,QAAQ,KAAK,OAAO,IAAI,qBAAqB,QAAQ;AAC3D,aAAO,KAAK,EAAE,MAAM,MAAM,CAAC;AAAA,IAC7B;AAAA,EACF;AAGA,MAAI,aAAa,qBAAqB,GAAG;AACvC,UAAM,OAAO,KAAK,MAAM,oBAAoB,QAAQ;AACpD,UAAM,QAAQ,KAAK,OAAO,UAAU,SAAS,qBAAqB,QAAQ;AAC1E,WAAO,KAAK,EAAE,MAAM,MAAM,CAAC;AAAA,EAC7B;AAEA,SAAO;AACT;AAQO,IAAM,sBAAsB,CACjC,MACA,OACW;AAGX,QAAM,mBAAmB,KAAK,IAAI,GAAG,IAAI,KAAK,CAAC;AAC/C,QAAM,WAAW,KAAK,IAAI,mBAAmB,KAAK,EAAE;AAGpD,MAAI,KAAK,MAAM,GAAG,GAAG;AACnB,UAAM,YAAY;AAClB,UAAM,QAAQ,KAAK,IAAI,GAAG,KAAK;AAC/B,WAAO,KAAK,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,IAAI,IAAI,KAAK,IAAI,SAAS,IAAI,GAAG,CAAC,IAAI,GAAG,CAAC;AAAA,EAC9E;AAGA,QAAM,OAAO,KAAK;AAClB,QAAM,OAAO,KAAK,KAAK,GAAG,IAAI,KAAK,IAAI,WAAW,CAAC;AACnD,QAAM,OAAO,GAAG;AAChB,QAAM,OAAO,GAAG,KAAK,GAAG,IAAI,KAAK,IAAI,WAAW,CAAC;AAEjD,SAAO,KAAK,KAAK,MAAM,KAAK,CAAC,CAAC,IAAI,KAAK,MAAM,KAAK,CAAC,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,KAAK,KAAK,MAAM,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,KAAK,KAAK,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK,MAAM,GAAG,CAAC,CAAC;AAClL;AAUO,IAAM,0BAA0B,CACrC,MACA,OACW;AACX,QAAM,KAAK,KAAK,MAAM,KAAK,CAAC;AAC5B,QAAM,KAAK,KAAK,MAAM,KAAK,CAAC;AAC5B,QAAM,KAAK,KAAK,MAAM,GAAG,CAAC;AAC1B,QAAM,KAAK,KAAK,MAAM,GAAG,CAAC;AAG1B,MAAI,OAAO,IAAI;AACb,WAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE;AAAA,EAC9B;AAEA,QAAM,IAAI;AACV,QAAM,YAAY,KAAK;AACvB,QAAM,aAAa,MAAM;AACzB,QAAM,OAAO,YAAY,IAAI;AAC7B,QAAM,OAAO,aAAa,IAAI;AAG9B,MAAI,KAAK,IAAI,KAAK,EAAE,KAAK,KAAK,KAAK,IAAI,KAAK,EAAE,KAAK,GAAG;AACpD,WAAO;AAAA,MACL,KAAK,EAAE,IAAI,EAAE;AAAA,MACb,KAAK,KAAK,OAAO,CAAC;AAAA,MAClB,KAAK,EAAE,IAAI,KAAK,OAAO,CAAC;AAAA,MACxB,KAAK,EAAE;AAAA,IACT,EAAE,KAAK,GAAG;AAAA,EACZ;AAGA,SAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE;AACtC;;;AClQA,SAAS,WAAW,QAAQ,UAAU,mBAAmB;AAuCzD,IAAI,mBAA2C;AAC/C,IAAI,cAA6B;AAKjC,SAAS,eAAe;AACtB,MAAI,gBAAgB,MAAM;AACxB,yBAAqB,WAAW;AAChC,kBAAc;AAAA,EAChB;AAEA,MAAI,kBAAkB;AAEpB,qBAAiB,oBAAoB,oBAAI,IAAI,CAAC;AAC9C,UAAM,EAAE,YAAY,aAAa,aAAa,IAAI;AAClD,uBAAmB;AACnB,eAAW,aAAa,YAAY;AAAA,EACtC;AACF;AAKA,SAAS,aAAa;AACpB,MAAI,gBAAgB,MAAM;AACxB,yBAAqB,WAAW;AAChC,kBAAc;AAAA,EAChB;AAEA,MAAI,kBAAkB;AACpB,UAAM,EAAE,SAAS,IAAI;AACrB,uBAAmB;AACnB,aAAS;AAAA,EACX;AACF;AAKA,SAAS,WAAW,QAAgB,UAA0B;AAC5D,SAAO,KAAK,MAAM,SAAS,QAAQ,IAAI;AACzC;AAwDA,SAAS,wBACP,MACA,cACA,UACmC;AACnC,MAAI,CAAC,KAAK,aAAc,QAAO,CAAC;AAChC,QAAM,WAAW,IAAI,IAAI,SAAS,IAAI,OAAK,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AAErD,SAAO,KAAK,aAAa,IAAI,SAAO;AAClC,QAAI,IAAI,SAAS,KAAM,QAAO;AAC9B,UAAM,cAAc,SAAS,IAAI,IAAI,MAAM;AAC3C,QAAI,CAAC,YAAa,QAAO;AACzB,UAAM,UAAU,IAAI,KAAK,YAAY,OAAiB;AACtD,UAAM,QAAQ,KAAK;AAAA,MACjB,aAAa,eAAe;AAAA,MAC5B,aAAa,YAAY;AAAA,MACzB,aAAa,WAAW;AAAA,IAC1B,IAAI,KAAK;AAAA,MACP,QAAQ,eAAe;AAAA,MACvB,QAAQ,YAAY;AAAA,MACpB,QAAQ,WAAW;AAAA,IACrB;AACA,UAAM,UAAU,KAAK,MAAM,SAAS,KAAK,KAAK,KAAK,IAAK;AACxD,WAAO,EAAE,GAAG,KAAK,KAAK,QAAQ;AAAA,EAChC,CAAC;AACH;AAKA,SAAS,sBAAsB,GAAe;AAC5C,MAAI,CAAC,oBAAoB,gBAAgB,MAAM;AAC7C;AAAA,EACF;AAEA,gBAAc,sBAAsB,MAAM;AACxC,QAAI,CAAC,kBAAkB;AACrB,oBAAc;AACd;AAAA,IACF;AAEA,UAAM,EAAE,QAAQ,aAAa,cAAc,MAAM,UAAU,YAAY,SAAS,IAAI;AACpF,UAAM,SAAS,EAAE,UAAU;AAE3B,QAAI,UAAU;AACd,QAAI,WAAW;AAEf,YAAQ,MAAM;AAAA,MACZ,KAAK;AACH,kBAAU,WAAW,cAAc,QAAQ,QAAQ;AACnD;AAAA,MACF,KAAK;AACH,cAAM,cAAc,WAAW,cAAc,QAAQ,QAAQ;AAC7D,kBAAU;AACV,cAAM,YAAY,cAAc;AAChC,mBAAW,KAAK,IAAI,UAAU,YAAY,WAAW;AACrD;AAAA,MACF,KAAK;AACH,cAAM,eAAe,WAAW,eAAe,QAAQ,QAAQ;AAC/D,mBAAW,KAAK,IAAI,UAAU,YAAY;AAC1C;AAAA,IACJ;AAIA,QAAI,SAAS,UAAU,SAAS,SAAS,KAAK,CAAC,iBAAiB,oBAAoB;AAClF,YAAM,cAAc,SAAS,KAAK,OAAK,EAAE,OAAO,kBAAkB,MAAM;AACxE,UAAI,eAAe,YAAY,gBAAgB,YAAY,aAAa,SAAS,GAAG;AAClF,YAAI,iBAAiB;AACrB,mBAAW,OAAO,YAAY,cAAc;AAC1C,cAAI,IAAI,SAAS,KAAM;AACvB,gBAAM,cAAc,iBAAiB,SAAS,KAAK,OAAK,EAAE,OAAO,IAAI,MAAM;AAC3E,cAAI,CAAC,YAAa;AAElB,gBAAM,YAAY,IAAI,KAAK,YAAY,SAAmB;AAC1D,gBAAM,kBAAkB,KAAK;AAAA,aAC1B,KAAK,IAAI,UAAU,eAAe,GAAG,UAAU,YAAY,GAAG,UAAU,WAAW,CAAC,IACnF,KAAK;AAAA,cACH,iBAAiB,WAAW,eAAe;AAAA,cAC3C,iBAAiB,WAAW,YAAY;AAAA,cACxC,iBAAiB,WAAW,WAAW;AAAA,YACzC,MAAM,KAAK,KAAK,KAAK;AAAA,UACzB;AACA,gBAAM,gBAAgB,KAAK,MAAM,kBAAkB,iBAAiB,QAAQ;AAC5E,2BAAiB,KAAK,IAAI,gBAAgB,aAAa;AAAA,QACzD;AAEA,kBAAU,KAAK,IAAI,gBAAgB,OAAO;AAAA,MAC5C;AAAA,IACF;AAGA,SAAK,SAAS,UAAU,SAAS,mBAAmB,CAAC,iBAAiB,sBAClE,iBAAiB,aAAa,SAAS,KAAK,iBAAiB,mBAAmB;AAGlF,YAAM,YAAY,SAAS,iBACvB,KAAK,OAAO,WAAW,iBAAiB,gBAAgB,iBAAiB,QAAQ,IACjF,KAAK,OAAO,UAAU,iBAAiB,eAAe,iBAAiB,QAAQ;AACnF,YAAM,YAAY,oBAAI,IAA6C;AACnE,iBAAW,aAAa,iBAAiB,cAAc;AACrD,cAAM,aAAa,IAAI,KAAK,UAAU,SAAmB;AACzD,cAAM,WAAW,IAAI,KAAK,UAAU,OAAiB;AACrD,cAAM,mBAAmB,KAAK;AAAA,WAC3B,KAAK,IAAI,WAAW,eAAe,GAAG,WAAW,YAAY,GAAG,WAAW,WAAW,CAAC,IACtF,KAAK;AAAA,YACH,iBAAiB,WAAW,eAAe;AAAA,YAC3C,iBAAiB,WAAW,YAAY;AAAA,YACxC,iBAAiB,WAAW,WAAW;AAAA,UACzC,MAAM,KAAK,KAAK,KAAK;AAAA,QACzB;AACA,cAAM,gBAAgB,KAAK;AAAA,WACxB,KAAK,IAAI,SAAS,eAAe,GAAG,SAAS,YAAY,GAAG,SAAS,WAAW,CAAC,IAChF,KAAK,IAAI,WAAW,eAAe,GAAG,WAAW,YAAY,GAAG,WAAW,WAAW,CAAC,MACpF,KAAK,KAAK,KAAK;AAAA,QACtB;AACA,cAAM,YAAY,KAAK,OAAO,mBAAmB,aAAa,iBAAiB,QAAQ;AACvF,cAAM,aAAa,KAAK,OAAO,gBAAgB,KAAK,iBAAiB,QAAQ;AAC7E,kBAAU,IAAI,UAAU,IAAI,EAAE,MAAM,WAAW,OAAO,WAAW,CAAC;AAAA,MACpE;AACA,uBAAiB,kBAAkB,SAAS;AAAA,IAC9C;AAGA,qBAAiB,cAAc;AAC/B,qBAAiB,eAAe;AAEhC,eAAW,SAAS,QAAQ;AAC5B,kBAAc;AAAA,EAChB,CAAC;AACH;AAKA,SAAS,sBAAsB;AAC7B,MAAI,kBAAkB;AACpB,iBAAa;AAAA,EACf;AACF;AAKA,IAAI,0BAA0B;AAK9B,SAAS,wBAAwB;AAC/B,MAAI,CAAC,yBAAyB;AAC5B,WAAO,iBAAiB,aAAa,qBAAqB;AAC1D,WAAO,iBAAiB,WAAW,mBAAmB;AACtD,8BAA0B;AAAA,EAC5B;AACF;AA4EO,IAAM,cAAc,CAAC,YAAmD;AAC7E,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,gBAAgB;AAAA,IAChB,WAAW,CAAC;AAAA,IACZ;AAAA,IACA,qBAAqB;AAAA,IACrB,qBAAqB;AAAA,IACrB;AAAA,IACA;AAAA,EACF,IAAI;AAGJ,QAAM,aAAa,OAAgB,KAAK;AAGxC,QAAM,CAAC,YAAY,aAAa,IAAI,SAAkB,KAAK;AAC3D,QAAM,CAAC,UAAU,WAAW,IAAI,SAAyD,IAAI;AAC7F,QAAM,CAAC,aAAa,cAAc,IAAI,SAAiB,CAAC;AACxD,QAAM,CAAC,cAAc,eAAe,IAAI,SAAiB,CAAC;AAK1D,QAAM,qBAAqB,YAAY,MAAuC;AAC5E,UAAMC,uBAAsB,CAAC,OAAa,UAAwB;AAChE,YAAM,MAAM,KAAK;AAAA,QACf,MAAM,eAAe;AAAA,QACrB,MAAM,YAAY;AAAA,QAClB,MAAM,WAAW;AAAA,MACnB;AACA,YAAM,MAAM,KAAK;AAAA,QACf,MAAM,eAAe;AAAA,QACrB,MAAM,YAAY;AAAA,QAClB,MAAM,WAAW;AAAA,MACnB;AACA,aAAO,KAAK,OAAO,MAAM,QAAQ,MAAO,KAAK,KAAK,GAAG;AAAA,IACvD;AAEA,UAAM,cAAcA,qBAAoB,kBAAkB,UAAU;AACpE,UAAM,WAAWA,qBAAoB,gBAAgB,gBAAgB;AAErE,UAAM,OAAO,KAAK,MAAM,cAAc,QAAQ;AAC9C,UAAM,QAAQ,KAAK,OAAO,WAAW,KAAK,QAAQ;AAElD,WAAO,EAAE,MAAM,MAAM;AAAA,EACvB,GAAG,CAAC,kBAAkB,gBAAgB,YAAY,QAAQ,CAAC;AAK3D,YAAU,MAAM;AACd,UAAM,EAAE,MAAM,MAAM,IAAI,mBAAmB;AAC3C,mBAAe,IAAI;AACnB,oBAAgB,KAAK;AAAA,EACvB,GAAG,CAAC,kBAAkB,CAAC;AAKvB,QAAM,iBAAiB,YAAY,CAAC,MAAc,UAAkB;AAClE,mBAAe,IAAI;AACnB,oBAAgB,KAAK;AAErB,QAAI,qBAAqB,WAAW,SAAS;AAC3C,YAAM,OAAO,kBAAkB,QAAQ;AACvC,wBAAkB;AAAA,QAChB,YAAY;AAAA,QACZ,UAAU;AAAA,QACV;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF,GAAG,CAAC,iBAAiB,CAAC;AAKtB,QAAM,iBAAiB,YAAY,CAAC,WAAmB,eAAuB;AAC5E,UAAM,WAAW,WAAW;AAC5B,eAAW,UAAU;AAGrB,UAAM,YAAY,KAAK,MAAM,YAAY,QAAQ;AACjD,UAAM,eAAe,KAAK,MAAM,aAAa,QAAQ,IAAI;AAEzD,UAAM,eAAe,IAAI,KAAK,KAAK;AAAA,MACjC,WAAW,eAAe;AAAA,MAC1B,WAAW,YAAY;AAAA,MACvB,WAAW,WAAW,IAAI;AAAA,IAC5B,CAAC;AAED,UAAM,aAAa,IAAI,KAAK,KAAK;AAAA,MAC/B,WAAW,eAAe;AAAA,MAC1B,WAAW,YAAY;AAAA,MACvB,WAAW,WAAW,IAAI,YAAY;AAAA,IACxC,CAAC;AAGD,kBAAc,KAAK;AACnB,gBAAY,IAAI;AAGhB,QAAI,mBAAmB;AACrB,wBAAkB;AAAA,QAChB,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,MAAM;AAAA,QACN,OAAO;AAAA,MACT,CAAC;AAAA,IACH;AAEA,QAAI,UAAU;AACZ,UAAI,CAAC,sBAAsB,aAAa,SAAS,SAAS,GAAG;AAE3D,cAAM,QAAQ,kBAAkB,QAAQ,QAAQ;AAChD,YAAI,MAAM,SAAS,GAAG;AAIpB,gBAAM,YAAY,KAAK;AAAA,YACrB,eAAe,eAAe;AAAA,YAC9B,eAAe,YAAY;AAAA,YAC3B,eAAe,WAAW;AAAA,UAC5B;AACA,gBAAM,WAAW,KAAK;AAAA,YACpB,WAAW,eAAe;AAAA,YAC1B,WAAW,YAAY;AAAA,YACvB,WAAW,WAAW;AAAA,UACxB;AACA,gBAAM,YAAY,KAAK,OAAO,WAAW,cAAc,KAAK,KAAK,KAAK,IAAK;AAE3E,gBAAM,kBAAkB,SAAS,KAAK,OAAK,EAAE,OAAO,MAAM;AAC1D,gBAAM,gBAAwB;AAAA,YAC5B;AAAA,cACE,GAAI,mBAAmB,EAAE,IAAI,QAAQ,MAAM,IAAI,WAAW,IAAI,SAAS,GAAG;AAAA,cAC1E,WAAW,aAAa,YAAY;AAAA,cACpC,SAAS,WAAW,YAAY;AAAA,cAChC,GAAI,iBAAiB,gBAAgB;AAAA,gBACnC,cAAc,wBAAwB,iBAAiB,cAAc,QAAQ;AAAA,cAC/E;AAAA,YACF;AAAA,YACA,GAAG,MAAM,IAAI,eAAa;AACxB,oBAAM,YAAY,IAAI,KAAK,UAAU,SAAmB;AACxD,oBAAM,UAAU,IAAI,KAAK,UAAU,OAAiB;AACpD,oBAAM,WAAW,IAAI,KAAK,KAAK;AAAA,gBAC7B,UAAU,eAAe;AAAA,gBAAG,UAAU,YAAY;AAAA,gBAAG,UAAU,WAAW,IAAI;AAAA,cAChF,CAAC;AACD,oBAAM,SAAS,IAAI,KAAK,KAAK;AAAA,gBAC3B,QAAQ,eAAe;AAAA,gBAAG,QAAQ,YAAY;AAAA,gBAAG,QAAQ,WAAW,IAAI;AAAA,cAC1E,CAAC;AACD,qBAAO,EAAE,GAAG,WAAW,WAAW,SAAS,YAAY,GAAG,SAAS,OAAO,YAAY,EAAE;AAAA,YAC1F,CAAC;AAAA,UACH;AACA,oBAAU,aAAa;AACvB;AAAA,QACF;AAAA,MACF;AAIA,UAAI,SAAS,SAAS,KAAK,WAAW;AACpC,cAAM,kBAAkB,SAAS,KAAK,OAAK,EAAE,OAAO,MAAM;AAC1D,cAAM,sBAAsB,iBAAiB,eACzC,wBAAwB,iBAAiB,cAAc,QAAQ,IAC/D;AACJ,kBAAU,EAAE,IAAI,QAAQ,WAAW,cAAc,SAAS,YAAY,oBAAoB,CAAC;AAAA,MAC7F,WAAW,WAAW;AACpB,kBAAU,EAAE,IAAI,QAAQ,WAAW,cAAc,SAAS,WAAW,CAAC;AAAA,MACxE;AAAA,IACF;AAAA,EACF,GAAG,CAAC,UAAU,YAAY,WAAW,mBAAmB,QAAQ,oBAAoB,WAAW,UAAU,kBAAkB,cAAc,CAAC;AAK1I,QAAM,eAAe,YAAY,MAAM;AACrC,eAAW,UAAU;AACrB,kBAAc,KAAK;AACnB,gBAAY,IAAI;AAEhB,QAAI,mBAAmB;AACrB,wBAAkB;AAAA,QAChB,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,MAAM;AAAA,QACN,OAAO;AAAA,MACT,CAAC;AAAA,IACH;AAAA,EACF,GAAG,CAAC,mBAAmB,aAAa,YAAY,CAAC;AAKjD,YAAU,MAAM;AACd,WAAO,MAAM;AACX,UAAI,WAAW,WAAW,kBAAkB;AAE1C,mBAAW;AAAA,MACb;AAAA,IACF;AAAA,EACF,GAAG,CAAC,CAAC;AAKL,QAAM,kBAAkB,YAAY,CAAC,MAAwB;AAC3D,UAAM,SAAS,EAAE;AACjB,UAAM,WAAW,eAAe,EAAE,SAAS,QAAQ,aAAa;AAGhE,QAAI,OAAuD;AAC3D,YAAQ,UAAU;AAAA,MAChB,KAAK;AACH,eAAO;AACP;AAAA,MACF,KAAK;AACH,eAAO;AACP;AAAA,MACF,KAAK;AACH,eAAO;AACP;AAAA,IACJ;AAEA,QAAI,CAAC,MAAM;AACT;AAAA,IACF;AAGA,UAAM,cAAc;AACpB,UAAM,eAAe;AAGrB,eAAW,UAAU;AAGrB,kBAAc,IAAI;AAClB,gBAAY,IAAI;AAGhB,QAAI,mBAAmB;AACrB,wBAAkB;AAAA,QAChB,YAAY;AAAA,QACZ,UAAU;AAAA,QACV,MAAM;AAAA,QACN,OAAO;AAAA,MACT,CAAC;AAAA,IACH;AAGA,0BAAsB;AAGtB,uBAAmB;AAAA,MACjB;AAAA,MACA;AAAA,MACA,QAAQ,EAAE;AAAA,MACV;AAAA,MACA;AAAA,MACA,aAAa;AAAA;AAAA,MACb,cAAc;AAAA;AAAA,MACd;AAAA,MACA;AAAA,MACA,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,UAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA,eAAe,SAAS,UAAU,SAAS,mBAAmB,CAAC,qBAC3D,kBAAkB,QAAQ,QAAQ,IAClC,CAAC;AAAA,MACL;AAAA,IACF;AAAA,EACF,GAAG,CAAC,eAAe,aAAa,cAAc,UAAU,YAAY,QAAQ,mBAAmB,gBAAgB,gBAAgB,cAAc,UAAU,oBAAoB,mBAAmB,SAAS,CAAC;AAKxM,QAAM,iBAAiB,YAAY,MAAc;AAC/C,QAAI,YAAY;AACd,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT,GAAG,CAAC,UAAU,CAAC;AAEf,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,iBAAiB;AAAA,MACf,aAAa;AAAA,MACb,OAAO;AAAA,QACL,QAAQ,eAAe;AAAA,QACvB,YAAY;AAAA,MACd;AAAA,IACF;AAAA,EACF;AACF;;;AF5dc,gBAAAC,MASF,QAAAC,aATE;AAvId,IAAM,gBAAgB,CAAC,WAAyB,cAA4B;AAC1E,SACE,UAAU,KAAK,OAAO,UAAU,KAAK,MACrC,UAAU,KAAK,SAAS,UAAU,KAAK,QACvC,UAAU,KAAK,cAAc,UAAU,KAAK,aAC5C,UAAU,KAAK,YAAY,UAAU,KAAK,WAC1C,UAAU,KAAK,UAAU,UAAU,KAAK,SACxC,UAAU,KAAK,aAAa,UAAU,KAAK,YAC3C,UAAU,KAAK,aAAa,UAAU,KAAK,YAC3C,UAAU,WAAW,QAAQ,MAAM,UAAU,WAAW,QAAQ,KAChE,UAAU,aAAa,UAAU,YACjC,UAAU,cAAc,UAAU,aAClC,UAAU,kBAAkB,SAAS,UAAU,kBAAkB,QACjE,UAAU,kBAAkB,UAAU,UAAU,kBAAkB,SAClE,UAAU,aAAa,UAAU,YACjC,UAAU,uBAAuB,UAAU;AAG/C;AAQA,IAAM,UAAkCC,OAAM;AAAA,EAC5C,CAAC,EAAE,MAAM,YAAY,UAAU,WAAW,UAAU,mBAAmB,UAAU,UAAU,oBAAoB,oBAAoB,kBAAkB,mBAAmB,UAAU,MAAM;AAEtL,UAAM,gBAAgBC,SAAQ,MAAM,aAAa,KAAK,SAAS,GAAG,CAAC,KAAK,SAAS,CAAC;AAClF,UAAM,cAAcA,SAAQ,MAAM,aAAa,KAAK,OAAO,GAAG,CAAC,KAAK,OAAO,CAAC;AAG5E,UAAM,EAAE,MAAM,MAAM,IAAIA;AAAA,MACtB,MAAM,iBAAiB,eAAe,aAAa,YAAY,QAAQ;AAAA,MACvE,CAAC,eAAe,aAAa,YAAY,QAAQ;AAAA,IACnD;AAGA,UAAM,WAAW,KAAK,SAAS;AAG/B,UAAM,gBAAgBA,SAAQ,MAAM;AAClC,UAAI,KAAK,aAAa,UAAa,KAAK,YAAY,EAAG,QAAO;AAC9D,aAAO,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,KAAK,MAAM,KAAK,QAAQ,CAAC,CAAC;AAAA,IAC7D,GAAG,CAAC,KAAK,QAAQ,CAAC;AAGlB,UAAM,gBAAgBA,SAAQ,MAAM;AAClC,UAAI,kBAAkB,KAAK;AACzB,eAAO,KAAK,WACR,4CACA;AAAA,MACN;AAEA,aAAO,KAAK,QACR,sBAAsB,KAAK,KAAK,iBAChC;AAAA,IACN,GAAG,CAAC,eAAe,KAAK,UAAU,KAAK,KAAK,CAAC;AAG7C,UAAM,gBAAgB,CAAC,WAAuG;AAC5H,YAAM,cAAoB;AAAA,QACxB,GAAG;AAAA,QACH,WAAW,OAAO,UAAU,YAAY;AAAA,QACxC,SAAS,OAAO,QAAQ,YAAY;AAAA,QACpC,GAAI,OAAO,wBAAwB,UAAa,EAAE,cAAc,OAAO,oBAAoB;AAAA,MAC7F;AACA,iBAAW,WAAW;AAAA,IACxB;AAGA,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,IAAI,YAAY;AAAA,MACd,QAAQ,KAAK;AAAA,MACb,kBAAkB;AAAA,MAClB,gBAAgB;AAAA,MAChB;AAAA,MACA;AAAA,MACA,WAAW;AAAA,MACX;AAAA,MACA,eAAe;AAAA,MACf;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAGD,UAAM,cAAc,kBAAkB,SAAS,aAAa,cAAc;AAC1E,UAAM,eAAe,kBAAkB,UAAU,aAAa,eAAe;AAG7E,UAAM,mBAAmB,aACrB,aAAa,aAAa,YAAY,QAAQ,IAC9C;AACJ,UAAM,iBAAiB,aACnB,aAAa,cAAc,eAAe,UAAU,YAAY,QAAQ,IACxE;AAEJ,UAAM,iBAAiB,gBAAgB,gBAAgB;AACvD,UAAM,eAAe,gBAAgB,cAAc;AAGnD,UAAM,eAAe,KAAK;AAAA,OACvB,eAAe,QAAQ,IAAI,iBAAiB,QAAQ,MAAM,MAAO,KAAK,KAAK;AAAA,IAC9E,IAAI;AAEJ,WACE,gBAAAH;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,OAAO,EAAE,QAAQ,GAAG,SAAS,KAAK;AAAA,QAElC,0BAAAC,MAAC,SAAI,WAAU,0BACb;AAAA,0BAAAA;AAAA,YAAC;AAAA;AAAA,cACC,gBAAY;AAAA,cACZ,WAAW,oBAAoB,aAAa,sBAAsB,EAAE;AAAA,cACpE,OAAO;AAAA,gBACL,MAAM,GAAG,WAAW;AAAA,gBACpB,OAAO,GAAG,YAAY;AAAA,gBACtB,iBAAiB;AAAA,gBACjB,QAAQ;AAAA,gBACR,QAAQ,gBAAgB,MAAM;AAAA,gBAC9B,YAAY,gBAAgB,MAAM;AAAA,cACpC;AAAA,cACA,aAAa,gBAAgB;AAAA,cAE5B;AAAA,gCAAgB,KACf,gBAAAD;AAAA,kBAAC;AAAA;AAAA,oBACC,WAAU;AAAA,oBACV,OAAO;AAAA,sBACL,OAAO,GAAG,aAAa;AAAA,sBACvB,iBAAiB;AAAA,oBACnB;AAAA;AAAA,gBACF;AAAA,gBAEF,gBAAAA,KAAC,SAAI,WAAU,mDAAkD;AAAA,gBACjE,gBAAAC,MAAC,UAAK,WAAU,yBACb;AAAA;AAAA,kBAAa;AAAA,mBAChB;AAAA,gBACA,gBAAAD,KAAC,SAAI,WAAU,oDAAmD;AAAA;AAAA;AAAA,UACpE;AAAA,UACA,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,OAAO;AAAA,gBACL,MAAM,GAAG,WAAW;AAAA,cACtB;AAAA,cAEA,0BAAAC,MAAC,UAAK,WAAU,6CACb;AAAA;AAAA,gBAAe;AAAA,gBAAE;AAAA,iBACpB;AAAA;AAAA,UACF;AAAA,UACA,gBAAAD;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,OAAO;AAAA,gBACL,MAAM,GAAG,cAAc,YAAY;AAAA,cACrC;AAAA,cAEA,0BAAAA,KAAC,UAAK,WAAU,6BACb,eAAK,MACR;AAAA;AAAA,UACF;AAAA,WACF;AAAA;AAAA,IACF;AAAA,EAEJ;AAAA,EACA;AACF;AAEA,QAAQ,cAAc;AAEtB,IAAO,kBAAQ;;;AG/Of,SAAgB,WAAAI,gBAAe;AA+C3B,gBAAAC,YAAA;AA9BJ,IAAM,iBAAgD,CAAC,EAAE,YAAY,SAAS,MAAM;AAElF,QAAM,QAAQ,oBAAI,KAAK;AACvB,QAAM,aAAa,IAAI,KAAK,KAAK;AAAA,IAC/B,MAAM,YAAY;AAAA,IAClB,MAAM,SAAS;AAAA,IACf,MAAM,QAAQ;AAAA,EAChB,CAAC;AAGD,QAAM,YAAYC,SAAQ,MAAM;AAC9B,WACE,WAAW,eAAe,MAAM,WAAW,eAAe,KAC1D,WAAW,YAAY,MAAM,WAAW,YAAY;AAAA,EAExD,GAAG,CAAC,YAAY,UAAU,CAAC;AAG3B,QAAM,WAAWA,SAAQ,MAAM;AAC7B,QAAI,CAAC,UAAW,QAAO;AAEvB,UAAM,SAAS,aAAa,YAAY,UAAU;AAClD,WAAO,KAAK,MAAM,SAAS,QAAQ;AAAA,EACrC,GAAG,CAAC,WAAW,YAAY,UAAU,UAAU,CAAC;AAEhD,MAAI,CAAC,aAAa,aAAa,MAAM;AACnC,WAAO;AAAA,EACT;AAEA,SACE,gBAAAD;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,OAAO;AAAA,QACL,MAAM,GAAG,QAAQ;AAAA,QACjB,OAAO;AAAA,QACP,iBAAiB;AAAA,MACnB;AAAA,MACA,cAAW;AAAA;AAAA,EACb;AAEJ;AAEA,IAAO,yBAAQ;;;AC3Df,OAAOE,UAAS,WAAAC,gBAAe;AA0DzB,SASI,OAAAC,MATJ,QAAAC,aAAA;AAtCN,IAAMC,iBAAgB,CAAC,WAAgC,cAAmC;AACxF,SACE,UAAU,aAAa,UAAU,YACjC,UAAU,UAAU,WAAW,UAAU,UAAU,UACnD,UAAU,gBAAgB,UAAU;AAExC;AAcA,IAAM,iBAAgDC,OAAM;AAAA,EAC1D,CAAC,EAAE,WAAW,UAAU,YAAY,MAAM;AAExC,UAAM,YAAYC,SAAoB,MAAM;AAC1C,aAAO,mBAAmB,WAAW,QAAQ;AAAA,IAC/C,GAAG,CAAC,WAAW,QAAQ,CAAC;AAGxB,UAAM,gBAAgBA,SAAQ,MAAM;AAClC,aAAO,uBAAuB,WAAW,QAAQ;AAAA,IACnD,GAAG,CAAC,WAAW,QAAQ,CAAC;AAGxB,UAAM,YAAYA,SAAQ,MAAM;AAC9B,aAAO,KAAK,MAAM,UAAU,SAAS,QAAQ;AAAA,IAC/C,GAAG,CAAC,UAAU,QAAQ,QAAQ,CAAC;AAE/B,WACE,gBAAAH;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,OAAO;AAAA,UACL,OAAO,GAAG,SAAS;AAAA,UACnB,QAAQ,GAAG,WAAW;AAAA,QACxB;AAAA,QAGC;AAAA,wBAAc,IAAI,CAAC,OAAO,UACzB,gBAAAD;AAAA,YAAC;AAAA;AAAA,cAEC,WAAU;AAAA,cACV,OAAO;AAAA,gBACL,MAAM,GAAG,MAAM,IAAI;AAAA,gBACnB,OAAO,GAAG,MAAM,KAAK;AAAA,cACvB;AAAA;AAAA,YALK,WAAW,KAAK;AAAA,UAMvB,CACD;AAAA,UAGA,UAAU,IAAI,CAAC,MAAM,UAAU;AAE9B,kBAAM,YAAY,KAAK,eACnB,4BACA,KAAK,cACH,2BACA;AAEN,mBACE,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBAEC,WAAW,qBAAqB,SAAS;AAAA,gBACzC,OAAO;AAAA,kBACL,MAAM,GAAG,KAAK,CAAC;AAAA,gBACjB;AAAA;AAAA,cAJK,YAAY,KAAK;AAAA,YAKxB;AAAA,UAEJ,CAAC;AAAA;AAAA;AAAA,IACH;AAAA,EAEJ;AAAA,EACAE;AACF;AAEA,eAAe,cAAc;AAE7B,IAAO,yBAAQ;;;AC7EX,mBAEI,OAAAG,MAFJ,QAAAC,aAAA;AAhBJ,IAAM,iBAAgD,CAAC;AAAA,EACrD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAAM;AACJ,MAAI,CAAC,cAAc,CAAC,UAAU;AAC5B,WAAO;AAAA,EACT;AAGA,QAAM,eAAe,aAAa,UAAU,aAAa;AACzD,QAAM,gBAAgB,aAAa,UAAU,aAAa;AAE1D,SACE,gBAAAA,MAAA,YACG;AAAA,oBACC,gBAAAD;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,OAAO;AAAA,UACL,MAAM,GAAG,IAAI;AAAA,UACb,QAAQ,GAAG,WAAW;AAAA,QACxB;AAAA;AAAA,IACF;AAAA,IAED,iBACC,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,OAAO;AAAA,UACL,MAAM,GAAG,OAAO,KAAK;AAAA,UACrB,QAAQ,GAAG,WAAW;AAAA,QACxB;AAAA;AAAA,IACF;AAAA,KAEJ;AAEJ;AAEA,IAAO,yBAAQ;;;AClDf,OAAOE,UAAS,WAAAC,gBAAe;AAqIzB,SAWI,OAAAC,MAXJ,QAAAC,aAAA;AAtGC,IAAM,kBAAkDC,OAAM,KAAK,CAAC;AAAA,EACzE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAAM;AAEJ,QAAM,EAAE,eAAe,YAAY,IAAIC,SAAQ,MAAM;AACnD,UAAM,YAAY,oBAAI,IAA6D;AACnF,UAAM,UAAU,oBAAI,IAAoB;AAExC,UAAM,QAAQ,CAAC,MAAM,UAAU;AAC7B,YAAM,YAAY,IAAI,KAAK,KAAK,SAAS;AACzC,YAAM,UAAU,IAAI,KAAK,KAAK,OAAO;AACrC,YAAM,WAAW,iBAAiB,WAAW,SAAS,YAAY,QAAQ;AAG1E,YAAM,WAAW,eAAe,IAAI,KAAK,EAAE;AAC3C,YAAM,eAAe,UAAU,QAAQ,SAAS;AAChD,YAAM,gBAAgB,UAAU,SAAS,SAAS;AAElD,cAAQ,IAAI,KAAK,IAAI,KAAK;AAC1B,gBAAU,IAAI,KAAK,IAAI;AAAA,QACrB,MAAM,eAAe;AAAA,QACrB,OAAO,eAAe;AAAA,QACtB,QAAQ,QAAQ;AAAA,MAClB,CAAC;AAAA,IACH,CAAC;AAED,WAAO,EAAE,eAAe,WAAW,aAAa,QAAQ;AAAA,EAC1D,GAAG,CAAC,OAAO,YAAY,UAAU,WAAW,aAAa,CAAC;AAG1D,QAAM,YAAYA,SAAQ,MAAM;AAC9B,UAAM,SAAS,aAAa,KAAK;AACjC,UAAM,eAAe,IAAI,IAAI,OAAO,aAAa,CAAC,CAAC;AACnD,WAAO;AAAA,EACT,GAAG,CAAC,KAAK,CAAC;AAGV,QAAM,QAAQA,SAAQ,MAAM;AAC1B,UAAM,QAAQ,sBAAsB,KAAK;AACzC,UAAMC,SAID,CAAC;AAEN,eAAW,QAAQ,OAAO;AACxB,YAAM,cAAc,cAAc,IAAI,KAAK,aAAa;AACxD,YAAM,YAAY,cAAc,IAAI,KAAK,WAAW;AACpD,YAAM,mBAAmB,YAAY,IAAI,KAAK,aAAa;AAC3D,YAAM,iBAAiB,YAAY,IAAI,KAAK,WAAW;AAEvD,UAAI,CAAC,eAAe,CAAC,aAAa,qBAAqB,UAAa,mBAAmB,QAAW;AAChG;AAAA,MACF;AAGA,YAAM,eAAe,mBAAmB;AAGxC,UAAI;AACJ,UAAI;AAEJ,UAAI,cAAc;AAEhB,gBAAQ,YAAY,SAAS;AAC7B,cAAM,UAAU,SAAS,YAAY;AAAA,MACvC,OAAO;AAEL,gBAAQ,YAAY,SAAS,YAAY;AACzC,cAAM,UAAU,SAAS;AAAA,MAC3B;AAEA,YAAM,OAAO,EAAE,GAAG,YAAY,OAAO,GAAG,MAAM;AAC9C,YAAM,KAAK,EAAE,GAAG,UAAU,MAAM,GAAG,IAAI;AAEvC,YAAM,OAAO,wBAAwB,MAAM,EAAE;AAG7C,YAAM,WAAW,UAAU,IAAI,KAAK,aAAa,KAAK,UAAU,IAAI,KAAK,WAAW;AAEpF,MAAAA,OAAM,KAAK;AAAA,QACT,IAAI,GAAG,KAAK,aAAa,IAAI,KAAK,WAAW;AAAA,QAC7C;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAEA,WAAOA;AAAA,EACT,GAAG,CAAC,OAAO,eAAe,aAAa,SAAS,CAAC;AAEjD,SACE,gBAAAH;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,OAAO;AAAA,MACP,QAAQ,MAAM,SAAS;AAAA,MACvB,OAAM;AAAA,MAEN;AAAA,wBAAAA,MAAC,UAEC;AAAA,0BAAAD;AAAA,YAAC;AAAA;AAAA,cACC,IAAG;AAAA,cACH,aAAY;AAAA,cACZ,cAAa;AAAA,cACb,aAAY;AAAA,cACZ,MAAK;AAAA,cACL,MAAK;AAAA,cACL,QAAO;AAAA,cAEP,0BAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,QAAO;AAAA,kBACP,MAAK;AAAA;AAAA,cACP;AAAA;AAAA,UACF;AAAA,UAGA,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,IAAG;AAAA,cACH,aAAY;AAAA,cACZ,cAAa;AAAA,cACb,aAAY;AAAA,cACZ,MAAK;AAAA,cACL,MAAK;AAAA,cACL,QAAO;AAAA,cAEP,0BAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,QAAO;AAAA,kBACP,MAAK;AAAA;AAAA,cACP;AAAA;AAAA,UACF;AAAA,WACF;AAAA,QAEC,MAAM,IAAI,CAAC,EAAE,IAAI,MAAM,SAAS,MAC/B,gBAAAA;AAAA,UAAC;AAAA;AAAA,YAEC,GAAG;AAAA,YACH,WAAW,WAAW,iDAAiD;AAAA,YACvE,WAAW,WAAW,0BAA0B;AAAA;AAAA,UAH3C;AAAA,QAIP,CACD;AAAA;AAAA;AAAA,EACH;AAEJ,CAAC;AAED,gBAAgB,cAAc;AAE9B,IAAO,0BAAQ;;;AV8GL,gBAAAK,MAQF,QAAAC,aARE;AA9LH,IAAM,aAAwC,CAAC;AAAA,EACpD;AAAA,EACA,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAAM;AACJ,QAAM,qBAAqBC,QAAuB,IAAI;AAGtD,QAAM,YAAYC,SAAQ,MAAM,kBAAkB,KAAK,GAAG,CAAC,KAAK,CAAC;AAGjE,QAAM,CAAC,kBAAkB,mBAAmB,IAAIC,UAAkC,IAAI;AAGtF,QAAM,CAAC,kBAAkB,mBAAmB,IAAIA,UAAuD,oBAAI,IAAI,CAAC;AAGhH,QAAM,YAAYD;AAAA,IAChB,MAAM,KAAK,MAAM,UAAU,SAAS,QAAQ;AAAA,IAC5C,CAAC,UAAU,QAAQ,QAAQ;AAAA,EAC7B;AAGA,QAAM,kBAAkBA;AAAA,IACtB,MAAM,MAAM,SAAS;AAAA,IACrB,CAAC,MAAM,QAAQ,SAAS;AAAA,EAC1B;AAGA,QAAM,aAAaA,SAAQ,MAAM;AAC/B,QAAI,UAAU,WAAW,GAAG;AAC1B,aAAO,IAAI,KAAK,KAAK,KAAI,oBAAI,KAAK,GAAE,eAAe,IAAG,oBAAI,KAAK,GAAE,YAAY,GAAG,CAAC,CAAC;AAAA,IACpF;AACA,UAAM,WAAW,UAAU,CAAC;AAC5B,WAAO,IAAI,KAAK,KAAK,IAAI,SAAS,eAAe,GAAG,SAAS,YAAY,GAAG,CAAC,CAAC;AAAA,EAChF,GAAG,CAAC,SAAS,CAAC;AAGd,QAAM,eAAeA,SAAQ,MAAM;AACjC,UAAM,MAAM,oBAAI,KAAK;AACrB,UAAM,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,eAAe,GAAG,IAAI,YAAY,GAAG,IAAI,WAAW,CAAC,CAAC;AAC1F,WAAO,UAAU,KAAK,SAAO,IAAI,QAAQ,MAAM,MAAM,QAAQ,CAAC;AAAA,EAChE,GAAG,CAAC,SAAS,CAAC;AAGd,QAAM,CAAC,gBAAgB,iBAAiB,IAAIC,UAKlC,IAAI;AAGd,QAAM,CAAC,qBAAqB,sBAAsB,IAAIA,UAAiE,IAAI;AAG3H,EAAAC,WAAU,MAAM;AACd,UAAM,SAAS,qBAAqB,KAAK;AACzC,wBAAoB,MAAM;AAC1B,6BAAyB,MAAM;AAAA,EACjC,GAAG,CAAC,OAAO,sBAAsB,CAAC;AAoBlC,QAAM,mBAAmBC,aAAY,CAAC,gBAAsB;AAE1D;AAAA,MAAW,CAAC,iBACV,aAAa;AAAA,QAAI,CAAC,MAChB,EAAE,OAAO,YAAY,KAAK,cAAc;AAAA,MAC1C;AAAA,IACF;AAAA,EACF,GAAG,CAAC,QAAQ,CAAC;AAGb,QAAM,sBAAsBH,SAAQ,MAAM;AACxC,UAAM,MAAM,IAAI,IAAI,gBAAgB;AACpC,QAAI,qBAAqB;AACvB,UAAI,IAAI,oBAAoB,QAAQ;AAAA,QAClC,MAAM,oBAAoB;AAAA,QAC1B,OAAO,oBAAoB;AAAA,MAC7B,CAAC;AAAA,IACH;AACA,WAAO;AAAA,EACT,GAAG,CAAC,kBAAkB,mBAAmB,CAAC;AAO1C,QAAM,wBAAwBG,aAAY,CAAC,cAA4D;AACrG,wBAAoB,IAAI,IAAI,SAAS,CAAC;AAAA,EACxC,GAAG,CAAC,CAAC;AAML,QAAM,gBAAgBA,aAAY,CAAC,kBAA0B;AAE3D,eAAW,CAAC,iBAAiB;AAC3B,YAAM,aAAa,IAAI,IAAI,cAAc,IAAI,OAAK,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;AAC5D,aAAO,aAAa,IAAI,OAAK,WAAW,IAAI,EAAE,EAAE,KAAK,CAAC;AAAA,IACxD,CAAC;AAED,gBAAY,aAAa;AAAA,EAC3B,GAAG,CAAC,UAAU,SAAS,CAAC;AAGxB,QAAM,cAAcJ,QAAqG,IAAI;AAE7H,QAAM,iBAAiBI,aAAY,CAAC,MAAwB;AAE1D,QAAI,EAAE,WAAW,EAAG;AACpB,UAAM,SAAS,EAAE;AACjB,QAAI,OAAO,QAAQ,gBAAgB,EAAG;AAEtC,UAAM,YAAY,mBAAmB;AACrC,QAAI,CAAC,UAAW;AAEhB,gBAAY,UAAU;AAAA,MACpB,QAAQ;AAAA,MACR,QAAQ,EAAE;AAAA,MACV,QAAQ,EAAE;AAAA,MACV,SAAS,UAAU;AAAA,MACnB,SAAS,UAAU;AAAA,IACrB;AACA,cAAU,MAAM,SAAS;AACzB,MAAE,eAAe;AAAA,EACnB,GAAG,CAAC,CAAC;AAEL,EAAAD,WAAU,MAAM;AACd,UAAM,gBAAgB,CAAC,MAAkB;AACvC,YAAM,MAAM,YAAY;AACxB,UAAI,CAAC,KAAK,OAAQ;AAClB,YAAM,YAAY,mBAAmB;AACrC,UAAI,CAAC,UAAW;AAEhB,gBAAU,aAAa,IAAI,WAAW,EAAE,UAAU,IAAI;AACtD,gBAAU,YAAY,IAAI,WAAW,EAAE,UAAU,IAAI;AAAA,IACvD;AAEA,UAAM,eAAe,MAAM;AACzB,UAAI,CAAC,YAAY,SAAS,OAAQ;AAClC,kBAAY,UAAU;AACtB,YAAM,YAAY,mBAAmB;AACrC,UAAI,UAAW,WAAU,MAAM,SAAS;AAAA,IAC1C;AAEA,WAAO,iBAAiB,aAAa,aAAa;AAClD,WAAO,iBAAiB,WAAW,YAAY;AAC/C,WAAO,MAAM;AACX,aAAO,oBAAoB,aAAa,aAAa;AACrD,aAAO,oBAAoB,WAAW,YAAY;AAAA,IACpD;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SACE,gBAAAL,KAAC,SAAI,WAAU,mBACb,0BAAAC;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL,WAAU;AAAA,MACV,OAAO,EAAE,QAAQ,GAAG,eAAe,MAAM,QAAQ,OAAO;AAAA,MACxD,aAAa;AAAA,MAGb;AAAA,wBAAAD,KAAC,SAAI,WAAU,sBAAqB,OAAO,EAAE,OAAO,GAAG,SAAS,KAAK,GACnE,0BAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAM;AAAA,YACN;AAAA,YACA;AAAA;AAAA,QACF,GACF;AAAA,QAGA,gBAAAC;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,OAAO;AAAA,cACL,UAAU;AAAA,cACV,OAAO,GAAG,SAAS;AAAA,YACrB;AAAA,YAEA;AAAA,8BAAAD;AAAA,gBAAC;AAAA;AAAA,kBACC;AAAA,kBACA;AAAA,kBACA,aAAa;AAAA;AAAA,cACf;AAAA,cAEC,gBAAgB,gBAAAA,KAAC,0BAAe,YAAwB,UAAoB;AAAA,cAG7E,gBAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC;AAAA,kBACA;AAAA,kBACA;AAAA,kBACA;AAAA,kBACA;AAAA,kBACA,eAAe;AAAA;AAAA,cACjB;AAAA,cAEC,kBACC,gBAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,YAAY,eAAe;AAAA,kBAC3B,UAAU,eAAe;AAAA,kBACzB,MAAM,eAAe;AAAA,kBACrB,OAAO,eAAe;AAAA,kBACtB,aAAa;AAAA;AAAA,cACf;AAAA,cAGD,MAAM,IAAI,CAAC,MAAM,UAChB,gBAAAA;AAAA,gBAAC;AAAA;AAAA,kBAEC;AAAA,kBACA;AAAA,kBACA;AAAA,kBACA;AAAA,kBACA,UAAU;AAAA,kBACV,mBAAmB,CAAC,UAAU;AAC5B,wBAAI,MAAM,YAAY;AACpB,wCAAkB,KAAK;AACvB,6CAAuB,EAAE,QAAQ,KAAK,IAAI,MAAM,MAAM,MAAM,OAAO,MAAM,MAAM,CAAC;AAAA,oBAClF,OAAO;AACL,wCAAkB,IAAI;AACtB,6CAAuB,IAAI;AAAA,oBAC7B;AAAA,kBACF;AAAA,kBACA,UAAU;AAAA,kBACV,UAAU;AAAA,kBACV,oBAAoB,sBAAsB;AAAA,kBAC1C,oBAAoB,sBAAsB;AAAA,kBAC1C,kBAAkB,iBAAiB,IAAI,KAAK,EAAE;AAAA,kBAC9C,mBAAmB;AAAA,kBACnB,WAAW;AAAA;AAAA,gBArBN,KAAK;AAAA,cAsBZ,CACD;AAAA;AAAA;AAAA,QACH;AAAA;AAAA;AAAA,EACF,GACF;AAEJ;","names":["useMemo","useCallback","useRef","useState","useEffect","isWeekend","React","useMemo","isWeekend","getUTCDayDifference","jsx","jsxs","React","useMemo","useMemo","jsx","useMemo","React","useMemo","jsx","jsxs","arePropsEqual","React","useMemo","jsx","jsxs","React","useMemo","jsx","jsxs","React","useMemo","lines","jsx","jsxs","useRef","useMemo","useState","useEffect","useCallback"]}
package/dist/styles.css CHANGED
@@ -20,6 +20,11 @@
20
20
  --gantt-week-separator-color: #f3f4f6;
21
21
  --gantt-day-line-width: 1px;
22
22
  --gantt-day-line-color: #f3f4f6;
23
+ --gantt-progress-color: rgba(0, 0, 0, 0.2);
24
+ --gantt-progress-completed: #fbbf24;
25
+ --gantt-progress-accepted: #22c55e;
26
+ --gantt-dependency-line-color: rgba(0, 0, 55, 0.8);
27
+ --gantt-dependency-cycle-color: #ef4444;
23
28
  }
24
29
  .gantt-container {
25
30
  width: 100%;
@@ -347,7 +352,7 @@
347
352
  padding: 0 0.5rem;
348
353
  box-sizing: border-box;
349
354
  white-space: nowrap;
350
- overflow: visible;
355
+ overflow: hidden;
351
356
  cursor: grab;
352
357
  }
353
358
  .gantt-tr-taskBar:hover {
@@ -358,15 +363,6 @@
358
363
  opacity: 1;
359
364
  transition: none !important;
360
365
  }
361
- .gantt-tr-taskName {
362
- color: var(--gantt-task-bar-text-color);
363
- font-size: 0.875rem;
364
- font-weight: 500;
365
- white-space: nowrap;
366
- overflow: hidden;
367
- text-overflow: ellipsis;
368
- max-width: 100%;
369
- }
370
366
  .gantt-tr-taskDuration {
371
367
  color: var(--gantt-task-bar-text-color);
372
368
  font-size: 0.875rem;
@@ -374,9 +370,6 @@
374
370
  white-space: nowrap;
375
371
  margin-right: 4px;
376
372
  }
377
- .gantt-tr-taskNameHidden {
378
- visibility: hidden;
379
- }
380
373
  .gantt-tr-rightLabels {
381
374
  position: absolute;
382
375
  top: 50%;
@@ -405,6 +398,8 @@
405
398
  height: 100%;
406
399
  background-color: rgba(0, 0, 0, 0.1);
407
400
  cursor: ew-resize;
401
+ pointer-events: auto;
402
+ z-index: 10;
408
403
  }
409
404
  .gantt-tr-resizeHandleLeft {
410
405
  left: 0;
@@ -447,6 +442,23 @@
447
442
  top: 50%;
448
443
  transform: translateY(-50%);
449
444
  }
445
+ .gantt-tr-progressBar {
446
+ position: absolute;
447
+ top: 0;
448
+ left: 0;
449
+ bottom: 0;
450
+ z-index: 1;
451
+ pointer-events: none;
452
+ border-radius: var(--gantt-task-bar-border-radius) 0 0 var(--gantt-task-bar-border-radius);
453
+ transition: width 0.3s ease;
454
+ }
455
+ .gantt-tr-taskDuration {
456
+ position: relative;
457
+ z-index: 2;
458
+ }
459
+ .gantt-tr-taskBar.gantt-tr-dragging .gantt-tr-progressBar {
460
+ transition: none !important;
461
+ }
450
462
 
451
463
  /* src/components/TodayIndicator/TodayIndicator.css */
452
464
  .gantt-ti-indicator {
@@ -501,6 +513,32 @@
501
513
  opacity: 0.6;
502
514
  }
503
515
 
516
+ /* src/components/DependencyLines/DependencyLines.css */
517
+ .gantt-dependencies-svg {
518
+ position: absolute;
519
+ top: 0;
520
+ left: 0;
521
+ z-index: 5;
522
+ pointer-events: none;
523
+ overflow: visible;
524
+ }
525
+ .gantt-dependency-path {
526
+ fill: none;
527
+ stroke: var(--gantt-dependency-line-color, #666666);
528
+ stroke-width: 1;
529
+ stroke-linecap: round;
530
+ stroke-linejoin: round;
531
+ }
532
+ .gantt-dependency-cycle {
533
+ stroke: var(--gantt-dependency-cycle-color, #ef4444);
534
+ }
535
+ .gantt-dependency-arrow polygon {
536
+ fill: var(--gantt-dependency-line-color, #666666);
537
+ }
538
+ .gantt-dependency-arrow-cycle polygon {
539
+ fill: var(--gantt-dependency-cycle-color, #ef4444);
540
+ }
541
+
504
542
  /* src/components/GanttChart/GanttChart.css */
505
543
  .gantt-container {
506
544
  width: 100%;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gantt-lib",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "Lightweight React Gantt chart component library",
5
5
  "license": "MIT",
6
6
  "repository": {