@comitor/ui 3.12.1 → 3.13.0
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.
- package/dist/components/composite/data-table.d.ts +2 -5
- package/dist/components/composite/data-table.js +9 -33
- package/dist/components/composite/data-table.js.map +1 -1
- package/dist/components/composite/form-section.js +5 -5
- package/dist/components/composite/form-section.js.map +1 -1
- package/dist/components/composite/gantt.js +47 -28
- package/dist/components/composite/gantt.js.map +1 -1
- package/dist/components/composite/inline-edit.js +33 -38
- package/dist/components/composite/inline-edit.js.map +1 -1
- package/dist/components/composite/search-filter-bar.js +2 -1
- package/dist/components/composite/search-filter-bar.js.map +1 -1
- package/dist/components/composite/status-config.d.ts +17 -15
- package/dist/components/composite/status-config.js +6 -2
- package/dist/components/composite/status-config.js.map +1 -1
- package/dist/components/ui/breadcrumb.js +1 -4
- package/dist/components/ui/breadcrumb.js.map +1 -1
- package/dist/components/ui/button.js +7 -7
- package/dist/components/ui/button.js.map +1 -1
- package/dist/components/ui/circular-progress.d.ts +9 -4
- package/dist/components/ui/circular-progress.js +58 -24
- package/dist/components/ui/circular-progress.js.map +1 -1
- package/dist/components/ui/progress.d.ts +6 -0
- package/dist/components/ui/progress.js +2 -2
- package/dist/components/ui/progress.js.map +1 -1
- package/dist/components/ui/table.d.ts +7 -13
- package/dist/components/ui/table.js +1 -1
- package/dist/components/ui/table.js.map +1 -1
- package/dist/components/ui/tabs-variants.js +2 -2
- package/dist/components/ui/tabs-variants.js.map +1 -1
- package/dist/components/ui/timeline.js +19 -14
- package/dist/components/ui/timeline.js.map +1 -1
- package/dist/i18n/namespaces/file-upload.js +5 -1
- package/dist/i18n/namespaces/file-upload.js.map +1 -1
- package/dist/i18n/namespaces/image-upload-field.js +10 -0
- package/dist/i18n/namespaces/image-upload-field.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/uploader/file-upload-constants.d.ts +26 -2
- package/dist/uploader/file-upload-constants.js.map +1 -1
- package/dist/uploader/file-upload.d.ts +18 -1
- package/dist/uploader/file-upload.js +50 -5
- package/dist/uploader/file-upload.js.map +1 -1
- package/dist/uploader/image-upload-field.d.ts +31 -2
- package/dist/uploader/image-upload-field.js +139 -8
- package/dist/uploader/image-upload-field.js.map +1 -1
- package/dist/uploader/index.d.ts +2 -2
- package/dist/uploader/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/composite/gantt.tsx"],"sourcesContent":["\"use client\";\n\nimport { useVirtualizer } from \"@tanstack/react-virtual\";\nimport { cva } from \"class-variance-authority\";\nimport { ChevronDown, ChevronLeft, ChevronRight } from \"lucide-react\";\nimport * as React from \"react\";\nimport { useComitorLabels, useComitorLocale } from \"../../i18n/locale-provider.js\";\nimport { cn } from \"../../lib/cn.js\";\nimport { useIsomorphicLayoutEffect } from \"../../lib/use-isomorphic-layout-effect.js\";\nimport { Button } from \"../ui/button.js\";\nimport { ToggleGroup, ToggleGroupItem } from \"../ui/toggle-group.js\";\nimport {\n GANTT_SCALES,\n type GanttDependency,\n type GanttDependencyDraft,\n type GanttDependencyType,\n type GanttItem,\n type GanttItemChange,\n type GanttLabels,\n type GanttScale,\n type GanttTone\n} from \"./gantt-constants.js\";\nimport {\n type BarGeometry,\n computeRange,\n dayToLocalDate,\n formatDay,\n isDependencyViolated,\n parseDay,\n routeDependency,\n SCALE_CONFIG,\n shiftByUnit,\n ticksBetween,\n todayDay,\n weekday\n} from \"./gantt-utils.js\";\n\nexport interface GanttProps extends Omit<React.ComponentProps<\"div\">, \"children\" | \"onChange\"> {\n /**\n * Các dòng, ĐÚNG thứ tự hiển thị. Gantt không tự sắp, không tự ẩn con của nhánh đang thu: app giữ\n * cây, lọc theo `expanded`, rồi đưa danh sách phẳng vào đây (xem `onExpandedChange`).\n */\n items: GanttItem[];\n /** Mũi tên phụ thuộc. Phụ thuộc trỏ tới việc không có trong `items` (hoặc chưa có ngày) thì bỏ qua. */\n dependencies?: GanttDependency[];\n /** Bậc phóng (controlled). Bỏ trống thì Gantt tự giữ, bắt đầu từ `defaultScale`. */\n scale?: GanttScale;\n /** Bậc phóng ban đầu khi không controlled (mặc định `week`). */\n defaultScale?: GanttScale;\n onScaleChange?: (scale: GanttScale) => void;\n /** Hôm nay `yyyy-MM-dd` (mặc định: theo đồng hồ máy người dùng). Truyền khi cần khớp giờ server. */\n today?: string;\n /** Các thứ nghỉ trong tuần, 0 = Chủ nhật … 6 = Thứ bảy (mặc định `[0, 6]`). */\n weekendDays?: number[];\n /** Ngày nghỉ lễ / nghỉ bù của workspace, `yyyy-MM-dd`. Tô như cuối tuần. */\n nonWorkingDays?: string[];\n /** Ngày đầu tuần: 1 = thứ hai (mặc định, ISO), 0 = chủ nhật. */\n weekStartsOn?: 0 | 1;\n /**\n * Dải ngày cố định của trục. Bỏ trống thì trục tự bao mọi thanh + hôm nay, nới thêm hai đầu theo bậc.\n */\n range?: { start: string; end: string };\n /** Khoá mọi thao tác sửa (kéo, giãn, phím, nối phụ thuộc). Dòng nào cần khoá riêng: `item.readOnly`. */\n readOnly?: boolean;\n /**\n * Người dùng đổi ngày (kéo, giãn, phím mũi tên, bấm xếp lịch). Gantt KHÔNG tự ghi: app cập nhật dữ\n * liệu rồi truyền `items` mới. Không truyền = không sửa được.\n */\n onItemChange?: (change: GanttItemChange) => void;\n /** Mở một mục: Enter trên dòng, bấm đúp thanh, bấm tên ở cột trái. */\n onItemOpen?: (item: GanttItem) => void;\n /** Bấm nút mở/thu (hoặc Space) trên dòng có `hasChildren`. */\n onExpandedChange?: (id: string, expanded: boolean) => void;\n /**\n * Người dùng kéo từ chấm ở đuôi một thanh sang thanh khác. Truyền mới hiện chấm nối. Chỉ có đường\n * CHUỘT/CHẠM — app phải có thêm một lối bàn phím (menu \"Thêm phụ thuộc\") cho cùng việc.\n */\n onDependencyCreate?: (dependency: GanttDependencyDraft) => void;\n /** Bấm vào một mũi tên — vd mở menu xoá phụ thuộc. */\n onDependencyClick?: (dependency: GanttDependency) => void;\n /**\n * Chiều cao dòng, px. Mặc định 36 — và 44 trên thiết bị chạm (`pointer: coarse`), để thanh và tay\n * giãn đạt vùng chạm 44px.\n */\n rowHeight?: number;\n /** Bề rộng cột trái, px (mặc định 280). */\n sidebarWidth?: number;\n /** Tiêu đề cột trái (mặc định: khoá `sidebarTitle`). */\n sidebarTitle?: React.ReactNode;\n /** Nội dung cột trái của một dòng — thay cho tên trần (vd thêm mã việc, avatar người phụ trách). */\n renderSidebarItem?: (item: GanttItem) => React.ReactNode;\n /**\n * Thanh công cụ phía trên (chọn bậc + nút Hôm nay). `false` = ẩn (app tự dựng bằng `GANTT_SCALES`);\n * truyền một node = giữ thanh mặc định và đặt node đó ở đầu bên phải.\n */\n toolbar?: boolean | React.ReactNode;\n /** Đè từng chữ (namespace `gantt` của `ComitorLocaleProvider`). */\n labels?: Partial<GanttLabels>;\n}\n\n/*\n * Màu thanh. Mỗi tone đi đủ ba vai trò của hợp đồng màu: `--x` tô thân, `--x-foreground` cho chữ\n * NẰM TRÊN thân, `--x-ink` cho viền — viền mới là thứ tách thanh khỏi nền trang (≥3:1, WCAG 1.4.11):\n * thân vàng `--primary` chỉ 1,73:1 với nền trắng, thân không viền là một thanh \"biến mất\" ở light.\n * `red` đi qua `--destructive` chứ không `--red` (xem CLAUDE.md: `--red` không phải màu nền có chữ).\n */\nconst ganttBarVariants = cva(\n \"absolute flex items-center overflow-hidden rounded-md border text-xs font-medium touch-none select-none\",\n {\n variants: {\n tone: {\n neutral: \"border-control-edge-strong bg-secondary text-secondary-foreground\",\n primary: \"border-primary-ink bg-primary text-primary-foreground\",\n blue: \"border-blue-ink bg-blue text-blue-foreground\",\n teal: \"border-teal-ink bg-teal text-teal-foreground\",\n green: \"border-green-ink bg-green text-green-foreground\",\n yellow: \"border-yellow-ink bg-yellow text-yellow-foreground\",\n orange: \"border-orange-ink bg-orange text-orange-foreground\",\n red: \"border-destructive-ink bg-destructive text-destructive-foreground\",\n purple: \"border-purple-ink bg-purple text-purple-foreground\",\n pink: \"border-pink-ink bg-pink text-pink-foreground\",\n navy: \"border-navy-ink bg-navy text-navy-foreground\"\n }\n },\n defaultVariants: { tone: \"neutral\" }\n }\n);\n\n/*\n * Dấu KHÔNG có chữ (mốc, thanh tổng, vạch tiến độ) đi bằng bậc `-ink`: chính nó là hình mang thông\n * tin, nên phải tự đạt 3:1 với nền trang — bậc tô nền của nhiều tone không đạt.\n */\nconst ganttMarkVariants = cva(\"\", {\n variants: {\n tone: {\n neutral: \"bg-foreground\",\n primary: \"bg-primary-ink\",\n blue: \"bg-blue-ink\",\n teal: \"bg-teal-ink\",\n green: \"bg-green-ink\",\n yellow: \"bg-yellow-ink\",\n orange: \"bg-orange-ink\",\n red: \"bg-destructive-ink\",\n purple: \"bg-purple-ink\",\n pink: \"bg-pink-ink\",\n navy: \"bg-navy-ink\"\n }\n },\n defaultVariants: { tone: \"neutral\" }\n});\n\nconst HEADER_ROW = 26;\n/** Dưới bề rộng này chữ không vừa trong thanh — tên đi ra bên phải thanh. */\nconst MIN_INNER_LABEL = 56;\n/** Thanh ngắn tới đâu vẫn còn chỗ để nắm (ở bậc năm một ngày chỉ 0,5px). */\nconst MIN_BAR = 6;\n/** Độ dài mặc định khi bấm xếp lịch cho việc chưa có ngày, theo bậc. */\nconst SCHEDULE_DAYS: Record<GanttScale, number> = { day: 1, week: 5, month: 14, quarter: 30, year: 90 };\n/** Vùng sát mép khung nhìn mà kéo vào thì trục tự cuộn. */\nconst EDGE_SCROLL = 40;\n\ninterface ScheduledRow {\n item: GanttItem;\n index: number;\n start: number | null;\n end: number | null;\n}\n\ntype DragMode = \"move\" | \"resize-start\" | \"resize-end\";\n\ninterface DragState {\n id: string;\n mode: DragMode;\n originX: number;\n originScrollLeft: number;\n start: number;\n end: number;\n moved: boolean;\n}\n\ninterface DraftDates {\n id: string;\n start: number;\n end: number;\n}\n\ninterface LinkDraft {\n from: string;\n /** Toạ độ con trỏ trong hệ toạ độ của TRỤC (px từ đầu trục, px từ dòng đầu). */\n x: number;\n y: number;\n target: string | null;\n}\n\nfunction useCoarsePointer(): boolean {\n const subscribe = React.useCallback((onChange: () => void) => {\n const mql = window.matchMedia(\"(pointer: coarse)\");\n mql.addEventListener(\"change\", onChange);\n return () => mql.removeEventListener(\"change\", onChange);\n }, []);\n return React.useSyncExternalStore(\n subscribe,\n () => window.matchMedia(\"(pointer: coarse)\").matches,\n () => false\n );\n}\n\n/**\n * Trục thời gian / Gantt: cột tên bên trái, thanh theo ngày bên phải, mũi tên phụ thuộc, vạch hôm nay,\n * tô ngày nghỉ, làm nổi đường găng. Dùng cho Gantt việc (bậc ngày/tuần) lẫn lộ trình dự án (bậc\n * tháng/quý/năm). Phần lộ trình mượn Timeline dự án của Linear; phần Gantt mức việc Linear KHÔNG có —\n * nó theo quy ước Gantt thông dụng (docs/gantt.md §1).\n *\n * ─ CONTROLLED ─\n * Gantt không giữ dữ liệu nghiệp vụ. Mọi thay đổi ngày đi ra qua `onItemChange`, mọi phụ thuộc mới\n * qua `onDependencyCreate`; app ghi rồi truyền `items`/`dependencies` mới. Trong lúc KÉO, thanh chạy\n * theo một bản nháp nội bộ và chỉ báo ra MỘT lần khi thả — app không nhận 60 lần gọi mỗi giây.\n *\n * ─ BÀN PHÍM ─ (một điểm dừng Tab cho cả lưới — dòng đang chọn)\n * ↑ ↓ Home End PageUp PageDown — chuyển dòng · ← → — dời một ngày · Shift+← → — dời một bước lớn\n * (tuần, hoặc tháng ở bậc tháng trở lên) · Alt+← → — đổi ngày kết thúc · Alt+Shift+← → — đổi ngày\n * bắt đầu · Enter — `onItemOpen` · Space — mở/thu nhánh.\n * Mỗi lần đổi bằng phím là một lần `onItemChange` (app tự gộp nếu cần), kèm lời xướng qua `aria-live`.\n *\n * ─ ẢO HOÁ ─\n * Dòng ảo hoá bằng `@tanstack/react-virtual`; ô của đầu trục, vạch lưới và mũi tên chỉ dựng trong\n * phần đang nhìn thấy — 1.000 dòng × nhiều năm ở bậc ngày vẫn chỉ vài chục dòng trong DOM. Phía\n * server chưa có khung để đo nên trả về đầu trục và 0 dòng (cùng hành vi `DataGrid`).\n */\nexport function Gantt({\n items,\n dependencies = [],\n scale: scaleProp,\n defaultScale = \"week\",\n onScaleChange,\n today: todayProp,\n weekendDays = [0, 6],\n nonWorkingDays,\n weekStartsOn = 1,\n range: rangeProp,\n readOnly = false,\n onItemChange,\n onItemOpen,\n onExpandedChange,\n onDependencyCreate,\n onDependencyClick,\n rowHeight: rowHeightProp,\n sidebarWidth = 280,\n sidebarTitle,\n renderSidebarItem,\n toolbar = true,\n labels,\n className,\n \"aria-label\": ariaLabel,\n ...props\n}: GanttProps) {\n const t = useComitorLabels(\"gantt\", labels);\n const { locale } = useComitorLocale();\n const coarse = useCoarsePointer();\n const rowHeight = rowHeightProp ?? (coarse ? 44 : 36);\n const barHeight = rowHeight - 12;\n\n const [scaleState, setScaleState] = React.useState<GanttScale>(defaultScale);\n const scale = scaleProp ?? scaleState;\n const setScale = React.useCallback(\n (next: GanttScale) => {\n if (scaleProp === undefined) setScaleState(next);\n onScaleChange?.(next);\n },\n [scaleProp, onScaleChange]\n );\n\n const config = SCALE_CONFIG[scale];\n const ppd = config.pxPerDay;\n const headerHeight = config.major ? HEADER_ROW * 2 : HEADER_ROW;\n const today = React.useMemo(() => parseDay(todayProp) ?? todayDay(), [todayProp]);\n const canEdit = !readOnly && !!onItemChange;\n\n // ── Dữ liệu → số ngày ──────────────────────────────────────────────────────────────────────────\n const rows = React.useMemo<ScheduledRow[]>(\n () =>\n items.map((item, index) => {\n const start = parseDay(item.start);\n let end = item.type === \"milestone\" ? start : parseDay(item.end);\n if (start != null && (end == null || end < start)) end = start;\n return { item, index, start: end == null ? null : start, end: start == null ? null : end };\n }),\n [items]\n );\n const indexById = React.useMemo(() => new Map(rows.map((row) => [row.item.id, row.index])), [rows]);\n\n const range = React.useMemo(() => {\n const days: number[] = [];\n for (const row of rows) {\n if (row.start != null && row.end != null) days.push(row.start, row.end);\n }\n const override = rangeProp ? { start: parseDay(rangeProp.start), end: parseDay(rangeProp.end) } : undefined;\n return computeRange(days, today, scale, weekStartsOn, override);\n }, [rows, today, scale, weekStartsOn, rangeProp]);\n\n const timelineWidth = Math.round((range.end - range.start) * ppd);\n const xOf = React.useCallback((day: number) => (day - range.start) * ppd, [range.start, ppd]);\n\n const nonWorking = React.useMemo(() => {\n const set = new Set<number>();\n for (const value of nonWorkingDays ?? []) {\n const day = parseDay(value);\n if (day != null) set.add(day);\n }\n return set;\n }, [nonWorkingDays]);\n const weekendSet = React.useMemo(() => new Set(weekendDays), [weekendDays]);\n const isOffDay = React.useCallback(\n (day: number) => weekendSet.has(weekday(day)) || nonWorking.has(day),\n [weekendSet, nonWorking]\n );\n\n // ── Định dạng ngày theo ngôn ngữ ─────────────────────────────────────────────────────────────────\n const fmt = React.useMemo(\n () => ({\n dayNumber: new Intl.DateTimeFormat(locale, { day: \"numeric\" }),\n weekdayNarrow: new Intl.DateTimeFormat(locale, { weekday: \"narrow\" }),\n dayMonth: new Intl.DateTimeFormat(locale, { day: \"numeric\", month: \"short\" }),\n monthShort: new Intl.DateTimeFormat(locale, { month: \"short\" }),\n monthYear: new Intl.DateTimeFormat(locale, { month: \"long\", year: \"numeric\" }),\n year: new Intl.DateTimeFormat(locale, { year: \"numeric\" }),\n full: new Intl.DateTimeFormat(locale, { day: \"numeric\", month: \"short\", year: \"numeric\" })\n }),\n [locale]\n );\n const formatFull = React.useCallback((day: number) => fmt.full.format(dayToLocalDate(day)), [fmt]);\n const formatShort = React.useCallback((day: number) => fmt.dayMonth.format(dayToLocalDate(day)), [fmt]);\n const describeRange = React.useCallback(\n (row: { item: GanttItem; start: number; end: number }) =>\n row.item.type === \"milestone\"\n ? t.milestoneDate(formatFull(row.start))\n : t.itemRange(formatFull(row.start), formatFull(row.end), row.end - row.start + 1),\n [t, formatFull]\n );\n\n // ── Cuộn, khung nhìn, ảo hoá ─────────────────────────────────────────────────────────────────────\n const scrollRef = React.useRef<HTMLDivElement>(null);\n const [viewport, setViewport] = React.useState({ left: 0, width: 0 });\n /*\n * Đọc THẲNG, không qua `requestAnimationFrame`: sự kiện cuộn vốn đã khớp nhịp khung hình, còn\n * rAF bị hoãn hẳn ở tab chạy nền — đo được: trục mở ở tab nền không bao giờ có vạch lưới.\n */\n const readViewport = React.useCallback(() => {\n const el = scrollRef.current;\n if (!el) return;\n setViewport((prev) =>\n prev.left === el.scrollLeft && prev.width === el.clientWidth\n ? prev\n : { left: el.scrollLeft, width: el.clientWidth }\n );\n }, []);\n\n useIsomorphicLayoutEffect(() => {\n const el = scrollRef.current;\n if (!el) return;\n readViewport();\n const observer = new ResizeObserver(readViewport);\n observer.observe(el);\n return () => observer.disconnect();\n }, [readViewport]);\n\n const virtualizer = useVirtualizer({\n count: rows.length,\n getScrollElement: () => scrollRef.current,\n estimateSize: () => rowHeight,\n overscan: 8,\n paddingStart: headerHeight,\n scrollPaddingStart: headerHeight\n });\n // biome-ignore lint/correctness/useExhaustiveDependencies: đo lại khi cỡ dòng/đầu trục đổi (chạm ⇄ chuột, đổi bậc)\n React.useEffect(() => {\n virtualizer.measure();\n }, [virtualizer, rowHeight, headerHeight]);\n const virtualRows = virtualizer.getVirtualItems();\n const firstVisible = virtualRows[0]?.index ?? 0;\n const lastVisible = virtualRows[virtualRows.length - 1]?.index ?? -1;\n\n /** Phần trục đang nhìn thấy (px từ đầu trục) — phần bị cột trái che không tính. */\n const visibleWidth = Math.max(0, viewport.width - sidebarWidth);\n const visible = { from: viewport.left, to: viewport.left + visibleWidth };\n\n /*\n * Giữ NGÀY ở giữa khung nhìn khi đổi bậc: bậc đổi thì `pxPerDay` đổi, cùng một `scrollLeft` trỏ\n * sang một ngày khác hẳn — người dùng phóng to ở tháng 10 rồi thấy mình đứng ở tháng 3.\n */\n const anchorRef = React.useRef<{ day: number } | null>(null);\n const placedRef = React.useRef(false);\n const rangeRef = React.useRef(range);\n rangeRef.current = range;\n const ppdRef = React.useRef(ppd);\n\n const scrollToDay = React.useCallback(\n (day: number, behavior: ScrollBehavior = \"auto\", ratio = 0.5) => {\n const el = scrollRef.current;\n if (!el) return;\n const width = Math.max(0, el.clientWidth - sidebarWidth);\n el.scrollTo({ left: Math.max(0, xOf(day) - width * ratio), behavior });\n },\n [xOf, sidebarWidth]\n );\n\n useIsomorphicLayoutEffect(() => {\n const el = scrollRef.current;\n if (!el || el.clientWidth === 0) return;\n if (!placedRef.current) {\n // Lần đầu: hôm nay đứng ở 1/3 khung — phía trước thường quan trọng hơn phía sau.\n placedRef.current = true;\n scrollToDay(today, \"auto\", 1 / 3);\n } else if (anchorRef.current) {\n scrollToDay(anchorRef.current.day);\n }\n anchorRef.current = null;\n ppdRef.current = ppd;\n readViewport();\n }, [ppd, viewport.width > 0]);\n\n const changeScale = (next: GanttScale) => {\n const el = scrollRef.current;\n if (el && next !== scale) {\n const center = el.scrollLeft + Math.max(0, el.clientWidth - sidebarWidth) / 2;\n anchorRef.current = { day: rangeRef.current.start + center / ppdRef.current };\n }\n setScale(next);\n };\n\n // ── Kéo / giãn ───────────────────────────────────────────────────────────────────────────────────\n const [draft, setDraftState] = React.useState<DraftDates | null>(null);\n /*\n * Bản nháp đọc lại lúc THẢ đi qua ref, không qua updater của `setState`: React được phép gọi\n * updater hai lần (StrictMode làm thật), và báo `onItemChange` từ trong đó là báo hai lần — đo\n * được ở harness: một lần kéo, hai dòng nhật ký.\n */\n const draftRef = React.useRef<DraftDates | null>(null);\n const setDraft = React.useCallback((next: DraftDates | null) => {\n const prev = draftRef.current;\n if (prev === next || (prev && next && prev.id === next.id && prev.start === next.start && prev.end === next.end)) {\n return;\n }\n draftRef.current = next;\n setDraftState(next);\n }, []);\n const [linkDraft, setLinkDraft] = React.useState<LinkDraft | null>(null);\n const dragRef = React.useRef<DragState | null>(null);\n const onItemChangeRef = React.useRef(onItemChange);\n onItemChangeRef.current = onItemChange;\n\n const datesOf = (row: ScheduledRow): { start: number; end: number } | null => {\n if (draft && draft.id === row.item.id) return draft;\n if (row.start == null || row.end == null) return null;\n return { start: row.start, end: row.end };\n };\n\n const geometryOf = (row: ScheduledRow): (BarGeometry & { start: number; end: number }) | null => {\n const dates = datesOf(row);\n if (!dates) return null;\n const y = row.index * rowHeight + rowHeight / 2;\n if (row.item.type === \"milestone\") {\n const cx = xOf(dates.start) + ppd / 2;\n const half = barHeight / 2 - 5;\n return { x1: cx - half, x2: cx + half, y, ...dates };\n }\n const x1 = xOf(dates.start);\n const x2 = Math.max(xOf(dates.end + 1), x1 + MIN_BAR);\n return { x1, x2, y, ...dates };\n };\n\n /** Toạ độ con trỏ → toạ độ của trục (px từ đầu trục, px từ đỉnh dòng đầu). */\n const toTimeline = (clientX: number, clientY: number) => {\n const el = scrollRef.current;\n if (!el) return { x: 0, y: 0 };\n const rect = el.getBoundingClientRect();\n return {\n x: clientX - rect.left + el.scrollLeft - sidebarWidth,\n y: clientY - rect.top + el.scrollTop - headerHeight\n };\n };\n\n const autoScroll = (clientX: number) => {\n const el = scrollRef.current;\n if (!el) return;\n const rect = el.getBoundingClientRect();\n if (clientX < rect.left + sidebarWidth + EDGE_SCROLL) el.scrollLeft -= 12;\n else if (clientX > rect.right - EDGE_SCROLL) el.scrollLeft += 12;\n };\n\n const startDrag = (event: React.PointerEvent, row: ScheduledRow, mode: DragMode) => {\n if (event.button !== 0 || !canEdit || row.item.readOnly || row.item.type === \"summary\") return;\n const dates = datesOf(row);\n if (!dates) return;\n event.preventDefault();\n event.stopPropagation();\n focusRow(row.index, false);\n dragRef.current = {\n id: row.item.id,\n mode: row.item.type === \"milestone\" ? \"move\" : mode,\n originX: event.clientX,\n originScrollLeft: scrollRef.current?.scrollLeft ?? 0,\n start: dates.start,\n end: dates.end,\n moved: false\n };\n\n const onMove = (e: PointerEvent) => {\n const drag = dragRef.current;\n if (!drag) return;\n autoScroll(e.clientX);\n const scrolled = (scrollRef.current?.scrollLeft ?? 0) - drag.originScrollLeft;\n const dx = e.clientX - drag.originX + scrolled;\n if (Math.abs(dx) > 3) drag.moved = true;\n const delta = Math.round(dx / ppd);\n let start = drag.start;\n let end = drag.end;\n if (drag.mode === \"move\") {\n start += delta;\n end += delta;\n } else if (drag.mode === \"resize-start\") {\n start = Math.min(drag.start + delta, drag.end);\n } else {\n end = Math.max(drag.end + delta, drag.start);\n }\n setDraft({ id: drag.id, start, end });\n };\n\n const finish = (commit: boolean) => {\n window.removeEventListener(\"pointermove\", onMove);\n window.removeEventListener(\"pointerup\", onUp);\n window.removeEventListener(\"pointercancel\", onCancel);\n window.removeEventListener(\"keydown\", onKey, true);\n const drag = dragRef.current;\n dragRef.current = null;\n const current = draftRef.current;\n setDraft(null);\n if (commit && drag && current && (current.start !== drag.start || current.end !== drag.end)) {\n onItemChangeRef.current?.({\n id: drag.id,\n start: formatDay(current.start),\n end: formatDay(current.end),\n kind: drag.mode\n });\n }\n };\n const onUp = () => finish(true);\n const onCancel = () => finish(false);\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") {\n e.preventDefault();\n e.stopPropagation();\n finish(false);\n }\n };\n window.addEventListener(\"pointermove\", onMove);\n window.addEventListener(\"pointerup\", onUp);\n window.addEventListener(\"pointercancel\", onCancel);\n window.addEventListener(\"keydown\", onKey, true);\n };\n\n const startLink = (event: React.PointerEvent, row: ScheduledRow) => {\n if (event.button !== 0 || !onDependencyCreate) return;\n event.preventDefault();\n event.stopPropagation();\n const from = row.item.id;\n const update = (e: PointerEvent) => {\n autoScroll(e.clientX);\n const point = toTimeline(e.clientX, e.clientY);\n const index = Math.floor(point.y / rowHeight);\n const target = rows[index];\n const valid =\n target && target.item.id !== from && target.start != null && target.item.type !== \"summary\"\n ? target.item.id\n : null;\n setLinkDraft({ from, x: point.x, y: point.y, target: valid });\n };\n const end = (commit: boolean, e?: PointerEvent) => {\n window.removeEventListener(\"pointermove\", update);\n window.removeEventListener(\"pointerup\", onUp);\n window.removeEventListener(\"pointercancel\", onCancel);\n setLinkDraft(null);\n if (!commit || !e) return;\n const point = toTimeline(e.clientX, e.clientY);\n const target = rows[Math.floor(point.y / rowHeight)];\n if (target && target.item.id !== from && target.start != null && target.item.type !== \"summary\") {\n onDependencyCreate({ from, to: target.item.id, type: \"finish-to-start\" });\n }\n };\n const onUp = (e: PointerEvent) => end(true, e);\n const onCancel = () => end(false);\n window.addEventListener(\"pointermove\", update);\n window.addEventListener(\"pointerup\", onUp);\n window.addEventListener(\"pointercancel\", onCancel);\n update(event.nativeEvent);\n };\n\n // ── Dòng đang rê chuột: tô dòng + bóng \"bấm để xếp lịch\" ─────────────────────────────────────────\n const [hover, setHover] = React.useState<{ index: number; day: number } | null>(null);\n const onPointerMove = (event: React.PointerEvent) => {\n if (dragRef.current || event.pointerType === \"touch\") return;\n const point = toTimeline(event.clientX, event.clientY);\n const index = Math.floor(point.y / rowHeight);\n const inTrack = point.x >= viewport.left && point.y >= 0 && index < rows.length;\n const next = inTrack ? { index, day: range.start + Math.floor(point.x / ppd) } : null;\n setHover((prev) => (prev?.index === next?.index && prev?.day === next?.day ? prev : next));\n };\n\n const schedule = (row: ScheduledRow, day: number) => {\n if (!canEdit || row.item.readOnly) return;\n const length = row.item.type === \"milestone\" ? 1 : SCHEDULE_DAYS[scale];\n onItemChange?.({ id: row.item.id, start: formatDay(day), end: formatDay(day + length - 1), kind: \"schedule\" });\n };\n\n // ── Bàn phím ─────────────────────────────────────────────────────────────────────────────────────\n const [focusedIndex, setFocusedIndex] = React.useState(0);\n const pendingFocusRef = React.useRef<number | null>(null);\n const [announcement, setAnnouncement] = React.useState(\"\");\n const focusIndex = Math.min(focusedIndex, Math.max(0, rows.length - 1));\n\n const focusRow = (index: number, scroll = true) => {\n const clamped = Math.max(0, Math.min(rows.length - 1, index));\n setFocusedIndex(clamped);\n pendingFocusRef.current = clamped;\n if (scroll) virtualizer.scrollToIndex(clamped, { align: \"auto\" });\n };\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: chạy lại mỗi lần dòng ảo đổi để bắt kịp dòng vừa mount\n React.useEffect(() => {\n const index = pendingFocusRef.current;\n if (index == null) return;\n const el = scrollRef.current?.querySelector<HTMLElement>(`[data-gantt-row=\"${index}\"]`);\n if (el) {\n pendingFocusRef.current = null;\n if (document.activeElement !== el) el.focus({ preventScroll: true });\n }\n }, [focusIndex, virtualRows]);\n\n /** Kéo thanh vào khung nhìn theo chiều ngang (khi chọn dòng bằng phím, hoặc sau khi dời ngày). */\n const revealBar = (geometry: { x1: number; x2: number } | null) => {\n const el = scrollRef.current;\n if (!el || !geometry) return;\n const width = Math.max(0, el.clientWidth - sidebarWidth);\n if (geometry.x2 < el.scrollLeft + 24 || geometry.x1 > el.scrollLeft + width - 24) {\n el.scrollLeft = Math.max(0, geometry.x1 - Math.min(96, width / 4));\n }\n };\n\n const commitKeyboard = (row: ScheduledRow, start: number, end: number, kind: GanttItemChange[\"kind\"]) => {\n if (!onItemChange) return;\n onItemChange({ id: row.item.id, start: formatDay(start), end: formatDay(end), kind });\n const text = row.item.type === \"milestone\" ? formatFull(start) : `${formatFull(start)} – ${formatFull(end)}`;\n setAnnouncement(t.rescheduled(row.item.title, text));\n revealBar({ x1: xOf(start), x2: xOf(end + 1) });\n };\n\n const onRowKeyDown = (event: React.KeyboardEvent, row: ScheduledRow) => {\n if (event.target !== event.currentTarget) return;\n const page = Math.max(1, Math.floor((scrollRef.current?.clientHeight ?? 0) / rowHeight) - 1);\n switch (event.key) {\n case \"ArrowDown\":\n event.preventDefault();\n focusRow(row.index + 1);\n revealBar(geometryOf(rows[row.index + 1] ?? row));\n return;\n case \"ArrowUp\":\n event.preventDefault();\n focusRow(row.index - 1);\n revealBar(geometryOf(rows[row.index - 1] ?? row));\n return;\n case \"Home\":\n event.preventDefault();\n focusRow(0);\n return;\n case \"End\":\n event.preventDefault();\n focusRow(rows.length - 1);\n return;\n case \"PageDown\":\n event.preventDefault();\n focusRow(row.index + page);\n return;\n case \"PageUp\":\n event.preventDefault();\n focusRow(row.index - page);\n return;\n case \"Enter\":\n event.preventDefault();\n onItemOpen?.(row.item);\n return;\n case \" \":\n if (row.item.hasChildren && onExpandedChange) {\n event.preventDefault();\n onExpandedChange(row.item.id, !row.item.expanded);\n }\n return;\n case \"ArrowLeft\":\n case \"ArrowRight\": {\n if (!canEdit || row.item.readOnly || row.item.type === \"summary\") return;\n if (row.start == null || row.end == null) return;\n event.preventDefault();\n const direction = event.key === \"ArrowRight\" ? 1 : -1;\n const milestone = row.item.type === \"milestone\";\n if (event.altKey && !milestone) {\n const step = event.shiftKey ? \"resize-start\" : \"resize-end\";\n if (step === \"resize-end\") {\n commitKeyboard(row, row.start, Math.max(row.start, row.end + direction), step);\n } else {\n commitKeyboard(row, Math.min(row.end, row.start + direction), row.end, step);\n }\n return;\n }\n const next = event.shiftKey\n ? shiftByUnit(row.start, row.end, config.bigStep, direction)\n : { start: row.start + direction, end: row.end + direction };\n commitKeyboard(row, next.start, next.end, \"move\");\n return;\n }\n }\n };\n\n // ── Phụ thuộc: vi phạm + đường đi ───────────────────────────────────────────────────────────────\n const conflicted = React.useMemo(() => {\n const set = new Set<string>();\n for (const dep of dependencies) {\n const a = rows[indexById.get(dep.from) ?? -1];\n const b = rows[indexById.get(dep.to) ?? -1];\n if (!a || !b || a.start == null || a.end == null || b.start == null || b.end == null) continue;\n if (\n isDependencyViolated(\n { start: a.start, end: a.end },\n { start: b.start, end: b.end },\n dep.type ?? \"finish-to-start\"\n )\n ) {\n set.add(dep.to);\n }\n }\n return set;\n }, [dependencies, rows, indexById]);\n\n const dependencyPaths: Array<{ dep: GanttDependency; d: string; violated: boolean; label: string }> = [];\n for (const dep of dependencies) {\n const a = rows[indexById.get(dep.from) ?? -1];\n const b = rows[indexById.get(dep.to) ?? -1];\n if (!a || !b) continue;\n if (Math.max(a.index, b.index) < firstVisible || Math.min(a.index, b.index) > lastVisible) continue;\n const ga = geometryOf(a);\n const gb = geometryOf(b);\n if (!ga || !gb) continue;\n const type: GanttDependencyType = dep.type ?? \"finish-to-start\";\n dependencyPaths.push({\n dep,\n d: routeDependency(ga, gb, type, rowHeight),\n violated: isDependencyViolated(ga, gb, type),\n label: t.dependency(a.item.title, b.item.title)\n });\n }\n\n // ── Đầu trục + nền: chỉ dựng phần đang nhìn thấy ────────────────────────────────────────────────\n const overscanPx = 400;\n const visFrom = range.start + Math.max(0, Math.floor((visible.from - overscanPx) / ppd));\n const visTo = Math.min(range.end, range.start + Math.ceil((visible.to + overscanPx) / ppd));\n const minorTicks = viewport.width > 0 ? ticksBetween(visFrom, visTo, config.minor, weekStartsOn) : [];\n const majorTicks = viewport.width > 0 && config.major ? ticksBetween(visFrom, visTo, config.major, weekStartsOn) : [];\n\n const minorLabel = (day: number) => {\n const date = dayToLocalDate(day);\n switch (config.minor) {\n case \"day\":\n return fmt.dayNumber.format(date);\n case \"week\":\n return fmt.dayMonth.format(date);\n case \"month\":\n return fmt.monthShort.format(date);\n case \"quarter\":\n return t.quarter(Math.floor(date.getMonth() / 3) + 1, date.getFullYear());\n default:\n return fmt.year.format(date);\n }\n };\n const majorLabel = (day: number) =>\n config.major === \"month\" ? fmt.monthYear.format(dayToLocalDate(day)) : fmt.year.format(dayToLocalDate(day));\n\n /** Các khúc ngày nghỉ liền nhau trong phần đang nhìn thấy, gộp thành một dải. */\n const offBands: Array<{ start: number; end: number }> = [];\n if (config.shadeDays && viewport.width > 0) {\n for (let day = visFrom; day < visTo; day++) {\n if (!isOffDay(day)) continue;\n const last = offBands[offBands.length - 1];\n if (last && last.end === day) last.end = day + 1;\n else offBands.push({ start: day, end: day + 1 });\n }\n }\n\n const rowsHeight = rows.length * rowHeight;\n const totalHeight = headerHeight + rowsHeight;\n const totalWidth = sidebarWidth + timelineWidth;\n const todayX = xOf(today) + ppd / 2;\n const showToday = today >= range.start && today < range.end;\n const instructionsId = React.useId();\n\n const toolbarExtra = typeof toolbar === \"boolean\" ? null : toolbar;\n const scaleNames: Record<GanttScale, string> = {\n day: t.scaleDay,\n week: t.scaleWeek,\n month: t.scaleMonth,\n quarter: t.scaleQuarter,\n year: t.scaleYear\n };\n\n return (\n <div\n data-slot=\"gantt\"\n data-scale={scale}\n className={cn(\"flex min-h-0 flex-col overflow-hidden rounded-lg border bg-card text-card-foreground\", className)}\n {...props}\n >\n {toolbar !== false && (\n <div data-slot=\"gantt-toolbar\" className=\"flex flex-wrap items-center gap-2 border-b px-3 py-2\">\n <ToggleGroup\n type=\"single\"\n variant=\"outline\"\n size=\"sm\"\n value={scale}\n onValueChange={(value) => value && changeScale(value as GanttScale)}\n aria-label={t.scaleLabel}\n >\n {GANTT_SCALES.map((value) => (\n <ToggleGroupItem key={value} value={value} className=\"px-2.5 text-xs\">\n {scaleNames[value]}\n </ToggleGroupItem>\n ))}\n </ToggleGroup>\n <Button variant=\"outline\" size=\"sm\" onClick={() => scrollToDay(today, \"smooth\", 1 / 3)}>\n {t.today}\n </Button>\n {toolbarExtra && <div className=\"ms-auto flex items-center gap-2\">{toolbarExtra}</div>}\n </div>\n )}\n\n <div\n ref={scrollRef}\n data-slot=\"gantt-viewport\"\n className=\"relative min-h-0 flex-1 overflow-auto overscroll-contain\"\n onScroll={readViewport}\n onPointerMove={onPointerMove}\n onPointerLeave={() => setHover(null)}\n >\n <div\n role=\"treegrid\"\n aria-label={ariaLabel ?? t.label}\n aria-rowcount={rows.length + 1}\n aria-colcount={2}\n aria-describedby={instructionsId}\n aria-readonly={!canEdit || undefined}\n className=\"group/gantt relative\"\n style={{ width: totalWidth, height: totalHeight }}\n >\n {/* ── Đầu trục ─────────────────────────────────────────────────────────────────────── */}\n <div\n role=\"row\"\n aria-rowindex={1}\n tabIndex={-1}\n className=\"sticky top-0 z-40 flex\"\n style={{ height: headerHeight }}\n >\n <div\n role=\"columnheader\"\n tabIndex={-1}\n className=\"sticky left-0 z-10 flex items-end border-e border-b bg-card px-3 pb-1.5 font-medium text-muted-foreground text-xs\"\n style={{ width: sidebarWidth }}\n >\n {sidebarTitle ?? t.sidebarTitle}\n </div>\n <div\n role=\"columnheader\"\n tabIndex={-1}\n aria-label={t.label}\n className=\"relative border-b bg-card\"\n style={{ width: timelineWidth }}\n >\n {majorTicks.map((tick) => (\n <div\n key={`major-${tick.day}`}\n className=\"absolute top-0 border-s border-border\"\n style={{ left: xOf(tick.day), width: (tick.next - tick.day) * ppd, height: HEADER_ROW }}\n >\n <span\n className=\"sticky inline-block whitespace-nowrap px-2 pt-1.5 font-medium text-foreground text-xs\"\n style={{ left: sidebarWidth }}\n >\n {majorLabel(tick.day)}\n </span>\n </div>\n ))}\n {minorTicks.map((tick) => {\n const width = (tick.next - tick.day) * ppd;\n const isToday = config.minor === \"day\" && tick.day === today;\n return (\n <div\n key={`minor-${tick.day}`}\n title={formatFull(tick.day)}\n className={cn(\n \"absolute flex items-center border-s border-border text-muted-foreground text-xs tabular-nums\",\n config.minor === \"day\" ? \"justify-center\" : \"px-2\"\n )}\n style={{ left: xOf(tick.day), width, top: headerHeight - HEADER_ROW, height: HEADER_ROW }}\n >\n {isToday ? (\n <span className=\"rounded-sm bg-primary px-1 font-semibold text-primary-foreground\">\n {minorLabel(tick.day)}\n </span>\n ) : (\n <span className=\"truncate\">\n {config.minor === \"day\" && (\n <span className=\"me-0.5 text-[10px]\">\n {fmt.weekdayNarrow.format(dayToLocalDate(tick.day))}\n </span>\n )}\n {minorLabel(tick.day)}\n </span>\n )}\n </div>\n );\n })}\n {showToday && config.minor !== \"day\" && (\n <span\n aria-hidden=\"true\"\n className=\"absolute bottom-1 -translate-x-1/2 whitespace-nowrap rounded-sm bg-primary px-1 font-semibold text-[11px] text-primary-foreground\"\n style={{ left: todayX }}\n >\n {formatShort(today)}\n </span>\n )}\n </div>\n </div>\n\n {/* ── Nền: ngày nghỉ, vạch lưới, vạch hôm nay ─────────────────────────────────────────── */}\n <div\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute z-0\"\n style={{ top: headerHeight, left: sidebarWidth, width: timelineWidth, height: rowsHeight }}\n >\n {offBands.map((band) => (\n <div\n key={`off-${band.start}`}\n data-slot=\"gantt-off-day\"\n className=\"absolute inset-y-0 bg-muted\"\n style={{ left: xOf(band.start), width: (band.end - band.start) * ppd }}\n />\n ))}\n {minorTicks.map((tick) => (\n <div\n key={`line-${tick.day}`}\n className=\"absolute inset-y-0 border-s border-border\"\n style={{ left: xOf(tick.day) }}\n />\n ))}\n {showToday && (\n <div\n data-slot=\"gantt-today\"\n className=\"absolute inset-y-0 w-0.5 -translate-x-1/2 bg-primary-ink\"\n style={{ left: todayX }}\n />\n )}\n </div>\n\n {/* ── Mũi tên phụ thuộc — DƯỚI dòng, để thanh luôn đè lên mũi tên chứ không ngược lại ─── */}\n <svg\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute z-10 overflow-visible\"\n style={{ top: headerHeight, left: sidebarWidth }}\n width={timelineWidth}\n height={rowsHeight}\n >\n <defs>\n <marker\n id={`${instructionsId}-arrow`}\n viewBox=\"0 0 8 8\"\n refX=\"7\"\n refY=\"4\"\n markerWidth=\"7\"\n markerHeight=\"7\"\n orient=\"auto\"\n >\n <path d=\"M0,0 L8,4 L0,8 z\" className=\"fill-muted-foreground\" />\n </marker>\n <marker\n id={`${instructionsId}-arrow-alert`}\n viewBox=\"0 0 8 8\"\n refX=\"7\"\n refY=\"4\"\n markerWidth=\"7\"\n markerHeight=\"7\"\n orient=\"auto\"\n >\n <path d=\"M0,0 L8,4 L0,8 z\" className=\"fill-destructive-ink\" />\n </marker>\n </defs>\n {dependencyPaths.map(({ dep, d, violated, label }) => {\n const alert = violated || dep.critical;\n return (\n <g\n key={dep.id}\n data-slot=\"gantt-dependency\"\n data-critical={dep.critical || undefined}\n data-conflict={violated || undefined}\n >\n <title>{label}</title>\n <path\n d={d}\n fill=\"none\"\n strokeWidth={dep.critical ? 2 : 1.5}\n strokeDasharray={violated ? \"4 3\" : undefined}\n markerEnd={`url(#${instructionsId}-${alert ? \"arrow-alert\" : \"arrow\"})`}\n className={alert ? \"stroke-destructive-ink\" : \"stroke-muted-foreground\"}\n />\n {onDependencyClick && (\n // Vùng bấm rộng 12px dọc theo đường — nét 1,5px thì không ai bấm trúng.\n // biome-ignore lint/a11y/noStaticElementInteractions: mũi tên chỉ là lối tắt CHUỘT; app có lối bàn phím riêng\n <path\n d={d}\n fill=\"none\"\n stroke=\"transparent\"\n strokeWidth={12}\n className=\"pointer-events-auto cursor-pointer\"\n onClick={() => onDependencyClick(dep)}\n />\n )}\n </g>\n );\n })}\n {linkDraft &&\n (() => {\n const source = rows[indexById.get(linkDraft.from) ?? -1];\n const geometry = source ? geometryOf(source) : null;\n if (!geometry) return null;\n return (\n <path\n d={`M${geometry.x2},${geometry.y} L${linkDraft.x},${linkDraft.y}`}\n fill=\"none\"\n strokeWidth={1.5}\n strokeDasharray=\"4 3\"\n markerEnd={`url(#${instructionsId}-arrow)`}\n className=\"stroke-muted-foreground\"\n />\n );\n })()}\n </svg>\n\n {/* ── Dòng ────────────────────────────────────────────────────────────────────────────── */}\n {virtualRows.map((virtualRow) => {\n const row = rows[virtualRow.index];\n if (!row) return null;\n const { item } = row;\n const geometry = geometryOf(row);\n const tone: GanttTone = item.tone ?? \"neutral\";\n const depth = item.depth ?? 0;\n const editable = canEdit && !item.readOnly && item.type !== \"summary\";\n const isDragging = draft?.id === item.id;\n const isHovered = hover?.index === row.index;\n const isLinkTarget = linkDraft?.target === item.id;\n const conflict = conflicted.has(item.id);\n const description = [\n geometry ? describeRange({ item, start: geometry.start, end: geometry.end }) : t.unscheduled,\n item.progress != null ? t.progress(Math.round(item.progress)) : null,\n item.critical ? t.critical : null,\n conflict ? t.conflict : null\n ]\n .filter(Boolean)\n .join(\". \");\n\n return (\n <div\n key={item.id}\n role=\"row\"\n aria-rowindex={row.index + 2}\n aria-level={depth + 1}\n aria-expanded={item.hasChildren ? !!item.expanded : undefined}\n aria-selected={row.index === focusIndex}\n tabIndex={row.index === focusIndex ? 0 : -1}\n data-gantt-row={row.index}\n data-slot=\"gantt-row\"\n className=\"group/row pointer-events-none absolute left-0 z-20 flex outline-hidden focus-visible:outline-2 focus-visible:outline-solid focus-visible:-outline-offset-2 focus-visible:outline-ring\"\n style={{ top: virtualRow.start, height: rowHeight, width: totalWidth }}\n onKeyDown={(event) => onRowKeyDown(event, row)}\n onFocus={(event) => {\n setFocusedIndex(row.index);\n // Ô con (`tabIndex={-1}` theo luật lưới) cũng nhận focus khi bị BẤM — dồn về dòng,\n // nếu không phím mũi tên rơi vào ô và lưới im lặng.\n if (event.target !== event.currentTarget) event.currentTarget.focus({ preventScroll: true });\n }}\n >\n {/* Cột trái */}\n <div\n role=\"rowheader\"\n tabIndex={-1}\n className={cn(\n \"pointer-events-auto sticky left-0 z-10 flex min-w-0 items-center gap-1 border-e border-b bg-card pe-2\",\n isHovered && \"bg-muted\",\n // Dòng đang chọn chỉ tô khi lưới đang giữ focus — rời lưới thì vệt tô không còn\n // đứng đó như một lựa chọn thật.\n row.index === focusIndex && \"group-focus-within/gantt:bg-accent\"\n )}\n style={{ width: sidebarWidth, paddingInlineStart: 8 + depth * 16 }}\n >\n {item.hasChildren ? (\n <button\n type=\"button\"\n tabIndex={-1}\n aria-label={item.expanded ? t.collapse : t.expand}\n className=\"flex size-5 shrink-0 items-center justify-center rounded-sm text-muted-foreground hover:bg-accent hover:text-accent-foreground\"\n onClick={() => onExpandedChange?.(item.id, !item.expanded)}\n >\n {item.expanded ? (\n <ChevronDown aria-hidden=\"true\" className=\"size-3.5\" />\n ) : (\n <ChevronRight aria-hidden=\"true\" className=\"size-3.5 rtl:rotate-180\" />\n )}\n </button>\n ) : (\n <span aria-hidden=\"true\" className=\"w-5 shrink-0\" />\n )}\n {/* biome-ignore lint/a11y/noStaticElementInteractions: lối chuột; bàn phím đi Enter trên dòng */}\n {/* biome-ignore lint/a11y/useKeyWithClickEvents: như trên */}\n <div\n className={cn(\n \"min-w-0 flex-1 truncate text-foreground text-sm\",\n item.type === \"summary\" && \"font-semibold\",\n onItemOpen && \"cursor-pointer\"\n )}\n onClick={() => {\n focusRow(row.index, false);\n onItemOpen?.(item);\n }}\n >\n {renderSidebarItem ? renderSidebarItem(item) : item.title}\n </div>\n </div>\n\n {/* Trục */}\n <div\n role=\"gridcell\"\n tabIndex={-1}\n className={cn(\"relative border-b border-border\", isHovered && \"bg-muted/40\")}\n style={{ width: timelineWidth }}\n >\n <span className=\"sr-only\">{description}</span>\n\n {!geometry && editable && (\n // Dòng chưa có ngày: cả dải trục là một vùng bấm để xếp lịch ở ngày dưới con trỏ.\n <div\n aria-hidden=\"true\"\n className=\"pointer-events-auto absolute inset-0 cursor-copy\"\n onClick={(event) => {\n const point = toTimeline(event.clientX, event.clientY);\n schedule(row, range.start + Math.floor(point.x / ppd));\n }}\n >\n {isHovered && hover && (\n <div\n className=\"absolute flex items-center rounded-md border border-control-edge-strong border-dashed px-2 text-muted-foreground text-xs\"\n style={{\n left: xOf(hover.day),\n width: Math.max(SCHEDULE_DAYS[scale] * ppd, 96),\n top: (rowHeight - barHeight) / 2,\n height: barHeight\n }}\n >\n <span className=\"truncate\">{t.scheduleHint}</span>\n </div>\n )}\n </div>\n )}\n\n {geometry && item.type === \"milestone\" && (\n <div\n aria-hidden=\"true\"\n data-slot=\"gantt-milestone\"\n data-critical={item.critical || undefined}\n className={cn(\n \"pointer-events-auto absolute rotate-45 rounded-[2px]\",\n ganttMarkVariants({ tone }),\n item.critical && \"outline-2 outline-destructive-ink\",\n editable ? \"cursor-grab\" : \"cursor-default\",\n isDragging && \"cursor-grabbing shadow-overlay\"\n )}\n style={{\n left: geometry.x1,\n top: rowHeight / 2 - (geometry.x2 - geometry.x1) / 2,\n width: geometry.x2 - geometry.x1,\n height: geometry.x2 - geometry.x1\n }}\n onPointerDown={(event) => startDrag(event, row, \"move\")}\n onDoubleClick={() => onItemOpen?.(item)}\n />\n )}\n\n {geometry && item.type === \"summary\" && (\n <div\n aria-hidden=\"true\"\n data-slot=\"gantt-summary\"\n className={cn(\"absolute h-2 rounded-sm\", ganttMarkVariants({ tone }))}\n style={{ left: geometry.x1, width: geometry.x2 - geometry.x1, top: rowHeight / 2 - 4 }}\n />\n )}\n\n {geometry && (item.type ?? \"task\") === \"task\" && (\n <>\n <div\n aria-hidden=\"true\"\n data-slot=\"gantt-bar\"\n data-critical={item.critical || undefined}\n data-conflict={conflict || undefined}\n data-dragging={isDragging || undefined}\n className={cn(\n ganttBarVariants({ tone }),\n \"pointer-events-auto overflow-visible\",\n item.critical && \"border-2 border-destructive-ink\",\n isLinkTarget && \"outline-2 outline-offset-2 outline-ring\",\n editable ? \"cursor-grab\" : \"cursor-default\",\n isDragging && \"z-10 cursor-grabbing shadow-overlay\"\n )}\n style={{\n left: geometry.x1,\n width: geometry.x2 - geometry.x1,\n top: (rowHeight - barHeight) / 2,\n height: barHeight\n }}\n onPointerDown={(event) => startDrag(event, row, \"move\")}\n onDoubleClick={() => onItemOpen?.(item)}\n >\n {geometry.x2 - geometry.x1 >= MIN_INNER_LABEL && (\n <span className=\"min-w-0 truncate px-2\">{item.title}</span>\n )}\n {item.progress != null && item.progress > 0 && (\n <span\n className={cn(\n \"absolute bottom-0 left-0 h-[3px] rounded-bs-md\",\n ganttMarkVariants({ tone })\n )}\n style={{ width: `${Math.min(100, item.progress)}%` }}\n />\n )}\n {editable && (\n <>\n {/* Tay giãn: 8px trong thân ở chuột; 44px vắt qua mép ở thiết bị chạm. */}\n {!(coarse && geometry.x2 - geometry.x1 < 96) && (\n <span\n data-slot=\"gantt-handle\"\n className={cn(\n \"absolute inset-y-0 cursor-ew-resize\",\n coarse ? \"-start-[22px] w-11\" : \"start-0 w-2\"\n )}\n onPointerDown={(event) => startDrag(event, row, \"resize-start\")}\n />\n )}\n <span\n data-slot=\"gantt-handle\"\n className={cn(\n \"absolute inset-y-0 cursor-ew-resize\",\n coarse\n ? geometry.x2 - geometry.x1 < 96\n ? \"-end-11 w-11\"\n : \"-end-[22px] w-11\"\n : \"end-0 w-2\"\n )}\n onPointerDown={(event) => startDrag(event, row, \"resize-end\")}\n />\n </>\n )}\n {onDependencyCreate && editable && !coarse && (\n <span\n data-slot=\"gantt-link-handle\"\n title={t.linkHandle(item.title)}\n className=\"invisible absolute -end-4 top-1/2 size-2.5 -translate-y-1/2 cursor-crosshair rounded-full bg-primary-ink group-hover/row:visible\"\n onPointerDown={(event) => startLink(event, row)}\n />\n )}\n </div>\n {geometry.x2 - geometry.x1 < MIN_INNER_LABEL && (\n <span\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute flex items-center whitespace-nowrap text-foreground text-xs\"\n style={{\n left: geometry.x2 + (onDependencyCreate ? 20 : 6),\n top: 0,\n height: rowHeight\n }}\n >\n {item.title}\n </span>\n )}\n </>\n )}\n\n {/* Ngày hai đầu: hiện khi rê hoặc khi đang kéo — đọc được ngày mà không phải dò đầu trục. */}\n {geometry && item.type !== \"summary\" && (isHovered || isDragging) && (\n <>\n <span\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute flex -translate-x-full items-center whitespace-nowrap pe-1.5 text-[11px] text-muted-foreground tabular-nums\"\n style={{ left: geometry.x1, top: 0, height: rowHeight }}\n >\n {formatShort(geometry.start)}\n </span>\n {item.type !== \"milestone\" && geometry.x2 - geometry.x1 >= MIN_INNER_LABEL && (\n <span\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute flex items-center whitespace-nowrap ps-1.5 text-[11px] text-muted-foreground tabular-nums\"\n style={{ left: geometry.x2 + (onDependencyCreate ? 14 : 0), top: 0, height: rowHeight }}\n >\n {formatShort(geometry.end)}\n </span>\n )}\n </>\n )}\n\n {/* Thanh nằm ngoài khung nhìn: mũi tên ở mép đưa tới nó. */}\n {geometry && viewport.width > 0 && geometry.x2 < visible.from + 4 && (\n <button\n type=\"button\"\n tabIndex={-1}\n aria-label={t.scrollToItem(item.title)}\n className=\"pointer-events-auto absolute flex size-6 items-center justify-center rounded-full border border-input bg-card text-muted-foreground shadow-control hover:text-foreground\"\n style={{ left: visible.from + 6, top: rowHeight / 2 - 12 }}\n onClick={() => revealBar(geometry)}\n >\n <ChevronLeft aria-hidden=\"true\" className=\"size-3.5\" />\n </button>\n )}\n {geometry && viewport.width > 0 && geometry.x1 > visible.to - 4 && (\n <button\n type=\"button\"\n tabIndex={-1}\n aria-label={t.scrollToItem(item.title)}\n className=\"pointer-events-auto absolute flex size-6 items-center justify-center rounded-full border border-input bg-card text-muted-foreground shadow-control hover:text-foreground\"\n style={{ left: visible.to - 30, top: rowHeight / 2 - 12 }}\n onClick={() => revealBar(geometry)}\n >\n <ChevronRight aria-hidden=\"true\" className=\"size-3.5\" />\n </button>\n )}\n </div>\n </div>\n );\n })}\n </div>\n </div>\n\n <p id={instructionsId} className=\"sr-only\">\n {t.instructions}\n </p>\n <div role=\"status\" aria-live=\"polite\" className=\"sr-only\">\n {announcement}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/composite/gantt.tsx"],"sourcesContent":["\"use client\";\n\nimport { useVirtualizer } from \"@tanstack/react-virtual\";\nimport { cva } from \"class-variance-authority\";\nimport { ChevronDown, ChevronLeft, ChevronRight } from \"lucide-react\";\nimport * as React from \"react\";\nimport { useComitorLabels, useComitorLocale } from \"../../i18n/locale-provider.js\";\nimport { cn } from \"../../lib/cn.js\";\nimport { useIsomorphicLayoutEffect } from \"../../lib/use-isomorphic-layout-effect.js\";\nimport { Button } from \"../ui/button.js\";\nimport { ToggleGroup, ToggleGroupItem } from \"../ui/toggle-group.js\";\nimport {\n GANTT_SCALES,\n type GanttDependency,\n type GanttDependencyDraft,\n type GanttDependencyType,\n type GanttItem,\n type GanttItemChange,\n type GanttLabels,\n type GanttScale,\n type GanttTone\n} from \"./gantt-constants.js\";\nimport {\n type BarGeometry,\n computeRange,\n dayToLocalDate,\n formatDay,\n isDependencyViolated,\n parseDay,\n routeDependency,\n SCALE_CONFIG,\n shiftByUnit,\n ticksBetween,\n todayDay,\n weekday\n} from \"./gantt-utils.js\";\n\nexport interface GanttProps extends Omit<React.ComponentProps<\"div\">, \"children\" | \"onChange\"> {\n /**\n * Các dòng, ĐÚNG thứ tự hiển thị. Gantt không tự sắp, không tự ẩn con của nhánh đang thu: app giữ\n * cây, lọc theo `expanded`, rồi đưa danh sách phẳng vào đây (xem `onExpandedChange`).\n */\n items: GanttItem[];\n /** Mũi tên phụ thuộc. Phụ thuộc trỏ tới việc không có trong `items` (hoặc chưa có ngày) thì bỏ qua. */\n dependencies?: GanttDependency[];\n /** Bậc phóng (controlled). Bỏ trống thì Gantt tự giữ, bắt đầu từ `defaultScale`. */\n scale?: GanttScale;\n /** Bậc phóng ban đầu khi không controlled (mặc định `week`). */\n defaultScale?: GanttScale;\n onScaleChange?: (scale: GanttScale) => void;\n /** Hôm nay `yyyy-MM-dd` (mặc định: theo đồng hồ máy người dùng). Truyền khi cần khớp giờ server. */\n today?: string;\n /** Các thứ nghỉ trong tuần, 0 = Chủ nhật … 6 = Thứ bảy (mặc định `[0, 6]`). */\n weekendDays?: number[];\n /** Ngày nghỉ lễ / nghỉ bù của workspace, `yyyy-MM-dd`. Tô như cuối tuần. */\n nonWorkingDays?: string[];\n /** Ngày đầu tuần: 1 = thứ hai (mặc định, ISO), 0 = chủ nhật. */\n weekStartsOn?: 0 | 1;\n /**\n * Dải ngày cố định của trục. Bỏ trống thì trục tự bao mọi thanh + hôm nay, nới thêm hai đầu theo bậc.\n */\n range?: { start: string; end: string };\n /** Khoá mọi thao tác sửa (kéo, giãn, phím, nối phụ thuộc). Dòng nào cần khoá riêng: `item.readOnly`. */\n readOnly?: boolean;\n /**\n * Người dùng đổi ngày (kéo, giãn, phím mũi tên, bấm xếp lịch). Gantt KHÔNG tự ghi: app cập nhật dữ\n * liệu rồi truyền `items` mới. Không truyền = không sửa được.\n */\n onItemChange?: (change: GanttItemChange) => void;\n /** Mở một mục: Enter trên dòng, bấm đúp thanh, bấm tên ở cột trái. */\n onItemOpen?: (item: GanttItem) => void;\n /** Bấm nút mở/thu (hoặc Space) trên dòng có `hasChildren`. */\n onExpandedChange?: (id: string, expanded: boolean) => void;\n /**\n * Người dùng kéo từ chấm ở đuôi một thanh sang thanh khác. Truyền mới hiện chấm nối. Chỉ có đường\n * CHUỘT/CHẠM — app phải có thêm một lối bàn phím (menu \"Thêm phụ thuộc\") cho cùng việc.\n */\n onDependencyCreate?: (dependency: GanttDependencyDraft) => void;\n /** Bấm vào một mũi tên — vd mở menu xoá phụ thuộc. */\n onDependencyClick?: (dependency: GanttDependency) => void;\n /**\n * Chiều cao dòng, px. Mặc định 36 — và 44 trên thiết bị chạm (`pointer: coarse`), để thanh và tay\n * giãn đạt vùng chạm 44px.\n */\n rowHeight?: number;\n /** Bề rộng cột trái, px (mặc định 280). */\n sidebarWidth?: number;\n /** Tiêu đề cột trái (mặc định: khoá `sidebarTitle`). */\n sidebarTitle?: React.ReactNode;\n /** Nội dung cột trái của một dòng — thay cho tên trần (vd thêm mã việc, avatar người phụ trách). */\n renderSidebarItem?: (item: GanttItem) => React.ReactNode;\n /**\n * Thanh công cụ phía trên (chọn bậc + nút Hôm nay). `false` = ẩn (app tự dựng bằng `GANTT_SCALES`);\n * truyền một node = giữ thanh mặc định và đặt node đó ở đầu bên phải.\n */\n toolbar?: boolean | React.ReactNode;\n /** Đè từng chữ (namespace `gantt` của `ComitorLocaleProvider`). */\n labels?: Partial<GanttLabels>;\n}\n\n/*\n * Màu thanh — CÙNG bộ tint với `StatusPill` (`STATUS_TONES`) và avatar chữ/icon (`lib/avatar-tone.ts`):\n * nền `--x` pha 15% (`yellow` 25% / 20% ở dark), chữ `--x-ink`, viền `--x-ink` full — viền mới là thứ tách\n * thanh khỏi nền trang (≥3:1, WCAG 1.4.11), tint quá nhạt để tự làm việc đó.\n *\n * `primary` đi theo tone `gold` của avatar (`gold-100` / `gold-600`), KHÔNG theo `primary` của `StatusPill`\n * (`primary/20` + `primary-ink`): ở bảng màu mặc định `--primary-ink` CHÍNH LÀ vàng thương hiệu `#E8B824`, nên chữ\n * vàng trên tint vàng chỉ 1,55:1. Cái giá: thanh `primary` giữ màu vàng kể cả khi app đặt accent khác.\n *\n * Tint là LỚP ẢNH (`bg-linear-to-r from-x/15 to-x/15`) phủ trên nền ĐỤC `bg-card`, không phải `bg-x/15` trần:\n * thanh nằm đè lên cột cuối tuần, vạch hôm nay và lưới — một nền có alpha để chúng lộ xuyên qua, và thanh vắt\n * qua cuối tuần thành hai màu. Kết quả nhìn y hệt `bg-x/15` đặt trên thẻ `--card`. `bg-linear-to-r` nằm CÙNG\n * chuỗi với chốt màu của từng tone: để ở lớp gốc thì cổng tương phản không bóc được màu để đo.\n *\n * `red` đi qua `--destructive` chứ không `--red` (xem CLAUDE.md: `--red` không phải màu nền có chữ), như\n * tone `destructive` của `StatusPill`.\n */\nconst ganttBarVariants = cva(\n \"absolute flex items-center overflow-hidden rounded-md border bg-card text-xs font-medium touch-none select-none\",\n {\n variants: {\n tone: {\n neutral: \"bg-linear-to-r border-muted-foreground from-muted to-muted text-foreground\",\n primary:\n \"bg-linear-to-r border-gold-600 from-gold-100 to-gold-100 text-gold-600 dark:border-gold-100 dark:from-gold-500/30 dark:to-gold-500/30 dark:text-gold-100\",\n blue: \"bg-linear-to-r border-blue-ink from-blue/15 to-blue/15 text-blue-ink\",\n teal: \"bg-linear-to-r border-teal-ink from-teal/15 to-teal/15 text-teal-ink\",\n green: \"bg-linear-to-r border-green-ink from-green/15 to-green/15 text-green-ink\",\n yellow:\n \"bg-linear-to-r border-yellow-ink from-yellow/25 to-yellow/25 text-yellow-ink dark:from-yellow/20 dark:to-yellow/20\",\n orange: \"bg-linear-to-r border-orange-ink from-orange/15 to-orange/15 text-orange-ink\",\n red: \"bg-linear-to-r border-destructive-ink from-destructive/15 to-destructive/15 text-destructive-ink\",\n purple: \"bg-linear-to-r border-purple-ink from-purple/15 to-purple/15 text-purple-ink\",\n pink: \"bg-linear-to-r border-pink-ink from-pink/15 to-pink/15 text-pink-ink\",\n navy: \"bg-linear-to-r border-navy-ink from-navy/15 to-navy/15 text-navy-ink\"\n }\n },\n defaultVariants: { tone: \"neutral\" }\n }\n);\n\n/*\n * Dấu KHÔNG có chữ (mốc, thanh tổng, vạch tiến độ) đi bằng bậc `-ink`: chính nó là hình mang thông\n * tin, nên phải tự đạt 3:1 với nền trang — bậc tô nền của nhiều tone không đạt.\n */\nconst ganttMarkVariants = cva(\"\", {\n variants: {\n tone: {\n // `--muted-foreground`, không `--foreground`: thanh tổng và mốc đen kịt nặng hơn mọi thứ quanh nó. Vẫn qua\n // 3:1 của dấu phi văn bản ở mọi bảng (3,83:1 light mặc định · 4,94:1 dark · 5,23:1 tương phản cao).\n neutral: \"bg-muted-foreground\",\n primary: \"bg-primary-ink\",\n blue: \"bg-blue-ink\",\n teal: \"bg-teal-ink\",\n green: \"bg-green-ink\",\n yellow: \"bg-yellow-ink\",\n orange: \"bg-orange-ink\",\n red: \"bg-destructive-ink\",\n purple: \"bg-purple-ink\",\n pink: \"bg-pink-ink\",\n navy: \"bg-navy-ink\"\n }\n },\n defaultVariants: { tone: \"neutral\" }\n});\n\nconst HEADER_ROW = 26;\n/** Dưới bề rộng này chữ không vừa trong thanh — tên đi ra bên phải thanh. */\nconst MIN_INNER_LABEL = 56;\n/** Thanh ngắn tới đâu vẫn còn chỗ để nắm (ở bậc năm một ngày chỉ 0,5px). */\nconst MIN_BAR = 6;\n/** Độ dài mặc định khi bấm xếp lịch cho việc chưa có ngày, theo bậc. */\nconst SCHEDULE_DAYS: Record<GanttScale, number> = { day: 1, week: 5, month: 14, quarter: 30, year: 90 };\n/** Vùng sát mép khung nhìn mà kéo vào thì trục tự cuộn. */\nconst EDGE_SCROLL = 40;\n\ninterface ScheduledRow {\n item: GanttItem;\n index: number;\n start: number | null;\n end: number | null;\n}\n\ntype DragMode = \"move\" | \"resize-start\" | \"resize-end\";\n\ninterface DragState {\n id: string;\n mode: DragMode;\n originX: number;\n originScrollLeft: number;\n start: number;\n end: number;\n moved: boolean;\n}\n\ninterface DraftDates {\n id: string;\n start: number;\n end: number;\n}\n\ninterface LinkDraft {\n from: string;\n /** Toạ độ con trỏ trong hệ toạ độ của TRỤC (px từ đầu trục, px từ dòng đầu). */\n x: number;\n y: number;\n target: string | null;\n}\n\nfunction useCoarsePointer(): boolean {\n const subscribe = React.useCallback((onChange: () => void) => {\n const mql = window.matchMedia(\"(pointer: coarse)\");\n mql.addEventListener(\"change\", onChange);\n return () => mql.removeEventListener(\"change\", onChange);\n }, []);\n return React.useSyncExternalStore(\n subscribe,\n () => window.matchMedia(\"(pointer: coarse)\").matches,\n () => false\n );\n}\n\n/**\n * Trục thời gian / Gantt: cột tên bên trái, thanh theo ngày bên phải, mũi tên phụ thuộc, vạch hôm nay,\n * tô ngày nghỉ, làm nổi đường găng. Dùng cho Gantt việc (bậc ngày/tuần) lẫn lộ trình dự án (bậc\n * tháng/quý/năm). Phần lộ trình mượn Timeline dự án của Linear; phần Gantt mức việc Linear KHÔNG có —\n * nó theo quy ước Gantt thông dụng (docs/gantt.md §1).\n *\n * ─ CONTROLLED ─\n * Gantt không giữ dữ liệu nghiệp vụ. Mọi thay đổi ngày đi ra qua `onItemChange`, mọi phụ thuộc mới\n * qua `onDependencyCreate`; app ghi rồi truyền `items`/`dependencies` mới. Trong lúc KÉO, thanh chạy\n * theo một bản nháp nội bộ và chỉ báo ra MỘT lần khi thả — app không nhận 60 lần gọi mỗi giây.\n *\n * ─ BÀN PHÍM ─ (một điểm dừng Tab cho cả lưới — dòng đang chọn)\n * ↑ ↓ Home End PageUp PageDown — chuyển dòng · ← → — dời một ngày · Shift+← → — dời một bước lớn\n * (tuần, hoặc tháng ở bậc tháng trở lên) · Alt+← → — đổi ngày kết thúc · Alt+Shift+← → — đổi ngày\n * bắt đầu · Enter — `onItemOpen` · Space — mở/thu nhánh.\n * Mỗi lần đổi bằng phím là một lần `onItemChange` (app tự gộp nếu cần), kèm lời xướng qua `aria-live`.\n *\n * ─ ẢO HOÁ ─\n * Dòng ảo hoá bằng `@tanstack/react-virtual`; ô của đầu trục, vạch lưới và mũi tên chỉ dựng trong\n * phần đang nhìn thấy — 1.000 dòng × nhiều năm ở bậc ngày vẫn chỉ vài chục dòng trong DOM. Phía\n * server chưa có khung để đo nên trả về đầu trục và 0 dòng (cùng hành vi `DataGrid`).\n */\nexport function Gantt({\n items,\n dependencies = [],\n scale: scaleProp,\n defaultScale = \"week\",\n onScaleChange,\n today: todayProp,\n weekendDays = [0, 6],\n nonWorkingDays,\n weekStartsOn = 1,\n range: rangeProp,\n readOnly = false,\n onItemChange,\n onItemOpen,\n onExpandedChange,\n onDependencyCreate,\n onDependencyClick,\n rowHeight: rowHeightProp,\n sidebarWidth = 280,\n sidebarTitle,\n renderSidebarItem,\n toolbar = true,\n labels,\n className,\n \"aria-label\": ariaLabel,\n ...props\n}: GanttProps) {\n const t = useComitorLabels(\"gantt\", labels);\n const { locale } = useComitorLocale();\n const coarse = useCoarsePointer();\n const rowHeight = rowHeightProp ?? (coarse ? 44 : 36);\n const barHeight = rowHeight - 12;\n\n const [scaleState, setScaleState] = React.useState<GanttScale>(defaultScale);\n const scale = scaleProp ?? scaleState;\n const setScale = React.useCallback(\n (next: GanttScale) => {\n if (scaleProp === undefined) setScaleState(next);\n onScaleChange?.(next);\n },\n [scaleProp, onScaleChange]\n );\n\n const config = SCALE_CONFIG[scale];\n const ppd = config.pxPerDay;\n const headerHeight = config.major ? HEADER_ROW * 2 : HEADER_ROW;\n const today = React.useMemo(() => parseDay(todayProp) ?? todayDay(), [todayProp]);\n const canEdit = !readOnly && !!onItemChange;\n\n // ── Dữ liệu → số ngày ──────────────────────────────────────────────────────────────────────────\n const rows = React.useMemo<ScheduledRow[]>(\n () =>\n items.map((item, index) => {\n const start = parseDay(item.start);\n let end = item.type === \"milestone\" ? start : parseDay(item.end);\n if (start != null && (end == null || end < start)) end = start;\n return { item, index, start: end == null ? null : start, end: start == null ? null : end };\n }),\n [items]\n );\n const indexById = React.useMemo(() => new Map(rows.map((row) => [row.item.id, row.index])), [rows]);\n\n const range = React.useMemo(() => {\n const days: number[] = [];\n for (const row of rows) {\n if (row.start != null && row.end != null) days.push(row.start, row.end);\n }\n const override = rangeProp ? { start: parseDay(rangeProp.start), end: parseDay(rangeProp.end) } : undefined;\n return computeRange(days, today, scale, weekStartsOn, override);\n }, [rows, today, scale, weekStartsOn, rangeProp]);\n\n const timelineWidth = Math.round((range.end - range.start) * ppd);\n const xOf = React.useCallback((day: number) => (day - range.start) * ppd, [range.start, ppd]);\n\n const nonWorking = React.useMemo(() => {\n const set = new Set<number>();\n for (const value of nonWorkingDays ?? []) {\n const day = parseDay(value);\n if (day != null) set.add(day);\n }\n return set;\n }, [nonWorkingDays]);\n const weekendSet = React.useMemo(() => new Set(weekendDays), [weekendDays]);\n const isOffDay = React.useCallback(\n (day: number) => weekendSet.has(weekday(day)) || nonWorking.has(day),\n [weekendSet, nonWorking]\n );\n\n // ── Định dạng ngày theo ngôn ngữ ─────────────────────────────────────────────────────────────────\n const fmt = React.useMemo(\n () => ({\n dayNumber: new Intl.DateTimeFormat(locale, { day: \"numeric\" }),\n weekdayNarrow: new Intl.DateTimeFormat(locale, { weekday: \"narrow\" }),\n dayMonth: new Intl.DateTimeFormat(locale, { day: \"numeric\", month: \"short\" }),\n monthShort: new Intl.DateTimeFormat(locale, { month: \"short\" }),\n monthYear: new Intl.DateTimeFormat(locale, { month: \"long\", year: \"numeric\" }),\n year: new Intl.DateTimeFormat(locale, { year: \"numeric\" }),\n full: new Intl.DateTimeFormat(locale, { day: \"numeric\", month: \"short\", year: \"numeric\" })\n }),\n [locale]\n );\n const formatFull = React.useCallback((day: number) => fmt.full.format(dayToLocalDate(day)), [fmt]);\n const formatShort = React.useCallback((day: number) => fmt.dayMonth.format(dayToLocalDate(day)), [fmt]);\n const describeRange = React.useCallback(\n (row: { item: GanttItem; start: number; end: number }) =>\n row.item.type === \"milestone\"\n ? t.milestoneDate(formatFull(row.start))\n : t.itemRange(formatFull(row.start), formatFull(row.end), row.end - row.start + 1),\n [t, formatFull]\n );\n\n // ── Cuộn, khung nhìn, ảo hoá ─────────────────────────────────────────────────────────────────────\n const scrollRef = React.useRef<HTMLDivElement>(null);\n const [viewport, setViewport] = React.useState({ left: 0, width: 0 });\n /*\n * Đọc THẲNG, không qua `requestAnimationFrame`: sự kiện cuộn vốn đã khớp nhịp khung hình, còn\n * rAF bị hoãn hẳn ở tab chạy nền — đo được: trục mở ở tab nền không bao giờ có vạch lưới.\n */\n const readViewport = React.useCallback(() => {\n const el = scrollRef.current;\n if (!el) return;\n setViewport((prev) =>\n prev.left === el.scrollLeft && prev.width === el.clientWidth\n ? prev\n : { left: el.scrollLeft, width: el.clientWidth }\n );\n }, []);\n\n useIsomorphicLayoutEffect(() => {\n const el = scrollRef.current;\n if (!el) return;\n readViewport();\n const observer = new ResizeObserver(readViewport);\n observer.observe(el);\n return () => observer.disconnect();\n }, [readViewport]);\n\n const virtualizer = useVirtualizer({\n count: rows.length,\n getScrollElement: () => scrollRef.current,\n estimateSize: () => rowHeight,\n overscan: 8,\n paddingStart: headerHeight,\n scrollPaddingStart: headerHeight\n });\n // biome-ignore lint/correctness/useExhaustiveDependencies: đo lại khi cỡ dòng/đầu trục đổi (chạm ⇄ chuột, đổi bậc)\n React.useEffect(() => {\n virtualizer.measure();\n }, [virtualizer, rowHeight, headerHeight]);\n const virtualRows = virtualizer.getVirtualItems();\n const firstVisible = virtualRows[0]?.index ?? 0;\n const lastVisible = virtualRows[virtualRows.length - 1]?.index ?? -1;\n\n /** Phần trục đang nhìn thấy (px từ đầu trục) — phần bị cột trái che không tính. */\n const visibleWidth = Math.max(0, viewport.width - sidebarWidth);\n const visible = { from: viewport.left, to: viewport.left + visibleWidth };\n\n /*\n * Giữ NGÀY ở giữa khung nhìn khi đổi bậc: bậc đổi thì `pxPerDay` đổi, cùng một `scrollLeft` trỏ\n * sang một ngày khác hẳn — người dùng phóng to ở tháng 10 rồi thấy mình đứng ở tháng 3.\n */\n const anchorRef = React.useRef<{ day: number } | null>(null);\n const placedRef = React.useRef(false);\n const rangeRef = React.useRef(range);\n rangeRef.current = range;\n const ppdRef = React.useRef(ppd);\n\n const scrollToDay = React.useCallback(\n (day: number, behavior: ScrollBehavior = \"auto\", ratio = 0.5) => {\n const el = scrollRef.current;\n if (!el) return;\n const width = Math.max(0, el.clientWidth - sidebarWidth);\n el.scrollTo({ left: Math.max(0, xOf(day) - width * ratio), behavior });\n },\n [xOf, sidebarWidth]\n );\n\n useIsomorphicLayoutEffect(() => {\n const el = scrollRef.current;\n if (!el || el.clientWidth === 0) return;\n if (!placedRef.current) {\n // Lần đầu: hôm nay đứng ở 1/3 khung — phía trước thường quan trọng hơn phía sau.\n placedRef.current = true;\n scrollToDay(today, \"auto\", 1 / 3);\n } else if (anchorRef.current) {\n scrollToDay(anchorRef.current.day);\n }\n anchorRef.current = null;\n ppdRef.current = ppd;\n readViewport();\n }, [ppd, viewport.width > 0]);\n\n const changeScale = (next: GanttScale) => {\n const el = scrollRef.current;\n if (el && next !== scale) {\n const center = el.scrollLeft + Math.max(0, el.clientWidth - sidebarWidth) / 2;\n anchorRef.current = { day: rangeRef.current.start + center / ppdRef.current };\n }\n setScale(next);\n };\n\n // ── Kéo / giãn ───────────────────────────────────────────────────────────────────────────────────\n const [draft, setDraftState] = React.useState<DraftDates | null>(null);\n /*\n * Bản nháp đọc lại lúc THẢ đi qua ref, không qua updater của `setState`: React được phép gọi\n * updater hai lần (StrictMode làm thật), và báo `onItemChange` từ trong đó là báo hai lần — đo\n * được ở harness: một lần kéo, hai dòng nhật ký.\n */\n const draftRef = React.useRef<DraftDates | null>(null);\n const setDraft = React.useCallback((next: DraftDates | null) => {\n const prev = draftRef.current;\n if (prev === next || (prev && next && prev.id === next.id && prev.start === next.start && prev.end === next.end)) {\n return;\n }\n draftRef.current = next;\n setDraftState(next);\n }, []);\n const [linkDraft, setLinkDraft] = React.useState<LinkDraft | null>(null);\n const dragRef = React.useRef<DragState | null>(null);\n const onItemChangeRef = React.useRef(onItemChange);\n onItemChangeRef.current = onItemChange;\n\n const datesOf = (row: ScheduledRow): { start: number; end: number } | null => {\n if (draft && draft.id === row.item.id) return draft;\n if (row.start == null || row.end == null) return null;\n return { start: row.start, end: row.end };\n };\n\n const geometryOf = (row: ScheduledRow): (BarGeometry & { start: number; end: number }) | null => {\n const dates = datesOf(row);\n if (!dates) return null;\n const y = row.index * rowHeight + rowHeight / 2;\n if (row.item.type === \"milestone\") {\n const cx = xOf(dates.start) + ppd / 2;\n // Nửa cạnh ≈ 1/5 chiều cao thanh: thanh 24px (dòng 36px mặc định) → cạnh 10px, xoay 45° thì đường chéo\n // ~14px, kèm viền găng 1,5px vẫn gọn trong chiều cao thanh. Công thức cũ (`barHeight / 2 - 5`) cho cạnh\n // 14px — hình thoi ~20px, to hơn cả thanh bên cạnh.\n const half = Math.max(3, Math.round(barHeight / 5));\n return { x1: cx - half, x2: cx + half, y, ...dates };\n }\n const x1 = xOf(dates.start);\n const x2 = Math.max(xOf(dates.end + 1), x1 + MIN_BAR);\n return { x1, x2, y, ...dates };\n };\n\n /** Toạ độ con trỏ → toạ độ của trục (px từ đầu trục, px từ đỉnh dòng đầu). */\n const toTimeline = (clientX: number, clientY: number) => {\n const el = scrollRef.current;\n if (!el) return { x: 0, y: 0 };\n const rect = el.getBoundingClientRect();\n return {\n x: clientX - rect.left + el.scrollLeft - sidebarWidth,\n y: clientY - rect.top + el.scrollTop - headerHeight\n };\n };\n\n const autoScroll = (clientX: number) => {\n const el = scrollRef.current;\n if (!el) return;\n const rect = el.getBoundingClientRect();\n if (clientX < rect.left + sidebarWidth + EDGE_SCROLL) el.scrollLeft -= 12;\n else if (clientX > rect.right - EDGE_SCROLL) el.scrollLeft += 12;\n };\n\n const startDrag = (event: React.PointerEvent, row: ScheduledRow, mode: DragMode) => {\n if (event.button !== 0 || !canEdit || row.item.readOnly || row.item.type === \"summary\") return;\n const dates = datesOf(row);\n if (!dates) return;\n event.preventDefault();\n event.stopPropagation();\n focusRow(row.index, false);\n dragRef.current = {\n id: row.item.id,\n mode: row.item.type === \"milestone\" ? \"move\" : mode,\n originX: event.clientX,\n originScrollLeft: scrollRef.current?.scrollLeft ?? 0,\n start: dates.start,\n end: dates.end,\n moved: false\n };\n\n const onMove = (e: PointerEvent) => {\n const drag = dragRef.current;\n if (!drag) return;\n autoScroll(e.clientX);\n const scrolled = (scrollRef.current?.scrollLeft ?? 0) - drag.originScrollLeft;\n const dx = e.clientX - drag.originX + scrolled;\n if (Math.abs(dx) > 3) drag.moved = true;\n const delta = Math.round(dx / ppd);\n let start = drag.start;\n let end = drag.end;\n if (drag.mode === \"move\") {\n start += delta;\n end += delta;\n } else if (drag.mode === \"resize-start\") {\n start = Math.min(drag.start + delta, drag.end);\n } else {\n end = Math.max(drag.end + delta, drag.start);\n }\n setDraft({ id: drag.id, start, end });\n };\n\n const finish = (commit: boolean) => {\n window.removeEventListener(\"pointermove\", onMove);\n window.removeEventListener(\"pointerup\", onUp);\n window.removeEventListener(\"pointercancel\", onCancel);\n window.removeEventListener(\"keydown\", onKey, true);\n const drag = dragRef.current;\n dragRef.current = null;\n const current = draftRef.current;\n setDraft(null);\n if (commit && drag && current && (current.start !== drag.start || current.end !== drag.end)) {\n onItemChangeRef.current?.({\n id: drag.id,\n start: formatDay(current.start),\n end: formatDay(current.end),\n kind: drag.mode\n });\n }\n };\n const onUp = () => finish(true);\n const onCancel = () => finish(false);\n const onKey = (e: KeyboardEvent) => {\n if (e.key === \"Escape\") {\n e.preventDefault();\n e.stopPropagation();\n finish(false);\n }\n };\n window.addEventListener(\"pointermove\", onMove);\n window.addEventListener(\"pointerup\", onUp);\n window.addEventListener(\"pointercancel\", onCancel);\n window.addEventListener(\"keydown\", onKey, true);\n };\n\n const startLink = (event: React.PointerEvent, row: ScheduledRow) => {\n if (event.button !== 0 || !onDependencyCreate) return;\n event.preventDefault();\n event.stopPropagation();\n const from = row.item.id;\n const update = (e: PointerEvent) => {\n autoScroll(e.clientX);\n const point = toTimeline(e.clientX, e.clientY);\n const index = Math.floor(point.y / rowHeight);\n const target = rows[index];\n const valid =\n target && target.item.id !== from && target.start != null && target.item.type !== \"summary\"\n ? target.item.id\n : null;\n setLinkDraft({ from, x: point.x, y: point.y, target: valid });\n };\n const end = (commit: boolean, e?: PointerEvent) => {\n window.removeEventListener(\"pointermove\", update);\n window.removeEventListener(\"pointerup\", onUp);\n window.removeEventListener(\"pointercancel\", onCancel);\n setLinkDraft(null);\n if (!commit || !e) return;\n const point = toTimeline(e.clientX, e.clientY);\n const target = rows[Math.floor(point.y / rowHeight)];\n if (target && target.item.id !== from && target.start != null && target.item.type !== \"summary\") {\n onDependencyCreate({ from, to: target.item.id, type: \"finish-to-start\" });\n }\n };\n const onUp = (e: PointerEvent) => end(true, e);\n const onCancel = () => end(false);\n window.addEventListener(\"pointermove\", update);\n window.addEventListener(\"pointerup\", onUp);\n window.addEventListener(\"pointercancel\", onCancel);\n update(event.nativeEvent);\n };\n\n // ── Dòng đang rê chuột: tô dòng + bóng \"bấm để xếp lịch\" ─────────────────────────────────────────\n const [hover, setHover] = React.useState<{ index: number; day: number } | null>(null);\n const onPointerMove = (event: React.PointerEvent) => {\n if (dragRef.current || event.pointerType === \"touch\") return;\n const point = toTimeline(event.clientX, event.clientY);\n const index = Math.floor(point.y / rowHeight);\n const inTrack = point.x >= viewport.left && point.y >= 0 && index < rows.length;\n const next = inTrack ? { index, day: range.start + Math.floor(point.x / ppd) } : null;\n setHover((prev) => (prev?.index === next?.index && prev?.day === next?.day ? prev : next));\n };\n\n const schedule = (row: ScheduledRow, day: number) => {\n if (!canEdit || row.item.readOnly) return;\n const length = row.item.type === \"milestone\" ? 1 : SCHEDULE_DAYS[scale];\n onItemChange?.({ id: row.item.id, start: formatDay(day), end: formatDay(day + length - 1), kind: \"schedule\" });\n };\n\n // ── Bàn phím ─────────────────────────────────────────────────────────────────────────────────────\n const [focusedIndex, setFocusedIndex] = React.useState(0);\n const pendingFocusRef = React.useRef<number | null>(null);\n const [announcement, setAnnouncement] = React.useState(\"\");\n const focusIndex = Math.min(focusedIndex, Math.max(0, rows.length - 1));\n\n const focusRow = (index: number, scroll = true) => {\n const clamped = Math.max(0, Math.min(rows.length - 1, index));\n setFocusedIndex(clamped);\n pendingFocusRef.current = clamped;\n if (scroll) virtualizer.scrollToIndex(clamped, { align: \"auto\" });\n };\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: chạy lại mỗi lần dòng ảo đổi để bắt kịp dòng vừa mount\n React.useEffect(() => {\n const index = pendingFocusRef.current;\n if (index == null) return;\n const el = scrollRef.current?.querySelector<HTMLElement>(`[data-gantt-row=\"${index}\"]`);\n if (el) {\n pendingFocusRef.current = null;\n if (document.activeElement !== el) el.focus({ preventScroll: true });\n }\n }, [focusIndex, virtualRows]);\n\n /** Kéo thanh vào khung nhìn theo chiều ngang (khi chọn dòng bằng phím, hoặc sau khi dời ngày). */\n const revealBar = (geometry: { x1: number; x2: number } | null) => {\n const el = scrollRef.current;\n if (!el || !geometry) return;\n const width = Math.max(0, el.clientWidth - sidebarWidth);\n if (geometry.x2 < el.scrollLeft + 24 || geometry.x1 > el.scrollLeft + width - 24) {\n el.scrollLeft = Math.max(0, geometry.x1 - Math.min(96, width / 4));\n }\n };\n\n const commitKeyboard = (row: ScheduledRow, start: number, end: number, kind: GanttItemChange[\"kind\"]) => {\n if (!onItemChange) return;\n onItemChange({ id: row.item.id, start: formatDay(start), end: formatDay(end), kind });\n const text = row.item.type === \"milestone\" ? formatFull(start) : `${formatFull(start)} – ${formatFull(end)}`;\n setAnnouncement(t.rescheduled(row.item.title, text));\n revealBar({ x1: xOf(start), x2: xOf(end + 1) });\n };\n\n const onRowKeyDown = (event: React.KeyboardEvent, row: ScheduledRow) => {\n if (event.target !== event.currentTarget) return;\n const page = Math.max(1, Math.floor((scrollRef.current?.clientHeight ?? 0) / rowHeight) - 1);\n switch (event.key) {\n case \"ArrowDown\":\n event.preventDefault();\n focusRow(row.index + 1);\n revealBar(geometryOf(rows[row.index + 1] ?? row));\n return;\n case \"ArrowUp\":\n event.preventDefault();\n focusRow(row.index - 1);\n revealBar(geometryOf(rows[row.index - 1] ?? row));\n return;\n case \"Home\":\n event.preventDefault();\n focusRow(0);\n return;\n case \"End\":\n event.preventDefault();\n focusRow(rows.length - 1);\n return;\n case \"PageDown\":\n event.preventDefault();\n focusRow(row.index + page);\n return;\n case \"PageUp\":\n event.preventDefault();\n focusRow(row.index - page);\n return;\n case \"Enter\":\n event.preventDefault();\n onItemOpen?.(row.item);\n return;\n case \" \":\n if (row.item.hasChildren && onExpandedChange) {\n event.preventDefault();\n onExpandedChange(row.item.id, !row.item.expanded);\n }\n return;\n case \"ArrowLeft\":\n case \"ArrowRight\": {\n if (!canEdit || row.item.readOnly || row.item.type === \"summary\") return;\n if (row.start == null || row.end == null) return;\n event.preventDefault();\n const direction = event.key === \"ArrowRight\" ? 1 : -1;\n const milestone = row.item.type === \"milestone\";\n if (event.altKey && !milestone) {\n const step = event.shiftKey ? \"resize-start\" : \"resize-end\";\n if (step === \"resize-end\") {\n commitKeyboard(row, row.start, Math.max(row.start, row.end + direction), step);\n } else {\n commitKeyboard(row, Math.min(row.end, row.start + direction), row.end, step);\n }\n return;\n }\n const next = event.shiftKey\n ? shiftByUnit(row.start, row.end, config.bigStep, direction)\n : { start: row.start + direction, end: row.end + direction };\n commitKeyboard(row, next.start, next.end, \"move\");\n return;\n }\n }\n };\n\n // ── Phụ thuộc: vi phạm + đường đi ───────────────────────────────────────────────────────────────\n const conflicted = React.useMemo(() => {\n const set = new Set<string>();\n for (const dep of dependencies) {\n const a = rows[indexById.get(dep.from) ?? -1];\n const b = rows[indexById.get(dep.to) ?? -1];\n if (!a || !b || a.start == null || a.end == null || b.start == null || b.end == null) continue;\n if (\n isDependencyViolated(\n { start: a.start, end: a.end },\n { start: b.start, end: b.end },\n dep.type ?? \"finish-to-start\"\n )\n ) {\n set.add(dep.to);\n }\n }\n return set;\n }, [dependencies, rows, indexById]);\n\n // Mũi tên vào/ra MỐC phải chạm ĐỈNH hình thoi, không phải mép hộp vuông trước khi xoay: xoay 45° đẩy hai\n // đỉnh trái/phải ra xa thêm (√2 − 1) × nửa cạnh (~2px ở cạnh 10px), và đầu mũi tên cắm vào hình thoi đúng\n // chừng đó. Chỉ dùng để VẼ đường — phép kiểm vi phạm vẫn đọc hình học gốc.\n const arrowEnds = <G extends { x1: number; x2: number }>(row: ScheduledRow, g: G): G => {\n if (row.item.type !== \"milestone\") return g;\n const grow = ((g.x2 - g.x1) / 2) * (Math.SQRT2 - 1);\n return { ...g, x1: g.x1 - grow, x2: g.x2 + grow };\n };\n\n const dependencyPaths: Array<{ dep: GanttDependency; d: string; violated: boolean; label: string }> = [];\n for (const dep of dependencies) {\n const a = rows[indexById.get(dep.from) ?? -1];\n const b = rows[indexById.get(dep.to) ?? -1];\n if (!a || !b) continue;\n if (Math.max(a.index, b.index) < firstVisible || Math.min(a.index, b.index) > lastVisible) continue;\n const ga = geometryOf(a);\n const gb = geometryOf(b);\n if (!ga || !gb) continue;\n const type: GanttDependencyType = dep.type ?? \"finish-to-start\";\n dependencyPaths.push({\n dep,\n d: routeDependency(arrowEnds(a, ga), arrowEnds(b, gb), type, rowHeight),\n violated: isDependencyViolated(ga, gb, type),\n label: t.dependency(a.item.title, b.item.title)\n });\n }\n\n // ── Đầu trục + nền: chỉ dựng phần đang nhìn thấy ────────────────────────────────────────────────\n const overscanPx = 400;\n const visFrom = range.start + Math.max(0, Math.floor((visible.from - overscanPx) / ppd));\n const visTo = Math.min(range.end, range.start + Math.ceil((visible.to + overscanPx) / ppd));\n const minorTicks = viewport.width > 0 ? ticksBetween(visFrom, visTo, config.minor, weekStartsOn) : [];\n const majorTicks = viewport.width > 0 && config.major ? ticksBetween(visFrom, visTo, config.major, weekStartsOn) : [];\n\n const minorLabel = (day: number) => {\n const date = dayToLocalDate(day);\n switch (config.minor) {\n case \"day\":\n return fmt.dayNumber.format(date);\n case \"week\":\n return fmt.dayMonth.format(date);\n case \"month\":\n return fmt.monthShort.format(date);\n case \"quarter\":\n return t.quarter(Math.floor(date.getMonth() / 3) + 1, date.getFullYear());\n default:\n return fmt.year.format(date);\n }\n };\n const majorLabel = (day: number) =>\n config.major === \"month\" ? fmt.monthYear.format(dayToLocalDate(day)) : fmt.year.format(dayToLocalDate(day));\n\n /** Các khúc ngày nghỉ liền nhau trong phần đang nhìn thấy, gộp thành một dải. */\n const offBands: Array<{ start: number; end: number }> = [];\n if (config.shadeDays && viewport.width > 0) {\n for (let day = visFrom; day < visTo; day++) {\n if (!isOffDay(day)) continue;\n const last = offBands[offBands.length - 1];\n if (last && last.end === day) last.end = day + 1;\n else offBands.push({ start: day, end: day + 1 });\n }\n }\n\n const rowsHeight = rows.length * rowHeight;\n const totalHeight = headerHeight + rowsHeight;\n const totalWidth = sidebarWidth + timelineWidth;\n const todayX = xOf(today) + ppd / 2;\n const showToday = today >= range.start && today < range.end;\n // Nửa bề rộng chip hôm nay ở hàng nhãn dưới (chỉ có ở bậc tuần trở lên) + 4px khe — xem chỗ vẽ nhãn.\n const todayChipHalf = showToday && config.minor !== \"day\" ? (formatShort(today).length * 7 + 8) / 2 + 4 : 0;\n const instructionsId = React.useId();\n\n const toolbarExtra = typeof toolbar === \"boolean\" ? null : toolbar;\n const scaleNames: Record<GanttScale, string> = {\n day: t.scaleDay,\n week: t.scaleWeek,\n month: t.scaleMonth,\n quarter: t.scaleQuarter,\n year: t.scaleYear\n };\n\n return (\n <div\n data-slot=\"gantt\"\n data-scale={scale}\n className={cn(\"flex min-h-0 flex-col overflow-hidden rounded-lg border bg-card text-card-foreground\", className)}\n {...props}\n >\n {toolbar !== false && (\n <div data-slot=\"gantt-toolbar\" className=\"flex flex-wrap items-center gap-2 border-b px-3 py-2\">\n <ToggleGroup\n type=\"single\"\n variant=\"outline\"\n size=\"sm\"\n value={scale}\n onValueChange={(value) => value && changeScale(value as GanttScale)}\n aria-label={t.scaleLabel}\n >\n {GANTT_SCALES.map((value) => (\n <ToggleGroupItem key={value} value={value} className=\"px-2.5 text-xs\">\n {scaleNames[value]}\n </ToggleGroupItem>\n ))}\n </ToggleGroup>\n <Button variant=\"outline\" size=\"sm\" onClick={() => scrollToDay(today, \"smooth\", 1 / 3)}>\n {t.today}\n </Button>\n {toolbarExtra && <div className=\"ms-auto flex items-center gap-2\">{toolbarExtra}</div>}\n </div>\n )}\n\n <div\n ref={scrollRef}\n data-slot=\"gantt-viewport\"\n className=\"relative min-h-0 flex-1 overflow-auto overscroll-contain\"\n onScroll={readViewport}\n onPointerMove={onPointerMove}\n onPointerLeave={() => setHover(null)}\n >\n <div\n role=\"treegrid\"\n aria-label={ariaLabel ?? t.label}\n aria-rowcount={rows.length + 1}\n aria-colcount={2}\n aria-describedby={instructionsId}\n aria-readonly={!canEdit || undefined}\n className=\"group/gantt relative\"\n style={{ width: totalWidth, height: totalHeight }}\n >\n {/* ── Đầu trục ─────────────────────────────────────────────────────────────────────── */}\n <div\n role=\"row\"\n aria-rowindex={1}\n tabIndex={-1}\n className=\"sticky top-0 z-40 flex\"\n style={{ height: headerHeight }}\n >\n <div\n role=\"columnheader\"\n tabIndex={-1}\n className=\"sticky left-0 z-10 flex items-end border-e border-b bg-card px-3 pb-1.5 font-medium text-muted-foreground text-xs\"\n style={{ width: sidebarWidth }}\n >\n {sidebarTitle ?? t.sidebarTitle}\n </div>\n <div\n role=\"columnheader\"\n tabIndex={-1}\n aria-label={t.label}\n className=\"relative border-b bg-card\"\n style={{ width: timelineWidth }}\n >\n {majorTicks.map((tick) => (\n <div\n key={`major-${tick.day}`}\n className=\"absolute top-0 border-s border-border\"\n style={{ left: xOf(tick.day), width: (tick.next - tick.day) * ppd, height: HEADER_ROW }}\n >\n <span\n className=\"sticky inline-block whitespace-nowrap px-2 pt-1.5 font-medium text-foreground text-xs\"\n style={{ left: sidebarWidth }}\n >\n {majorLabel(tick.day)}\n </span>\n </div>\n ))}\n {minorTicks.map((tick) => {\n const width = (tick.next - tick.day) * ppd;\n const isToday = config.minor === \"day\" && tick.day === today;\n // Chip hôm nay (bậc tuần trở lên) nằm CHỒNG lên hàng nhãn này. Nó hẹp hơn nhãn bên dưới\n // nên che không hết (\"N\" của \"Nov 2\" lộ ra cạnh chip) — nhãn nào chip chạm thì ẩn hẳn.\n // Bề rộng ước theo số ký tự (chữ 11–12px ≈ 7px/ký tự), thừa một chút cho chắc.\n const labelStart = xOf(tick.day) + 8;\n const hiddenByToday =\n todayChipHalf > 0 &&\n todayX - todayChipHalf < labelStart + minorLabel(tick.day).length * 7 &&\n todayX + todayChipHalf > labelStart;\n return (\n <div\n key={`minor-${tick.day}`}\n title={formatFull(tick.day)}\n className={cn(\n \"absolute flex items-center border-s border-border text-muted-foreground text-xs tabular-nums\",\n config.minor === \"day\" ? \"justify-center\" : \"px-2\"\n )}\n style={{ left: xOf(tick.day), width, top: headerHeight - HEADER_ROW, height: HEADER_ROW }}\n >\n {isToday ? (\n <span className=\"rounded-sm bg-primary px-1 font-semibold text-primary-foreground\">\n {minorLabel(tick.day)}\n </span>\n ) : (\n <span className={cn(\"truncate\", hiddenByToday && \"invisible\")}>\n {config.minor === \"day\" && (\n <span className=\"me-0.5 text-[10px]\">\n {fmt.weekdayNarrow.format(dayToLocalDate(tick.day))}\n </span>\n )}\n {minorLabel(tick.day)}\n </span>\n )}\n </div>\n );\n })}\n {showToday && config.minor !== \"day\" && (\n <span\n aria-hidden=\"true\"\n className=\"absolute bottom-1 -translate-x-1/2 whitespace-nowrap rounded-sm bg-primary px-1 font-semibold text-[11px] text-primary-foreground\"\n style={{ left: todayX }}\n >\n {formatShort(today)}\n </span>\n )}\n </div>\n </div>\n\n {/* ── Nền: ngày nghỉ, vạch lưới, vạch hôm nay ─────────────────────────────────────────── */}\n <div\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute z-0\"\n style={{ top: headerHeight, left: sidebarWidth, width: timelineWidth, height: rowsHeight }}\n >\n {offBands.map((band) => (\n <div\n key={`off-${band.start}`}\n data-slot=\"gantt-off-day\"\n className=\"absolute inset-y-0 bg-muted\"\n style={{ left: xOf(band.start), width: (band.end - band.start) * ppd }}\n />\n ))}\n {minorTicks.map((tick) => (\n <div\n key={`line-${tick.day}`}\n className=\"absolute inset-y-0 border-s border-border\"\n style={{ left: xOf(tick.day) }}\n />\n ))}\n {/* Kẻ ngang của dòng nằm Ở ĐÂY (z-0), không phải `border-b` của ô trục: dòng ở z-20, cao hơn lớp\n mũi tên (z-10), nên kẻ ngang vẽ ở dòng sẽ cắt ngang mọi mũi tên đi qua nó. */}\n {virtualRows.map((virtualRow) => (\n <div\n key={`row-line-${virtualRow.index}`}\n className=\"absolute inset-x-0 border-b border-border\"\n style={{ top: virtualRow.start - headerHeight + rowHeight - 1, height: 1 }}\n />\n ))}\n {showToday && (\n <div\n data-slot=\"gantt-today\"\n className=\"absolute inset-y-0 w-0.5 -translate-x-1/2 bg-primary-ink\"\n style={{ left: todayX }}\n />\n )}\n </div>\n\n {/* ── Mũi tên phụ thuộc — DƯỚI dòng, để thanh luôn đè lên mũi tên chứ không ngược lại ─── */}\n <svg\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute z-10 overflow-visible\"\n style={{ top: headerHeight, left: sidebarWidth }}\n width={timelineWidth}\n height={rowsHeight}\n >\n {/* Đầu mũi tên cố định 8px (`userSpaceOnUse`). Mặc định `strokeWidth` nhân cỡ với bề dày nét: nét\n găng 2px thành đầu 14px, nuốt trọn đoạn cuối 5px của đường đi LÙI và mất hình. */}\n <defs>\n <marker\n id={`${instructionsId}-arrow`}\n viewBox=\"0 0 8 8\"\n refX=\"7\"\n refY=\"4\"\n markerUnits=\"userSpaceOnUse\"\n markerWidth=\"8\"\n markerHeight=\"8\"\n orient=\"auto\"\n >\n <path d=\"M0,0 L8,4 L0,8 z\" className=\"fill-muted-foreground\" />\n </marker>\n <marker\n id={`${instructionsId}-arrow-alert`}\n viewBox=\"0 0 8 8\"\n refX=\"7\"\n refY=\"4\"\n markerUnits=\"userSpaceOnUse\"\n markerWidth=\"8\"\n markerHeight=\"8\"\n orient=\"auto\"\n >\n <path d=\"M0,0 L8,4 L0,8 z\" className=\"fill-destructive-ink\" />\n </marker>\n </defs>\n {dependencyPaths.map(({ dep, d, violated, label }) => {\n const alert = violated || dep.critical;\n return (\n <g\n key={dep.id}\n data-slot=\"gantt-dependency\"\n data-critical={dep.critical || undefined}\n data-conflict={violated || undefined}\n >\n <title>{label}</title>\n <path\n d={d}\n fill=\"none\"\n strokeWidth={dep.critical ? 2 : 1.5}\n strokeDasharray={violated ? \"4 3\" : undefined}\n markerEnd={`url(#${instructionsId}-${alert ? \"arrow-alert\" : \"arrow\"})`}\n className={alert ? \"stroke-destructive-ink\" : \"stroke-muted-foreground\"}\n />\n {onDependencyClick && (\n // Vùng bấm rộng 12px dọc theo đường — nét 1,5px thì không ai bấm trúng.\n // biome-ignore lint/a11y/noStaticElementInteractions: mũi tên chỉ là lối tắt CHUỘT; app có lối bàn phím riêng\n <path\n d={d}\n fill=\"none\"\n stroke=\"transparent\"\n strokeWidth={12}\n className=\"pointer-events-auto cursor-pointer\"\n onClick={() => onDependencyClick(dep)}\n />\n )}\n </g>\n );\n })}\n {linkDraft &&\n (() => {\n const source = rows[indexById.get(linkDraft.from) ?? -1];\n const geometry = source ? geometryOf(source) : null;\n if (!geometry) return null;\n return (\n <path\n d={`M${geometry.x2},${geometry.y} L${linkDraft.x},${linkDraft.y}`}\n fill=\"none\"\n strokeWidth={1.5}\n strokeDasharray=\"4 3\"\n markerEnd={`url(#${instructionsId}-arrow)`}\n className=\"stroke-muted-foreground\"\n />\n );\n })()}\n </svg>\n\n {/* ── Dòng ────────────────────────────────────────────────────────────────────────────── */}\n {virtualRows.map((virtualRow) => {\n const row = rows[virtualRow.index];\n if (!row) return null;\n const { item } = row;\n const geometry = geometryOf(row);\n const tone: GanttTone = item.tone ?? \"neutral\";\n const depth = item.depth ?? 0;\n const editable = canEdit && !item.readOnly && item.type !== \"summary\";\n const isDragging = draft?.id === item.id;\n const isHovered = hover?.index === row.index;\n const isLinkTarget = linkDraft?.target === item.id;\n const conflict = conflicted.has(item.id);\n const description = [\n geometry ? describeRange({ item, start: geometry.start, end: geometry.end }) : t.unscheduled,\n item.progress != null ? t.progress(Math.round(item.progress)) : null,\n item.critical ? t.critical : null,\n conflict ? t.conflict : null\n ]\n .filter(Boolean)\n .join(\". \");\n\n return (\n <div\n key={item.id}\n role=\"row\"\n aria-rowindex={row.index + 2}\n aria-level={depth + 1}\n aria-expanded={item.hasChildren ? !!item.expanded : undefined}\n aria-selected={row.index === focusIndex}\n tabIndex={row.index === focusIndex ? 0 : -1}\n data-gantt-row={row.index}\n data-slot=\"gantt-row\"\n className=\"group/row pointer-events-none absolute left-0 z-20 flex outline-hidden focus-visible:outline-2 focus-visible:outline-solid focus-visible:-outline-offset-2 focus-visible:outline-ring\"\n style={{ top: virtualRow.start, height: rowHeight, width: totalWidth }}\n onKeyDown={(event) => onRowKeyDown(event, row)}\n onFocus={(event) => {\n setFocusedIndex(row.index);\n // Ô con (`tabIndex={-1}` theo luật lưới) cũng nhận focus khi bị BẤM — dồn về dòng,\n // nếu không phím mũi tên rơi vào ô và lưới im lặng.\n if (event.target !== event.currentTarget) event.currentTarget.focus({ preventScroll: true });\n }}\n >\n {/* Cột trái */}\n <div\n role=\"rowheader\"\n tabIndex={-1}\n className={cn(\n \"pointer-events-auto sticky left-0 z-10 flex min-w-0 items-center gap-1 border-e border-b bg-card pe-2\",\n isHovered && \"bg-muted\",\n // Dòng đang chọn chỉ tô khi lưới đang giữ focus — rời lưới thì vệt tô không còn\n // đứng đó như một lựa chọn thật.\n row.index === focusIndex && \"group-focus-within/gantt:bg-accent\"\n )}\n style={{ width: sidebarWidth, paddingInlineStart: 8 + depth * 16 }}\n >\n {item.hasChildren ? (\n <button\n type=\"button\"\n tabIndex={-1}\n aria-label={item.expanded ? t.collapse : t.expand}\n className=\"flex size-5 shrink-0 items-center justify-center rounded-sm text-muted-foreground hover:bg-accent hover:text-accent-foreground\"\n onClick={() => onExpandedChange?.(item.id, !item.expanded)}\n >\n {item.expanded ? (\n <ChevronDown aria-hidden=\"true\" className=\"size-3.5\" />\n ) : (\n <ChevronRight aria-hidden=\"true\" className=\"size-3.5 rtl:rotate-180\" />\n )}\n </button>\n ) : (\n <span aria-hidden=\"true\" className=\"w-5 shrink-0\" />\n )}\n {/* biome-ignore lint/a11y/noStaticElementInteractions: lối chuột; bàn phím đi Enter trên dòng */}\n {/* biome-ignore lint/a11y/useKeyWithClickEvents: như trên */}\n <div\n className={cn(\n \"min-w-0 flex-1 truncate text-foreground text-sm\",\n item.type === \"summary\" && \"font-semibold\",\n onItemOpen && \"cursor-pointer\"\n )}\n onClick={() => {\n focusRow(row.index, false);\n onItemOpen?.(item);\n }}\n >\n {renderSidebarItem ? renderSidebarItem(item) : item.title}\n </div>\n </div>\n\n {/* Trục */}\n <div\n role=\"gridcell\"\n tabIndex={-1}\n className={cn(\"relative\", isHovered && \"bg-muted/40\")}\n style={{ width: timelineWidth }}\n >\n <span className=\"sr-only\">{description}</span>\n\n {!geometry && editable && (\n // Dòng chưa có ngày: cả dải trục là một vùng bấm để xếp lịch ở ngày dưới con trỏ.\n <div\n aria-hidden=\"true\"\n className=\"pointer-events-auto absolute inset-0 cursor-copy\"\n onClick={(event) => {\n const point = toTimeline(event.clientX, event.clientY);\n schedule(row, range.start + Math.floor(point.x / ppd));\n }}\n >\n {isHovered && hover && (\n <div\n className=\"absolute flex items-center rounded-md border border-control-edge-strong border-dashed px-2 text-muted-foreground text-xs\"\n style={{\n left: xOf(hover.day),\n width: Math.max(SCHEDULE_DAYS[scale] * ppd, 96),\n top: (rowHeight - barHeight) / 2,\n height: barHeight\n }}\n >\n <span className=\"truncate\">{t.scheduleHint}</span>\n </div>\n )}\n </div>\n )}\n\n {geometry && item.type === \"milestone\" && (\n <div\n aria-hidden=\"true\"\n data-slot=\"gantt-milestone\"\n data-critical={item.critical || undefined}\n className={cn(\n \"pointer-events-auto absolute rotate-45 rounded-[2px]\",\n ganttMarkVariants({ tone }),\n item.critical && \"outline-[1.5px] outline-destructive-ink\",\n editable ? \"cursor-grab\" : \"cursor-default\",\n isDragging && \"cursor-grabbing shadow-overlay\"\n )}\n style={{\n left: geometry.x1,\n top: rowHeight / 2 - (geometry.x2 - geometry.x1) / 2,\n width: geometry.x2 - geometry.x1,\n height: geometry.x2 - geometry.x1\n }}\n onPointerDown={(event) => startDrag(event, row, \"move\")}\n onDoubleClick={() => onItemOpen?.(item)}\n />\n )}\n\n {geometry && item.type === \"summary\" && (\n <div\n aria-hidden=\"true\"\n data-slot=\"gantt-summary\"\n className={cn(\"absolute h-2 rounded-sm\", ganttMarkVariants({ tone }))}\n style={{ left: geometry.x1, width: geometry.x2 - geometry.x1, top: rowHeight / 2 - 4 }}\n />\n )}\n\n {geometry && (item.type ?? \"task\") === \"task\" && (\n <>\n <div\n aria-hidden=\"true\"\n data-slot=\"gantt-bar\"\n data-critical={item.critical || undefined}\n data-conflict={conflict || undefined}\n data-dragging={isDragging || undefined}\n className={cn(\n ganttBarVariants({ tone }),\n \"pointer-events-auto overflow-visible\",\n item.critical && \"border-2 border-destructive-ink\",\n isLinkTarget && \"outline-2 outline-offset-2 outline-ring\",\n editable ? \"cursor-grab\" : \"cursor-default\",\n isDragging && \"z-10 cursor-grabbing shadow-overlay\"\n )}\n style={{\n left: geometry.x1,\n width: geometry.x2 - geometry.x1,\n top: (rowHeight - barHeight) / 2,\n height: barHeight\n }}\n onPointerDown={(event) => startDrag(event, row, \"move\")}\n onDoubleClick={() => onItemOpen?.(item)}\n >\n {geometry.x2 - geometry.x1 >= MIN_INNER_LABEL && (\n <span className=\"min-w-0 truncate px-2\">{item.title}</span>\n )}\n {item.progress != null && item.progress > 0 && (\n // Vạch tiến độ 2px, lùi 1px khỏi viền (cùng màu `-ink`): dán sát đáy thì nó nhập vào\n // viền thành một dải 4px, nặng hơn cả thân thanh.\n <span\n className={cn(\"absolute bottom-px left-px h-0.5 rounded-full\", ganttMarkVariants({ tone }))}\n style={{ width: `max(0px, calc(${Math.min(100, item.progress)}% - 2px))` }}\n />\n )}\n {editable && (\n <>\n {/* Tay giãn: 8px trong thân ở chuột; 44px vắt qua mép ở thiết bị chạm. */}\n {!(coarse && geometry.x2 - geometry.x1 < 96) && (\n <span\n data-slot=\"gantt-handle\"\n className={cn(\n \"absolute inset-y-0 cursor-ew-resize\",\n coarse ? \"-start-[22px] w-11\" : \"start-0 w-2\"\n )}\n onPointerDown={(event) => startDrag(event, row, \"resize-start\")}\n />\n )}\n <span\n data-slot=\"gantt-handle\"\n className={cn(\n \"absolute inset-y-0 cursor-ew-resize\",\n coarse\n ? geometry.x2 - geometry.x1 < 96\n ? \"-end-11 w-11\"\n : \"-end-[22px] w-11\"\n : \"end-0 w-2\"\n )}\n onPointerDown={(event) => startDrag(event, row, \"resize-end\")}\n />\n </>\n )}\n {onDependencyCreate && editable && !coarse && (\n <span\n data-slot=\"gantt-link-handle\"\n title={t.linkHandle(item.title)}\n className=\"invisible absolute -end-4 top-1/2 size-2.5 -translate-y-1/2 cursor-crosshair rounded-full bg-primary-ink group-hover/row:visible\"\n onPointerDown={(event) => startLink(event, row)}\n />\n )}\n </div>\n {geometry.x2 - geometry.x1 < MIN_INNER_LABEL && (\n <span\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute flex items-center whitespace-nowrap text-foreground text-xs\"\n style={{\n left: geometry.x2 + (onDependencyCreate ? 20 : 6),\n top: 0,\n height: rowHeight\n }}\n >\n {item.title}\n </span>\n )}\n </>\n )}\n\n {/* Ngày hai đầu: hiện khi rê hoặc khi đang kéo — đọc được ngày mà không phải dò đầu trục. */}\n {geometry && item.type !== \"summary\" && (isHovered || isDragging) && (\n <>\n <span\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute flex -translate-x-full items-center whitespace-nowrap pe-1.5 text-[11px] text-muted-foreground tabular-nums\"\n style={{ left: geometry.x1, top: 0, height: rowHeight }}\n >\n {formatShort(geometry.start)}\n </span>\n {item.type !== \"milestone\" && geometry.x2 - geometry.x1 >= MIN_INNER_LABEL && (\n <span\n aria-hidden=\"true\"\n className=\"pointer-events-none absolute flex items-center whitespace-nowrap ps-1.5 text-[11px] text-muted-foreground tabular-nums\"\n style={{ left: geometry.x2 + (onDependencyCreate ? 14 : 0), top: 0, height: rowHeight }}\n >\n {formatShort(geometry.end)}\n </span>\n )}\n </>\n )}\n\n {/* Thanh nằm ngoài khung nhìn: mũi tên ở mép đưa tới nó. */}\n {geometry && viewport.width > 0 && geometry.x2 < visible.from + 4 && (\n <button\n type=\"button\"\n tabIndex={-1}\n aria-label={t.scrollToItem(item.title)}\n className=\"pointer-events-auto absolute flex size-6 items-center justify-center rounded-full border border-input bg-card text-muted-foreground shadow-control hover:text-foreground\"\n style={{ left: visible.from + 6, top: rowHeight / 2 - 12 }}\n onClick={() => revealBar(geometry)}\n >\n <ChevronLeft aria-hidden=\"true\" className=\"size-3.5\" />\n </button>\n )}\n {geometry && viewport.width > 0 && geometry.x1 > visible.to - 4 && (\n <button\n type=\"button\"\n tabIndex={-1}\n aria-label={t.scrollToItem(item.title)}\n className=\"pointer-events-auto absolute flex size-6 items-center justify-center rounded-full border border-input bg-card text-muted-foreground shadow-control hover:text-foreground\"\n style={{ left: visible.to - 30, top: rowHeight / 2 - 12 }}\n onClick={() => revealBar(geometry)}\n >\n <ChevronRight aria-hidden=\"true\" className=\"size-3.5\" />\n </button>\n )}\n </div>\n </div>\n );\n })}\n </div>\n </div>\n\n <p id={instructionsId} className=\"sr-only\">\n {t.instructions}\n </p>\n <div role=\"status\" aria-live=\"polite\" className=\"sr-only\">\n {announcement}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -70,6 +70,8 @@ function InlineEdit({
|
|
|
70
70
|
void commit();
|
|
71
71
|
}
|
|
72
72
|
};
|
|
73
|
+
const textFlow = multiline ? "block whitespace-pre-wrap" : wrap ? "block whitespace-normal break-words" : "block truncate";
|
|
74
|
+
const box = cn("-mx-1 block w-full rounded-md px-1 py-0.5 text-left", className, displayClassName);
|
|
73
75
|
if (!editing) {
|
|
74
76
|
return /* @__PURE__ */ jsx(
|
|
75
77
|
"button",
|
|
@@ -79,21 +81,13 @@ function InlineEdit({
|
|
|
79
81
|
"aria-label": ariaLabel,
|
|
80
82
|
onClick: () => setEditing(true),
|
|
81
83
|
className: cn(
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
// `block`, không để `<button>` mặc định `inline-block`: nút inline nằm trong một dòng chữ, và dòng đó cao
|
|
85
|
+
// hơn nút (chừa chỗ chân chữ) — khối cao thêm ~4,5px so với ô sửa, nên bấm vào sửa là mọi thứ bên dưới nhảy lên.
|
|
86
|
+
box,
|
|
87
|
+
"transition-colors duration-150 ease-out hover:bg-muted disabled:pointer-events-none disabled:opacity-60",
|
|
88
|
+
!value && "text-muted-foreground"
|
|
87
89
|
),
|
|
88
|
-
children: /* @__PURE__ */ jsx(
|
|
89
|
-
"span",
|
|
90
|
-
{
|
|
91
|
-
className: cn(
|
|
92
|
-
multiline ? "whitespace-pre-wrap" : wrap ? "block whitespace-normal break-words" : "block truncate"
|
|
93
|
-
),
|
|
94
|
-
children: value || placeholder
|
|
95
|
-
}
|
|
96
|
-
)
|
|
90
|
+
children: /* @__PURE__ */ jsx("span", { className: textFlow, children: value || placeholder })
|
|
97
91
|
}
|
|
98
92
|
);
|
|
99
93
|
}
|
|
@@ -105,35 +99,34 @@ function InlineEdit({
|
|
|
105
99
|
"aria-label": ariaLabel,
|
|
106
100
|
onKeyDown: handleKeyDown,
|
|
107
101
|
onBlur: saveOnBlur ? () => void commit() : void 0,
|
|
102
|
+
/*
|
|
103
|
+
* Cùng `px-1 py-0.5`, cùng cỡ/dòng chữ (thừa hưởng, không ép `text-sm`), cùng `className`/`displayClassName` với
|
|
104
|
+
* phần hiển thị. Viền vẽ bằng `ring` (bóng, không chiếm chỗ) thay `border`, vì viền 1px đẩy chữ lệch 1px.
|
|
105
|
+
*/
|
|
108
106
|
className: cn(
|
|
109
|
-
"
|
|
110
|
-
"
|
|
107
|
+
"absolute inset-y-0 -left-1 right-1 resize-none overflow-hidden rounded-md bg-background px-1 py-0.5 text-foreground",
|
|
108
|
+
"ring-1 ring-input ring-inset focus:outline-none focus:ring-2 focus:ring-primary-ink",
|
|
111
109
|
"disabled:opacity-60",
|
|
112
|
-
className
|
|
110
|
+
className,
|
|
111
|
+
displayClassName
|
|
113
112
|
)
|
|
114
113
|
};
|
|
115
114
|
return /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-1", children: [
|
|
116
|
-
|
|
117
|
-
"
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
inputRef.current = element;
|
|
132
|
-
},
|
|
133
|
-
type: "text",
|
|
134
|
-
onChange: (event) => setDraft(event.target.value)
|
|
135
|
-
}
|
|
136
|
-
),
|
|
115
|
+
/* @__PURE__ */ jsxs("div", { className: "relative min-w-0 flex-1", children: [
|
|
116
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: cn(box, "invisible"), children: /* @__PURE__ */ jsx("span", { className: textFlow, children: draft ? `${draft}\u200B` : placeholder }) }),
|
|
117
|
+
/* @__PURE__ */ jsx(
|
|
118
|
+
"textarea",
|
|
119
|
+
{
|
|
120
|
+
...sharedProps,
|
|
121
|
+
ref: (element) => {
|
|
122
|
+
inputRef.current = element;
|
|
123
|
+
},
|
|
124
|
+
rows: 1,
|
|
125
|
+
...multiline ? {} : wrap ? { "aria-multiline": false } : { "aria-multiline": false, wrap: "off", className: cn(sharedProps.className, "whitespace-pre") },
|
|
126
|
+
onChange: (event) => setDraft(multiline ? event.target.value : event.target.value.replace(/[\r\n]+/g, " "))
|
|
127
|
+
}
|
|
128
|
+
)
|
|
129
|
+
] }),
|
|
137
130
|
showActions && /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-0.5", children: [
|
|
138
131
|
/* @__PURE__ */ jsxs(
|
|
139
132
|
Button,
|
|
@@ -141,6 +134,7 @@ function InlineEdit({
|
|
|
141
134
|
type: "button",
|
|
142
135
|
variant: "ghost",
|
|
143
136
|
size: "icon-sm",
|
|
137
|
+
className: "size-7",
|
|
144
138
|
disabled: saving,
|
|
145
139
|
onMouseDown: (event) => {
|
|
146
140
|
event.preventDefault();
|
|
@@ -158,6 +152,7 @@ function InlineEdit({
|
|
|
158
152
|
type: "button",
|
|
159
153
|
variant: "ghost",
|
|
160
154
|
size: "icon-sm",
|
|
155
|
+
className: "size-7",
|
|
161
156
|
disabled: saving,
|
|
162
157
|
onMouseDown: (event) => {
|
|
163
158
|
event.preventDefault();
|