/* eslint-disable tailwindcss/no-custom-classname -- this adapter needs stable non-Tailwind class hooks for react-grid-layout handles. */ import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import ReactGridLayout, { WidthProvider } from 'react-grid-layout/legacy'; import { usePersistedGridLayout } from '@renderer/hooks/usePersistedGridLayout'; import { cn } from '@renderer/lib/utils'; import { browserGridLayoutRepository } from '@renderer/services/layout-system/BrowserGridLayoutRepository'; import { KanbanColumn } from './KanbanColumn'; import type { PersistedGridLayoutItem } from '@renderer/services/layout-system/gridLayoutTypes'; import type { KanbanColumnId } from '@shared/types'; import type { ReactElement, Ref } from 'react'; import type { Layout, LayoutItem, ResizeHandleAxis } from 'react-grid-layout/legacy'; const GRID_COLS = 12; const GRID_ROW_HEIGHT = 18; const GRID_MARGIN: [number, number] = [12, 12]; const DEFAULT_ITEM_WIDTH = 4; const DEFAULT_ITEM_HEIGHT_PX = 400; const DEFAULT_ITEM_HEIGHT = Math.max( 1, Math.round((DEFAULT_ITEM_HEIGHT_PX + GRID_MARGIN[1]) / (GRID_ROW_HEIGHT + GRID_MARGIN[1])) ); const DEFAULT_MIN_HEIGHT = 10; const DEFAULT_MIN_WIDTH = 3; const GRID_SCOPE_KEY = 'kanban-grid-layout:global:v2'; const SKELETON_HIDE_DELAY_MS = 500; const SKELETON_HIDE_DELAY_MS_ON_MODE_SWITCH = 750; const RESIZE_HANDLES: ResizeHandleAxis[] = ['s', 'w', 'e', 'n', 'sw', 'nw', 'se', 'ne']; const WidthAwareGridLayout = WidthProvider(ReactGridLayout); export interface KanbanGridColumn { id: KanbanColumnId; title: string; count: number; icon?: React.ReactNode; headerBg?: string; bodyBg?: string; content: React.ReactNode; showAddButton?: boolean; skeletonCards?: { key: string; height: number; }[]; } interface KanbanGridLayoutProps { columns: KanbanGridColumn[]; allColumnIds: KanbanColumnId[]; primaryColumnId?: KanbanColumnId | null; onPrimaryColumnWidthChange?: (width: number | null) => void; skeletonDelayMs?: number; } interface LoadedKanbanGridLayoutProps { readonly columns: KanbanGridColumn[]; readonly visibleItems: PersistedGridLayoutItem[]; readonly onPersistLayout: (layout: Layout, options?: { persist?: boolean }) => void; readonly primaryColumnId?: KanbanColumnId | null; readonly onPrimaryColumnWidthChange?: (width: number | null) => void; readonly className?: string; } interface LoadingKanbanGridLayoutProps { readonly columns: KanbanGridColumn[]; readonly visibleItems: PersistedGridLayoutItem[]; readonly primaryColumnId?: KanbanColumnId | null; readonly onPrimaryColumnWidthChange?: (width: number | null) => void; readonly className?: string; } const ITEMS_PER_FIRST_ROW = 3; const SECOND_ROW_ITEM_WIDTH = 6; function buildDefaultItems(itemIds: string[]): PersistedGridLayoutItem[] { return itemIds.map((id, index) => { const isSecondRow = index >= ITEMS_PER_FIRST_ROW; const w = isSecondRow ? SECOND_ROW_ITEM_WIDTH : DEFAULT_ITEM_WIDTH; const x = isSecondRow ? (index - ITEMS_PER_FIRST_ROW) * SECOND_ROW_ITEM_WIDTH : index * DEFAULT_ITEM_WIDTH; const y = isSecondRow ? DEFAULT_ITEM_HEIGHT : 0; return { id, x, y, w, h: DEFAULT_ITEM_HEIGHT, minW: DEFAULT_MIN_WIDTH, minH: DEFAULT_MIN_HEIGHT, }; }); } function toReactGridLayoutItem(item: PersistedGridLayoutItem): LayoutItem { return { i: item.id, x: item.x, y: item.y, w: item.w, h: item.h, minW: item.minW, minH: item.minH, maxW: item.maxW, maxH: item.maxH, }; } function fromReactGridLayout(layout: Layout): PersistedGridLayoutItem[] { return layout.map((item) => ({ id: item.i, x: item.x, y: item.y, w: item.w, h: item.h, minW: item.minW, minH: item.minH, maxW: item.maxW, maxH: item.maxH, })); } function renderResizeHandle(axis: ResizeHandleAxis, ref: Ref): ReactElement { return (