@comitor/ui 3.11.1 → 3.12.1

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.
Files changed (75) hide show
  1. package/README.md +10 -5
  2. package/dist/chat/chat-composer.d.ts +36 -0
  3. package/dist/chat/chat-composer.js +85 -0
  4. package/dist/chat/chat-composer.js.map +1 -0
  5. package/dist/chat/chat-constants.d.ts +63 -0
  6. package/dist/chat/chat-constants.js +1 -0
  7. package/dist/chat/chat-constants.js.map +1 -0
  8. package/dist/chat/chat-message.d.ts +51 -0
  9. package/dist/chat/chat-message.js +137 -0
  10. package/dist/chat/chat-message.js.map +1 -0
  11. package/dist/chat/chat-sources.d.ts +25 -0
  12. package/dist/chat/chat-sources.js +97 -0
  13. package/dist/chat/chat-sources.js.map +1 -0
  14. package/dist/chat/chat-steps.d.ts +20 -0
  15. package/dist/chat/chat-steps.js +84 -0
  16. package/dist/chat/chat-steps.js.map +1 -0
  17. package/dist/chat/chat-thread.d.ts +21 -0
  18. package/dist/chat/chat-thread.js +87 -0
  19. package/dist/chat/chat-thread.js.map +1 -0
  20. package/dist/chat/index.d.ts +12 -0
  21. package/dist/chat/index.js +14 -0
  22. package/dist/chat/index.js.map +1 -0
  23. package/dist/components/composite/gantt-constants.d.ts +127 -0
  24. package/dist/components/composite/gantt-constants.js +5 -0
  25. package/dist/components/composite/gantt-constants.js.map +1 -0
  26. package/dist/components/composite/gantt-utils.d.ts +106 -0
  27. package/dist/components/composite/gantt-utils.js +196 -0
  28. package/dist/components/composite/gantt-utils.js.map +1 -0
  29. package/dist/components/composite/gantt.d.ts +93 -0
  30. package/dist/components/composite/gantt.js +1137 -0
  31. package/dist/components/composite/gantt.js.map +1 -0
  32. package/dist/components/composite/stat-card.d.ts +1 -1
  33. package/dist/components/composite/tree-view-constants.d.ts +96 -0
  34. package/dist/components/composite/tree-view-constants.js +85 -0
  35. package/dist/components/composite/tree-view-constants.js.map +1 -0
  36. package/dist/components/composite/tree-view.d.ts +58 -0
  37. package/dist/components/composite/tree-view.js +388 -0
  38. package/dist/components/composite/tree-view.js.map +1 -0
  39. package/dist/components/ui/badge.d.ts +1 -1
  40. package/dist/components/ui/button.d.ts +1 -1
  41. package/dist/editor/bubble-toolbar.d.ts +1 -1
  42. package/dist/editor/editor-core.d.ts +2 -2
  43. package/dist/editor/editor-core.js +6 -0
  44. package/dist/editor/editor-core.js.map +1 -1
  45. package/dist/editor/editor-types.d.ts +1 -1
  46. package/dist/editor/fixed-toolbar.d.ts +1 -1
  47. package/dist/editor/image-upload.d.ts +1 -1
  48. package/dist/editor/index.d.ts +1 -1
  49. package/dist/editor/markdown-editor.d.ts +1 -1
  50. package/dist/editor/markdown-extensions.d.ts +3 -1
  51. package/dist/editor/markdown-extensions.js +3 -1
  52. package/dist/editor/markdown-extensions.js.map +1 -1
  53. package/dist/editor/rich-text-editor.d.ts +1 -1
  54. package/dist/editor/slash-commands.d.ts +1 -1
  55. package/dist/editor/toolbar-actions.d.ts +1 -1
  56. package/dist/{editor-types-CSrMeuSw.d.ts → editor-types-BacXD2VN.d.ts} +7 -1
  57. package/dist/i18n/locale-provider.d.ts +3 -0
  58. package/dist/i18n/messages.d.ts +6 -0
  59. package/dist/i18n/messages.js +11 -2
  60. package/dist/i18n/messages.js.map +1 -1
  61. package/dist/i18n/namespaces/chat.d.ts +8 -0
  62. package/dist/i18n/namespaces/chat.js +47 -0
  63. package/dist/i18n/namespaces/chat.js.map +1 -0
  64. package/dist/i18n/namespaces/gantt.d.ts +10 -0
  65. package/dist/i18n/namespaces/gantt.js +57 -0
  66. package/dist/i18n/namespaces/gantt.js.map +1 -0
  67. package/dist/i18n/namespaces/tree-view.d.ts +8 -0
  68. package/dist/i18n/namespaces/tree-view.js +21 -0
  69. package/dist/i18n/namespaces/tree-view.js.map +1 -0
  70. package/dist/index.d.ts +6 -1
  71. package/dist/index.js +6 -1
  72. package/dist/index.js.map +1 -1
  73. package/dist/lib/avatar-tone.d.ts +4 -0
  74. package/dist/lib/avatar-tone.js.map +1 -1
  75. package/package.json +9 -2
@@ -0,0 +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":[]}
@@ -24,7 +24,7 @@ import { IconComponent } from './empty-state.js';
24
24
  * light và `--yellow` ở dark, nên một class là đủ.
25
25
  */
26
26
  declare const statCardIconVariants: (props?: ({
27
- tone?: "default" | "destructive" | "warning" | "success" | "primary" | "info" | "app" | null | undefined;
27
+ tone?: "primary" | "default" | "destructive" | "warning" | "success" | "info" | "app" | null | undefined;
28
28
  } & class_variance_authority_types.ClassProp) | undefined) => string;
29
29
  type StatCardTone = NonNullable<VariantProps<typeof statCardIconVariants>["tone"]>;
30
30
  type StatCardDeltaDirection = "up" | "down" | "flat";
@@ -0,0 +1,96 @@
1
+ import * as React from 'react';
2
+
3
+ /**
4
+ * Kiểu dữ liệu + hàm thuần của `TreeView` — module THUẦN (không `"use client"`), barrel xuất THẲNG
5
+ * từ đây, để `moveTreeItems` gọi được ở Server Action / route handler (vd áp lại một thao tác kéo
6
+ * thả lên dữ liệu trước khi ghi), không chỉ ở client.
7
+ *
8
+ * ─ HỢP ĐỒNG DỮ LIỆU ─
9
+ * Cây đi vào dưới dạng danh sách PHẲNG: mỗi mục biết `parentId` của nó (`null` = gốc), và thứ tự
10
+ * giữa các anh em là thứ tự xuất hiện trong mảng. Đó là hình dạng một bảng SQL `parent_id` +
11
+ * `position` trả về — app không phải dựng cây lồng nhau rồi lại tháo ra khi ghi.
12
+ */
13
+
14
+ interface TreeViewItem {
15
+ id: string;
16
+ /** Cha (`null` = nằm ở gốc). Cha không có trong danh sách = mục mồ côi, bị bỏ qua. */
17
+ parentId: string | null;
18
+ /** Tên hiển thị — cũng là tên cho trình đọc màn hình. */
19
+ name: string;
20
+ /**
21
+ * Coi là nhánh dù chưa có con (vd giai đoạn vừa tạo) — hiện nút mở/thu và nhận thả VÀO trong.
22
+ * Mục có con thì luôn là nhánh.
23
+ */
24
+ isFolder?: boolean;
25
+ /** Khoá kéo và thao tác dàn ý trên riêng mục này. */
26
+ readOnly?: boolean;
27
+ }
28
+ /**
29
+ * Một lần đổi chỗ — do kéo thả hoặc phím dàn ý. `index` là vị trí trong danh sách con của
30
+ * `parentId` SAU KHI đã gỡ các mục đang chuyển ra (nên dời một mục xuống một bậc trong cùng cha là
31
+ * `index = vị trí cũ + 1`, không phải `+ 2`). `moveTreeItems` áp đúng quy ước này.
32
+ */
33
+ interface TreeViewMove {
34
+ ids: string[];
35
+ parentId: string | null;
36
+ index: number;
37
+ kind: "drag" | "indent" | "outdent" | "move-up" | "move-down";
38
+ }
39
+ type TreeViewRollup = "sum" | "min" | "max" | "avg" | ((values: number[]) => number | null);
40
+ interface TreeViewColumn<T extends TreeViewItem = TreeViewItem> {
41
+ id: string;
42
+ /** Tiêu đề cột. */
43
+ header: React.ReactNode;
44
+ /** Bề rộng, px (mặc định 96). */
45
+ width?: number;
46
+ /** Căn chữ (mặc định `end` — cột số). */
47
+ align?: "start" | "end";
48
+ /** Giá trị số của RIÊNG mục này (`null` = không có). Cột không có `value` thì dùng `render`. */
49
+ value?: (item: T) => number | null | undefined;
50
+ /**
51
+ * Cộng dồn ở nhánh: giá trị của nhánh = phép gộp trên giá trị (đã cộng dồn) của các con trực
52
+ * tiếp. Không đặt thì nhánh hiện giá trị của riêng nó. `avg` là trung bình của các con, không
53
+ * có trọng số — cần trọng số thì truyền hàm.
54
+ */
55
+ rollup?: TreeViewRollup;
56
+ /** Định dạng giá trị (mặc định `toLocaleString` theo ngôn ngữ của provider). */
57
+ format?: (value: number, item: T, context: {
58
+ rolledUp: boolean;
59
+ }) => React.ReactNode;
60
+ /** Chữ đọc cho trình đọc màn hình (mặc định: chuỗi định dạng số). */
61
+ formatText?: (value: number, item: T) => string;
62
+ /** Vẽ tự do — thay cho `value`/`format` (vd avatar người phụ trách). Không cộng dồn. */
63
+ render?: (item: T) => React.ReactNode;
64
+ }
65
+ /** Mọi chữ `TreeView` tự vẽ hoặc tự đọc. Theo `ComitorLocaleProvider` (namespace `treeView`). */
66
+ interface TreeViewLabels {
67
+ /** Tên cây khi app không truyền `aria-label`. */
68
+ label: string;
69
+ /** Tiêu đề cột tên (khi có cột phụ). */
70
+ nameHeader: string;
71
+ /** Tên của gốc, dùng trong lời xướng khi một mục lên tới gốc. */
72
+ root: string;
73
+ /** Hướng dẫn phím dàn ý, gắn qua `aria-describedby`. */
74
+ instructions: string;
75
+ /** Xướng sau khi mục vào trong một nhánh (Tab, hoặc thả). */
76
+ movedInto: (name: string, parent: string, position: number, total: number) => string;
77
+ /** Xướng sau khi mục đổi chỗ trong cùng cha (Alt+↑/↓). */
78
+ movedTo: (name: string, position: number, total: number) => string;
79
+ }
80
+ /**
81
+ * Áp một `TreeViewMove` lên danh sách phẳng — trả mảng MỚI (không sửa mảng cũ): mục chuyển đổi
82
+ * `parentId` và đứng ở `index` trong số anh em mới. Mục chuyển giữ nguyên thứ tự tương đối với nhau.
83
+ * Từ chối (trả nguyên mảng cũ) khi thao tác sẽ biến một mục thành con cháu của chính nó.
84
+ */
85
+ declare function moveTreeItems<T extends TreeViewItem>(items: T[], move: TreeViewMove): T[];
86
+ /** Con trực tiếp theo cha, giữ thứ tự của mảng. Mục có cha không tồn tại bị bỏ (mồ côi). */
87
+ declare function groupTreeChildren<T extends TreeViewItem>(items: T[]): Map<string | null, T[]>;
88
+ /**
89
+ * Giá trị của một cột cho MỌI mục, đã cộng dồn từ dưới lên. Dùng chung với `TreeView` để app tính
90
+ * cùng con số ở nơi khác (xuất CSV, tổng ở chân trang) mà không viết lại phép gộp.
91
+ */
92
+ declare function computeTreeRollup<T extends TreeViewItem>(items: T[], column: Pick<TreeViewColumn<T>, "value" | "rollup">): Map<string, number | null>;
93
+ /** Số hiệu dàn ý `1`, `1.2`, `1.2.3` cho mọi mục, theo thứ tự anh em. */
94
+ declare function computeTreeNumbering<T extends TreeViewItem>(items: T[]): Map<string, string>;
95
+
96
+ export { type TreeViewColumn, type TreeViewItem, type TreeViewLabels, type TreeViewMove, type TreeViewRollup, computeTreeNumbering, computeTreeRollup, groupTreeChildren, moveTreeItems };
@@ -0,0 +1,85 @@
1
+ function aggregate(rollup, values) {
2
+ if (values.length === 0) return null;
3
+ if (typeof rollup === "function") return rollup(values);
4
+ switch (rollup) {
5
+ case "sum":
6
+ return values.reduce((a, b) => a + b, 0);
7
+ case "min":
8
+ return Math.min(...values);
9
+ case "max":
10
+ return Math.max(...values);
11
+ case "avg":
12
+ return values.reduce((a, b) => a + b, 0) / values.length;
13
+ }
14
+ }
15
+ function moveTreeItems(items, move) {
16
+ const moving = new Set(move.ids);
17
+ const byId = new Map(items.map((item) => [item.id, item]));
18
+ for (let cursor = move.parentId; cursor != null; cursor = byId.get(cursor)?.parentId ?? null) {
19
+ if (moving.has(cursor)) return items;
20
+ }
21
+ const moved = items.filter((item) => moving.has(item.id)).map((item) => ({ ...item, parentId: move.parentId }));
22
+ const rest = items.filter((item) => !moving.has(item.id));
23
+ const siblings = rest.filter((item) => item.parentId === move.parentId);
24
+ const anchor = siblings[move.index];
25
+ const at = anchor ? rest.indexOf(anchor) : rest.length;
26
+ return [...rest.slice(0, at), ...moved, ...rest.slice(at)];
27
+ }
28
+ function groupTreeChildren(items) {
29
+ const ids = new Set(items.map((item) => item.id));
30
+ const children = /* @__PURE__ */ new Map();
31
+ for (const item of items) {
32
+ const parent = item.parentId != null && ids.has(item.parentId) ? item.parentId : null;
33
+ if (item.parentId != null && parent == null) continue;
34
+ const list = children.get(parent);
35
+ if (list) list.push(item);
36
+ else children.set(parent, [item]);
37
+ }
38
+ return children;
39
+ }
40
+ function computeTreeRollup(items, column) {
41
+ const children = groupTreeChildren(items);
42
+ const result = /* @__PURE__ */ new Map();
43
+ const visit = (item, guard) => {
44
+ if (result.has(item.id)) return result.get(item.id) ?? null;
45
+ if (guard.has(item.id)) return null;
46
+ guard.add(item.id);
47
+ const kids = children.get(item.id) ?? [];
48
+ let value;
49
+ if (column.rollup && kids.length > 0) {
50
+ const values = [];
51
+ for (const kid of kids) {
52
+ const v = visit(kid, guard);
53
+ if (v != null && Number.isFinite(v)) values.push(v);
54
+ }
55
+ value = aggregate(column.rollup, values);
56
+ } else {
57
+ value = column.value?.(item) ?? null;
58
+ }
59
+ result.set(item.id, value);
60
+ return value;
61
+ };
62
+ for (const item of items) visit(item, /* @__PURE__ */ new Set());
63
+ return result;
64
+ }
65
+ function computeTreeNumbering(items) {
66
+ const children = groupTreeChildren(items);
67
+ const numbers = /* @__PURE__ */ new Map();
68
+ const walk = (parent, prefix, depth) => {
69
+ if (depth > 64) return;
70
+ (children.get(parent) ?? []).forEach((item, index) => {
71
+ const number = prefix ? `${prefix}.${index + 1}` : String(index + 1);
72
+ numbers.set(item.id, number);
73
+ walk(item.id, number, depth + 1);
74
+ });
75
+ };
76
+ walk(null, "", 0);
77
+ return numbers;
78
+ }
79
+ export {
80
+ computeTreeNumbering,
81
+ computeTreeRollup,
82
+ groupTreeChildren,
83
+ moveTreeItems
84
+ };
85
+ //# sourceMappingURL=tree-view-constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/composite/tree-view-constants.ts"],"sourcesContent":["/**\n * Kiểu dữ liệu + hàm thuần của `TreeView` — module THUẦN (không `\"use client\"`), barrel xuất THẲNG\n * từ đây, để `moveTreeItems` gọi được ở Server Action / route handler (vd áp lại một thao tác kéo\n * thả lên dữ liệu trước khi ghi), không chỉ ở client.\n *\n * ─ HỢP ĐỒNG DỮ LIỆU ─\n * Cây đi vào dưới dạng danh sách PHẲNG: mỗi mục biết `parentId` của nó (`null` = gốc), và thứ tự\n * giữa các anh em là thứ tự xuất hiện trong mảng. Đó là hình dạng một bảng SQL `parent_id` +\n * `position` trả về — app không phải dựng cây lồng nhau rồi lại tháo ra khi ghi.\n */\nimport type * as React from \"react\";\n\nexport interface TreeViewItem {\n id: string;\n /** Cha (`null` = nằm ở gốc). Cha không có trong danh sách = mục mồ côi, bị bỏ qua. */\n parentId: string | null;\n /** Tên hiển thị — cũng là tên cho trình đọc màn hình. */\n name: string;\n /**\n * Coi là nhánh dù chưa có con (vd giai đoạn vừa tạo) — hiện nút mở/thu và nhận thả VÀO trong.\n * Mục có con thì luôn là nhánh.\n */\n isFolder?: boolean;\n /** Khoá kéo và thao tác dàn ý trên riêng mục này. */\n readOnly?: boolean;\n}\n\n/**\n * Một lần đổi chỗ — do kéo thả hoặc phím dàn ý. `index` là vị trí trong danh sách con của\n * `parentId` SAU KHI đã gỡ các mục đang chuyển ra (nên dời một mục xuống một bậc trong cùng cha là\n * `index = vị trí cũ + 1`, không phải `+ 2`). `moveTreeItems` áp đúng quy ước này.\n */\nexport interface TreeViewMove {\n ids: string[];\n parentId: string | null;\n index: number;\n kind: \"drag\" | \"indent\" | \"outdent\" | \"move-up\" | \"move-down\";\n}\n\nexport type TreeViewRollup = \"sum\" | \"min\" | \"max\" | \"avg\" | ((values: number[]) => number | null);\n\nexport interface TreeViewColumn<T extends TreeViewItem = TreeViewItem> {\n id: string;\n /** Tiêu đề cột. */\n header: React.ReactNode;\n /** Bề rộng, px (mặc định 96). */\n width?: number;\n /** Căn chữ (mặc định `end` — cột số). */\n align?: \"start\" | \"end\";\n /** Giá trị số của RIÊNG mục này (`null` = không có). Cột không có `value` thì dùng `render`. */\n value?: (item: T) => number | null | undefined;\n /**\n * Cộng dồn ở nhánh: giá trị của nhánh = phép gộp trên giá trị (đã cộng dồn) của các con trực\n * tiếp. Không đặt thì nhánh hiện giá trị của riêng nó. `avg` là trung bình của các con, không\n * có trọng số — cần trọng số thì truyền hàm.\n */\n rollup?: TreeViewRollup;\n /** Định dạng giá trị (mặc định `toLocaleString` theo ngôn ngữ của provider). */\n format?: (value: number, item: T, context: { rolledUp: boolean }) => React.ReactNode;\n /** Chữ đọc cho trình đọc màn hình (mặc định: chuỗi định dạng số). */\n formatText?: (value: number, item: T) => string;\n /** Vẽ tự do — thay cho `value`/`format` (vd avatar người phụ trách). Không cộng dồn. */\n render?: (item: T) => React.ReactNode;\n}\n\n/** Mọi chữ `TreeView` tự vẽ hoặc tự đọc. Theo `ComitorLocaleProvider` (namespace `treeView`). */\nexport interface TreeViewLabels {\n /** Tên cây khi app không truyền `aria-label`. */\n label: string;\n /** Tiêu đề cột tên (khi có cột phụ). */\n nameHeader: string;\n /** Tên của gốc, dùng trong lời xướng khi một mục lên tới gốc. */\n root: string;\n /** Hướng dẫn phím dàn ý, gắn qua `aria-describedby`. */\n instructions: string;\n /** Xướng sau khi mục vào trong một nhánh (Tab, hoặc thả). */\n movedInto: (name: string, parent: string, position: number, total: number) => string;\n /** Xướng sau khi mục đổi chỗ trong cùng cha (Alt+↑/↓). */\n movedTo: (name: string, position: number, total: number) => string;\n}\n\nfunction aggregate(rollup: TreeViewRollup, values: number[]): number | null {\n if (values.length === 0) return null;\n if (typeof rollup === \"function\") return rollup(values);\n switch (rollup) {\n case \"sum\":\n return values.reduce((a, b) => a + b, 0);\n case \"min\":\n return Math.min(...values);\n case \"max\":\n return Math.max(...values);\n case \"avg\":\n return values.reduce((a, b) => a + b, 0) / values.length;\n }\n}\n\n/**\n * Áp một `TreeViewMove` lên danh sách phẳng — trả mảng MỚI (không sửa mảng cũ): mục chuyển đổi\n * `parentId` và đứng ở `index` trong số anh em mới. Mục chuyển giữ nguyên thứ tự tương đối với nhau.\n * Từ chối (trả nguyên mảng cũ) khi thao tác sẽ biến một mục thành con cháu của chính nó.\n */\nexport function moveTreeItems<T extends TreeViewItem>(items: T[], move: TreeViewMove): T[] {\n const moving = new Set(move.ids);\n const byId = new Map(items.map((item) => [item.id, item]));\n // Chặn vòng: cha mới không được là chính mục đang chuyển hay con cháu của nó.\n for (let cursor = move.parentId; cursor != null; cursor = byId.get(cursor)?.parentId ?? null) {\n if (moving.has(cursor)) return items;\n }\n const moved = items.filter((item) => moving.has(item.id)).map((item) => ({ ...item, parentId: move.parentId }));\n const rest = items.filter((item) => !moving.has(item.id));\n const siblings = rest.filter((item) => item.parentId === move.parentId);\n const anchor = siblings[move.index];\n const at = anchor ? rest.indexOf(anchor) : rest.length;\n return [...rest.slice(0, at), ...moved, ...rest.slice(at)];\n}\n\n/** Con trực tiếp theo cha, giữ thứ tự của mảng. Mục có cha không tồn tại bị bỏ (mồ côi). */\nexport function groupTreeChildren<T extends TreeViewItem>(items: T[]): Map<string | null, T[]> {\n const ids = new Set(items.map((item) => item.id));\n const children = new Map<string | null, T[]>();\n for (const item of items) {\n const parent = item.parentId != null && ids.has(item.parentId) ? item.parentId : null;\n if (item.parentId != null && parent == null) continue;\n const list = children.get(parent);\n if (list) list.push(item);\n else children.set(parent, [item]);\n }\n return children;\n}\n\n/**\n * Giá trị của một cột cho MỌI mục, đã cộng dồn từ dưới lên. Dùng chung với `TreeView` để app tính\n * cùng con số ở nơi khác (xuất CSV, tổng ở chân trang) mà không viết lại phép gộp.\n */\nexport function computeTreeRollup<T extends TreeViewItem>(\n items: T[],\n column: Pick<TreeViewColumn<T>, \"value\" | \"rollup\">\n): Map<string, number | null> {\n const children = groupTreeChildren(items);\n const result = new Map<string, number | null>();\n const visit = (item: T, guard: Set<string>): number | null => {\n if (result.has(item.id)) return result.get(item.id) ?? null;\n if (guard.has(item.id)) return null;\n guard.add(item.id);\n const kids = children.get(item.id) ?? [];\n let value: number | null;\n if (column.rollup && kids.length > 0) {\n const values: number[] = [];\n for (const kid of kids) {\n const v = visit(kid, guard);\n if (v != null && Number.isFinite(v)) values.push(v);\n }\n value = aggregate(column.rollup, values);\n } else {\n value = column.value?.(item) ?? null;\n }\n result.set(item.id, value);\n return value;\n };\n for (const item of items) visit(item, new Set());\n return result;\n}\n\n/** Số hiệu dàn ý `1`, `1.2`, `1.2.3` cho mọi mục, theo thứ tự anh em. */\nexport function computeTreeNumbering<T extends TreeViewItem>(items: T[]): Map<string, string> {\n const children = groupTreeChildren(items);\n const numbers = new Map<string, string>();\n const walk = (parent: string | null, prefix: string, depth: number) => {\n if (depth > 64) return;\n (children.get(parent) ?? []).forEach((item, index) => {\n const number = prefix ? `${prefix}.${index + 1}` : String(index + 1);\n numbers.set(item.id, number);\n walk(item.id, number, depth + 1);\n });\n };\n walk(null, \"\", 0);\n return numbers;\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,58 @@
1
+ import * as React from 'react';
2
+ import { TreeViewItem, TreeViewMove, TreeViewColumn, TreeViewLabels } from './tree-view-constants.js';
3
+
4
+ interface TreeViewProps<T extends TreeViewItem> extends Omit<React.ComponentProps<"div">, "children" | "onChange" | "onSelect"> {
5
+ /** Danh sách PHẲNG — `parentId` + thứ tự trong mảng (xem `TreeViewItem`). */
6
+ items: T[];
7
+ /** Nhánh đang mở (controlled). Bỏ trống thì cây tự giữ, bắt đầu từ `defaultExpandedIds`. */
8
+ expandedIds?: string[];
9
+ defaultExpandedIds?: string[];
10
+ onExpandedIdsChange?: (ids: string[]) => void;
11
+ /** Mục đang chọn (controlled). Bấm = chọn một; Ctrl/⌘ = thêm/bớt; Shift = chọn dải. */
12
+ selectedIds?: string[];
13
+ defaultSelectedIds?: string[];
14
+ onSelectedIdsChange?: (ids: string[]) => void;
15
+ /**
16
+ * Người dùng đổi chỗ (kéo thả, Tab, Shift+Tab, Alt+↑/↓). Cây KHÔNG tự ghi: app áp bằng
17
+ * `moveTreeItems` (hoặc gọi API) rồi truyền `items` mới. Không truyền = thứ tự chỉ đọc.
18
+ */
19
+ onMove?: (move: TreeViewMove) => void;
20
+ /** Mở một mục: Enter, hoặc bấm đúp. */
21
+ onItemOpen?: (item: T) => void;
22
+ /** Cột phụ bên phải tên — số liệu có cộng dồn ở nhánh (giờ ước lượng, tiến độ…). */
23
+ columns?: TreeViewColumn<T>[];
24
+ /** Hiện số hiệu dàn ý `1.2.3` trước tên (WBS). */
25
+ numbering?: boolean;
26
+ /** Vẽ phần tên của một dòng — thay cho tên trần (icon, nhãn trạng thái…). */
27
+ renderItem?: (item: T, state: {
28
+ level: number;
29
+ expanded: boolean;
30
+ folder: boolean;
31
+ number: string;
32
+ }) => React.ReactNode;
33
+ /** Chiều cao dòng, px (mặc định 32 — và 44 trên thiết bị chạm). */
34
+ rowHeight?: number;
35
+ /** Thụt lề mỗi bậc, px (mặc định 20). */
36
+ indent?: number;
37
+ /** Khoá mọi thao tác đổi chỗ dù có `onMove`. */
38
+ readOnly?: boolean;
39
+ labels?: Partial<TreeViewLabels>;
40
+ }
41
+ /**
42
+ * Cây có thao tác DÀN Ý — dựng cho WBS (dự án → giai đoạn → hạng mục → việc), dùng được cho mọi
43
+ * cây có thứ tự (danh mục, sơ đồ tổ chức, thư mục).
44
+ *
45
+ * ─ CONTROLLED ─
46
+ * Dữ liệu và thứ tự là của app. Mọi lần đổi chỗ đi ra qua MỘT callback `onMove` (cha mới + vị trí
47
+ * mới), kể cả khi nó đến từ kéo thả hay từ phím; `moveTreeItems` áp nó lên danh sách phẳng.
48
+ *
49
+ * ─ BÀN PHÍM ─ (một điểm dừng Tab — mục đang focus; mũi tên theo mẫu Treeview của W3C)
50
+ * ↑ ↓ — chuyển mục · → — mở nhánh / xuống con đầu · ← — thu nhánh / lên cha · Home End
51
+ * Tab — thụt vào (thành con cuối của mục đứng trên) · Shift+Tab — lùi ra (đứng sau cha cũ)
52
+ * Alt+↑ / Alt+↓ — đổi chỗ với anh em · Enter — `onItemOpen` · Shift+↑/↓, Ctrl+A — chọn
53
+ * Tab chỉ bị giữ lại khi thật sự thụt được; ở mục đầu tiên của một cấp nó đi tiếp ra ngoài như
54
+ * thường — cây không thành cái bẫy bàn phím.
55
+ */
56
+ declare function TreeView<T extends TreeViewItem>({ items, expandedIds, defaultExpandedIds, onExpandedIdsChange, selectedIds, defaultSelectedIds, onSelectedIdsChange, onMove, onItemOpen, columns, numbering, renderItem, rowHeight: rowHeightProp, indent, readOnly, labels, className, "aria-label": ariaLabel, ...props }: TreeViewProps<T>): React.JSX.Element;
57
+
58
+ export { TreeView, type TreeViewProps };