bolt-table 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/BoltTable.tsx","../src/DraggableHeader.tsx","../src/ResizeOverlay.tsx","../src/TableBody.tsx"],"sourcesContent":["'use client';\n\nimport {\n closestCenter,\n DndContext,\n DragEndEvent,\n DragOverlay,\n DragStartEvent,\n PointerSensor,\n useSensor,\n useSensors,\n} from '@dnd-kit/core';\nimport {\n arrayMove,\n horizontalListSortingStrategy,\n SortableContext,\n} from '@dnd-kit/sortable';\nimport { useVirtualizer } from '@tanstack/react-virtual';\nimport {\n ChevronDown,\n ChevronLeft,\n ChevronRight,\n ChevronsLeft,\n ChevronsRight,\n GripVertical,\n} from 'lucide-react';\nimport React, {\n CSSProperties,\n useCallback,\n useMemo,\n useRef,\n useState,\n} from 'react';\n\nimport DraggableHeader from './DraggableHeader';\nimport ResizeOverlay, { type ResizeOverlayHandle } from './ResizeOverlay';\nimport TableBody from './TableBody';\nimport type {\n ColumnContextMenuItem,\n ColumnType,\n DataRecord,\n ExpandableConfig,\n PaginationType,\n RowSelectionConfig,\n SortDirection,\n} from './types';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// BoltTable\n//\n// A high-performance, fully-featured React table component built on:\n// • @tanstack/react-virtual — row virtualization (only visible rows in DOM)\n// • @dnd-kit/core + sortable — drag-to-reorder column headers\n//\n// Feature overview:\n// ┌─────────────────────────────────────────────────────────────────┐\n// │ Feature │ Controlled │ Uncontrolled (default) │\n// ├─────────────────────────────────────────────────────────────────┤\n// │ Column order │ onColumnOrderChange callback │\n// │ Column width │ onColumnResize callback │\n// │ Column pinning │ onColumnPin callback │\n// │ Column visibility │ onColumnHide callback │\n// │ Sort │ onSortChange (server-side) │\n// │ │ local sort (client-side, default) │\n// │ Filter │ onFilterChange (server-side) │\n// │ │ local filter (client-side, default) │\n// │ Pagination │ onPaginationChange (server-side) │\n// │ │ client-side slice (default) │\n// │ Row selection │ rowSelection.onChange (always) │\n// │ Row expansion │ expandable.expandedRowKeys (opt.) │\n// └─────────────────────────────────────────────────────────────────┘\n//\n// Performance notes:\n// - Only the visible rows are in the DOM at any time (virtualization).\n// - Row hover is handled via pure DOM attribute mutation — zero React re-renders.\n// - Column headers use React.memo with custom comparators.\n// - The selection column renders checkboxes inside Cell (not via column.render)\n// so toggling one row's selection never re-renders other cells.\n// - Column widths are stored in a separate state bucket so pagination prop\n// changes never reset user-adjusted widths.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Props for the BoltTable component.\n *\n * @typeParam T - The type of a single row record. Must extend `DataRecord`\n * (i.e. `Record<string, unknown>`). Pass your own interface for\n * full type safety in column `render`, `sorter`, and `filterFn`.\n *\n * @example\n * interface User {\n * id: string;\n * name: string;\n * email: string;\n * age: number;\n * }\n *\n * <BoltTable<User>\n * columns={columns}\n * data={users}\n * rowKey=\"id\"\n * pagination={{ pageSize: 20 }}\n * />\n */\ninterface BoltTableProps<T extends DataRecord = DataRecord> {\n /**\n * Column definitions array. Controls what columns are shown, their order,\n * width, pinning, sort/filter behavior, and cell rendering.\n *\n * BoltTable watches this array for changes using a content fingerprint\n * (key + hidden + pinned + width). The internal column state syncs whenever\n * the fingerprint changes, but sub-pixel width jitter (e.g. percentage widths)\n * is normalized to avoid unnecessary re-syncs.\n *\n * @example\n * const columns: ColumnType<User>[] = [\n * { key: 'name', dataIndex: 'name', title: 'Name', width: 200, sortable: true },\n * { key: 'email', dataIndex: 'email', title: 'Email', width: 250 },\n * { key: 'age', dataIndex: 'age', title: 'Age', width: 80, sortable: true },\n * ];\n */\n readonly columns: ColumnType<T>[];\n\n /**\n * The row data to display. Each element corresponds to one table row.\n *\n * For **client-side** pagination/sort/filter, pass the full dataset.\n * BoltTable will slice, sort, and filter it internally.\n *\n * For **server-side** operations, pass only the current page's data and\n * provide `onSortChange`, `onFilterChange`, and `onPaginationChange` callbacks\n * to handle these operations on your server.\n *\n * @example\n * data={users} // User[]\n */\n readonly data: T[];\n\n /**\n * Height of each regular (non-expanded) row in pixels.\n * All rows must have the same base height for virtualization to work correctly.\n *\n * @default 40\n *\n * @example\n * rowHeight={48}\n */\n readonly rowHeight?: number;\n\n /**\n * The **estimated** height (in pixels) for expanded row content panels.\n * Used as the initial size estimate when a row is first expanded, before\n * the actual content height has been measured by ResizeObserver.\n * Once measured, the virtualizer updates to the real height.\n *\n * Set this close to your typical expanded row height for the smoothest experience.\n *\n * @default 200\n *\n * @example\n * expandedRowHeight={300}\n */\n readonly expandedRowHeight?: number;\n\n /**\n * Optional maximum height in pixels for expanded row panels.\n * When the expanded content exceeds this height, the panel becomes scrollable.\n * When omitted, panels grow to their full content height.\n *\n * @example\n * maxExpandedRowHeight={400}\n */\n readonly maxExpandedRowHeight?: number;\n\n /**\n * The primary color used for interactive elements throughout the table:\n * - Sort direction indicators in column headers\n * - Active filter icon in column headers\n * - Column resize overlay line and label\n * - Selected row background tint (as a transparent overlay)\n * - Expand/collapse chevron buttons\n * - Checkbox and radio button accent color\n * - Highlighted sort/filter options in the context menu\n * - Page number highlight in the pagination footer\n *\n * Accepts any valid CSS color string.\n *\n * @default '#1890ff'\n *\n * @example\n * accentColor=\"#6366f1\" // indigo\n * accentColor=\"#10b981\" // emerald\n * accentColor=\"hsl(262, 80%, 50%)\"\n */\n readonly accentColor?: string;\n\n /**\n * Additional CSS class name applied to the outermost wrapper div of BoltTable.\n * Use this to apply custom sizing, border, or shadow to the table container.\n *\n * @example\n * className=\"rounded-lg border shadow-sm\"\n */\n readonly className?: string;\n\n /**\n * Granular CSS class name overrides for specific parts of the table.\n * Each key targets a different region of the table.\n *\n * @example\n * classNames={{\n * header: 'text-xs uppercase tracking-wider',\n * cell: 'text-sm',\n * row: 'border-b',\n * pinnedHeader: 'bg-blue-50',\n * pinnedCell: 'bg-blue-50/50',\n * }}\n */\n readonly classNames?: ClassNamesTypes;\n\n /**\n * Inline style overrides for specific parts of the table.\n * Applied after all default and className-based styles, so these take\n * the highest specificity.\n *\n * Note: `pinnedBg` is a special string property (not CSSProperties) that\n * sets the background color of pinned column cells and headers directly.\n *\n * @example\n * styles={{\n * header: { fontSize: 12, fontWeight: 600 },\n * pinnedBg: 'rgba(239, 246, 255, 0.9)',\n * rowHover: { backgroundColor: '#f0f9ff' },\n * rowSelected: { backgroundColor: '#dbeafe' },\n * }}\n */\n readonly styles?: StylesTypes;\n\n /**\n * A custom React node to use as the drag grip icon in column headers.\n * When omitted, the default `GripVertical` icon from lucide-react is used.\n * Ignored when `hideGripIcon` is `true`.\n *\n * @example\n * gripIcon={<DragHandleIcon className=\"h-3 w-3\" />}\n */\n readonly gripIcon?: React.ReactNode;\n\n /**\n * When `true`, the drag grip icon is hidden from all column headers.\n * Columns can still be dragged even without the grip icon.\n *\n * @default false\n *\n * @example\n * hideGripIcon={true}\n */\n readonly hideGripIcon?: boolean;\n\n /**\n * Pagination configuration for the footer, or `false` to disable pagination entirely.\n *\n * **Client-side pagination** (pass all data, BoltTable slices it):\n * ```tsx\n * pagination={{ pageSize: 20 }}\n * ```\n *\n * **Server-side pagination** (pass only current page's data):\n * ```tsx\n * pagination={{ current: page, pageSize: 20, total: 500 }}\n * onPaginationChange={(page, size) => fetchPage(page, size)}\n * ```\n *\n * **Disable pagination:**\n * ```tsx\n * pagination={false}\n * ```\n *\n * @default undefined (pagination footer shown with default settings)\n */\n readonly pagination?: PaginationType | false;\n\n /**\n * Called when the user changes the current page or page size via the pagination footer.\n * Required for server-side pagination. For client-side pagination, this is optional\n * (BoltTable handles page changes internally).\n *\n * @param page - The new page number (1-based)\n * @param pageSize - The new page size\n *\n * @example\n * onPaginationChange={(page, size) => {\n * setCurrentPage(page);\n * setPageSize(size);\n * fetchData({ page, size });\n * }}\n */\n readonly onPaginationChange?: (page: number, pageSize: number) => void;\n\n /**\n * Called when the user finishes resizing a column (on mouse up).\n * Use this to persist the new width to your state or storage.\n *\n * @param columnKey - The `key` of the resized column\n * @param newWidth - The new column width in pixels\n *\n * @example\n * onColumnResize={(key, width) => {\n * setColumnWidths(prev => ({ ...prev, [key]: width }));\n * }}\n */\n readonly onColumnResize?: (columnKey: string, newWidth: number) => void;\n\n /**\n * Called after the user drops a column header into a new position.\n * Receives the full new column key order.\n * Use this to persist the order to your state or storage.\n *\n * @param newOrder - Array of all column keys in their new order\n *\n * @example\n * onColumnOrderChange={(order) => {\n * setColumnOrder(order);\n * }}\n */\n readonly onColumnOrderChange?: (newOrder: string[]) => void;\n\n /**\n * Called when the user pins or unpins a column via the context menu.\n *\n * @param columnKey - The `key` of the column whose pin state changed\n * @param pinned - The new pin state: `'left'`, `'right'`, or `false`\n *\n * @example\n * onColumnPin={(key, pinned) => {\n * setColumns(prev => prev.map(col =>\n * col.key === key ? { ...col, pinned } : col\n * ));\n * }}\n */\n readonly onColumnPin?: (\n columnKey: string,\n pinned: 'left' | 'right' | false,\n ) => void;\n\n /**\n * Called when the user hides or shows a column via the context menu.\n * Note: pinned columns cannot be hidden.\n *\n * @param columnKey - The `key` of the column whose visibility changed\n * @param hidden - `true` if the column is now hidden, `false` if now visible\n *\n * @example\n * onColumnHide={(key, hidden) => {\n * setColumns(prev => prev.map(col =>\n * col.key === key ? { ...col, hidden } : col\n * ));\n * }}\n */\n readonly onColumnHide?: (columnKey: string, hidden: boolean) => void;\n\n /**\n * Determines the unique key for each row. Used for selection, expansion,\n * and stable virtualizer item keys.\n *\n * Can be:\n * - A **string**: the name of a property on the row object (e.g. `'id'`)\n * - A **function**: `(record) => string` for computed keys\n * - A **number** or **symbol**: property access by index/symbol\n *\n * Always returns a string internally (numbers/symbols are coerced to string).\n *\n * @default 'id'\n *\n * @example\n * rowKey=\"id\"\n * rowKey={(record) => `${record.type}-${record.id}`}\n */\n readonly rowKey?: string | ((record: T) => string) | number | symbol;\n\n /**\n * Row selection configuration. When provided, prepends a checkbox (or radio)\n * column to the left of the table.\n *\n * BoltTable does not manage selection state internally — you must track\n * `selectedRowKeys` in your own state and update it in `onChange`.\n *\n * @example\n * rowSelection={{\n * type: 'checkbox',\n * selectedRowKeys,\n * onChange: (keys) => setSelectedRowKeys(keys),\n * }}\n */\n expandable?: ExpandableConfig<T>;\n\n /**\n * Expandable row configuration. When provided, prepends an expand toggle\n * column to the left of the table (to the right of the selection column\n * if both are used).\n *\n * Supports both controlled (`expandedRowKeys`) and uncontrolled modes.\n *\n * @example\n * expandable={{\n * rowExpandable: (record) => record.hasDetails,\n * expandedRowRender: (record) => <DetailPanel record={record} />,\n * }}\n */\n readonly rowSelection?: RowSelectionConfig<T>;\n\n /**\n * Called when the user scrolls near the bottom of the table.\n * Use this for infinite scroll / load-more behavior.\n * Fires when the last visible row is within `onEndReachedThreshold` rows of the end.\n *\n * A debounce guard prevents this from firing repeatedly — it resets automatically\n * when `data.length` changes or when `isLoading` flips back to `false`.\n *\n * @example\n * onEndReached={() => {\n * if (!isLoading) fetchNextPage();\n * }}\n */\n readonly onEndReached?: () => void;\n\n /**\n * How many rows from the end of the list should trigger `onEndReached`.\n * A higher value triggers loading earlier (more buffer); lower means later.\n *\n * @default 5\n *\n * @example\n * onEndReachedThreshold={10}\n */\n readonly onEndReachedThreshold?: number;\n\n /**\n * When `true` and `data` is empty, the table renders animated shimmer\n * skeleton rows instead of the empty state or real data.\n *\n * When `true` and `data` is non-empty (e.g. loading the next page in\n * infinite scroll), a small number of shimmer rows are appended at the\n * bottom below the real data.\n *\n * @default false\n *\n * @example\n * isLoading={isFetching}\n */\n readonly isLoading?: boolean;\n\n /**\n * Scroll indicator configuration (reserved for future use).\n * Currently unused but accepted to avoid prop-drilling issues in parent components.\n */\n readonly scrollIndicators?: { vertical?: boolean; horizontal?: boolean };\n\n /**\n * Called when the user changes the sort direction via the column header context menu.\n *\n * **Server-side sorting**: provide this callback. BoltTable will NOT sort the\n * data locally — it will pass the event to you and display the data as-is.\n *\n * **Client-side sorting** (default): omit this callback. BoltTable will sort\n * the data locally using `column.sorter` or a default comparator.\n *\n * @param columnKey - The `key` of the column being sorted\n * @param direction - The new sort direction, or `null` to clear the sort\n *\n * @example\n * // Server-side\n * onSortChange={(key, dir) => {\n * setSortKey(key);\n * setSortDir(dir);\n * refetch({ sortKey: key, sortDir: dir });\n * }}\n */\n readonly onSortChange?: (columnKey: string, direction: SortDirection) => void;\n\n /**\n * Called when the user applies or clears a column filter via the context menu.\n *\n * **Server-side filtering**: provide this callback. BoltTable will NOT filter\n * the data locally — it passes the full filters map to you.\n *\n * **Client-side filtering** (default): omit this callback. BoltTable will filter\n * locally using `column.filterFn` or a default case-insensitive substring match.\n *\n * @param filters - A map of `{ [columnKey]: filterValue }` for all active filters.\n * A column is removed from the map when its filter is cleared.\n *\n * @example\n * // Server-side\n * onFilterChange={(filters) => {\n * setActiveFilters(filters);\n * refetch({ filters });\n * }}\n */\n readonly onFilterChange?: (filters: Record<string, string>) => void;\n\n /**\n * Custom items to append to the bottom of the right-click context menu\n * that appears on column headers. These appear after the built-in\n * sort / filter / pin / hide options.\n *\n * @example\n * columnContextMenuItems={[\n * {\n * key: 'copy-col',\n * label: 'Copy column data',\n * icon: <CopyIcon className=\"h-3 w-3\" />,\n * onClick: (columnKey) => copyColumnToClipboard(columnKey),\n * },\n * ]}\n */\n readonly columnContextMenuItems?: ColumnContextMenuItem[];\n\n /**\n * Controls how the table's height is determined.\n *\n * - `true` (default): the table **auto-sizes** to its content, up to a maximum\n * of 10 rows. Shorter tables occupy less vertical space. The container uses\n * `maxHeight` so a smaller flex parent can still clip it.\n *\n * - `false`: the table fills its parent container (`height: 100%`). The parent\n * is fully responsible for providing a height. Use this when BoltTable is\n * placed inside a fixed-height container (e.g. a modal, a resizable panel).\n *\n * @default true\n *\n * @example\n * // Table inside a fixed-height panel\n * autoHeight={false}\n */\n readonly autoHeight?: boolean;\n\n /**\n * When `true`, renders a full shimmer skeleton layout (including column headers)\n * before the table's column widths have been calculated from real data.\n *\n * Use this for the initial page load when you don't yet know the column widths\n * but want to show a realistic skeleton immediately.\n *\n * Differs from `isLoading`:\n * - `layoutLoading=true` → entire grid (headers + rows) is a skeleton\n * - `isLoading=true` → headers are real, only body rows are skeletons\n *\n * @default false\n *\n * @example\n * layoutLoading={!columnsResolved}\n */\n readonly layoutLoading?: boolean;\n\n /**\n * Custom React node to render when the table has no data and is not loading.\n * Replaces the default \"No data\" message.\n * The empty state is centered in the visible viewport (sticky left + fixed width)\n * so it always appears centered even when the table is scrolled horizontally.\n *\n * @example\n * emptyRenderer={\n * <div className=\"flex flex-col items-center gap-2 py-12 text-muted-foreground\">\n * <SearchX className=\"h-8 w-8\" />\n * <p>No results found</p>\n * </div>\n * }\n */\n readonly emptyRenderer?: React.ReactNode;\n}\n\n/**\n * CSS class name overrides for specific regions of BoltTable.\n * All fields are optional — omit any you don't need to customize.\n *\n * @example\n * const classNames: ClassNamesTypes = {\n * header: 'text-xs font-semibold uppercase text-gray-500',\n * cell: 'text-sm text-gray-900',\n * pinnedHeader: 'bg-blue-50 border-r border-blue-200',\n * pinnedCell: 'bg-blue-50/50',\n * };\n */\nexport interface ClassNamesTypes {\n /**\n * Applied to all non-pinned column header cells.\n * Pinned headers also receive `pinnedHeader` on top of this.\n */\n header?: string;\n\n /** Applied to all body cells (both pinned and non-pinned) */\n cell?: string;\n\n /** Applied to each row's wrapper element (reserved for future use) */\n row?: string;\n\n /**\n * Applied to the floating drag overlay header shown while dragging a column.\n * Use this to style the \"ghost\" column that follows the cursor.\n */\n dragHeader?: string;\n\n /**\n * Applied additionally to pinned column header cells (on top of `header`).\n * Use this to add a border, background, or shadow to distinguish pinned headers.\n */\n pinnedHeader?: string;\n\n /**\n * Applied additionally to pinned column body cells.\n * Use this to add a border or separator between pinned and scrolling columns.\n */\n pinnedCell?: string;\n\n /**\n * Applied to the expanded row content panel (the div below an expanded row).\n * Does not affect the row itself, only the expanded panel.\n */\n expandedRow?: string;\n}\n\n/**\n * Inline style overrides for specific regions of BoltTable.\n * Applied after all default styles, so these take the highest specificity.\n *\n * All fields accept standard React `CSSProperties` except `pinnedBg`,\n * which accepts a CSS color string directly.\n *\n * @example\n * const styles: StylesTypes = {\n * header: { fontSize: 12, letterSpacing: '0.05em' },\n * rowHover: { backgroundColor: '#f8fafc' },\n * rowSelected: { backgroundColor: '#eff6ff' },\n * pinnedBg: 'rgba(239, 246, 255, 0.95)',\n * };\n */\nexport interface StylesTypes {\n /** Inline styles for all non-pinned column header cells */\n header?: CSSProperties;\n\n /** Inline styles for all body cells */\n cell?: CSSProperties;\n\n /** Inline styles for each row wrapper (reserved for future use) */\n row?: CSSProperties;\n\n /**\n * Inline styles for the drag overlay header shown while dragging a column.\n * Applied on top of `header` styles.\n */\n dragHeader?: CSSProperties;\n\n /**\n * Inline styles for pinned column header cells.\n * Applied on top of `header` styles.\n */\n pinnedHeader?: CSSProperties;\n\n /**\n * Inline styles for pinned column body cells.\n * Applied on top of `cell` styles.\n */\n pinnedCell?: CSSProperties;\n\n /** Inline styles for the expanded row content panel */\n expandedRow?: CSSProperties;\n\n /**\n * CSS color string applied as the background of hovered rows.\n * Defaults to `hsl(var(--muted) / 0.5)` if omitted.\n *\n * @example\n * rowHover: { backgroundColor: '#f8fafc' }\n */\n rowHover?: CSSProperties;\n\n /**\n * CSS color string applied as the background tint of selected rows.\n * Defaults to `${accentColor}15` (accentColor at 8% opacity).\n *\n * @example\n * rowSelected: { backgroundColor: '#dbeafe' }\n */\n rowSelected?: CSSProperties;\n\n /**\n * CSS color string for the background of pinned column cells and headers.\n * Accepts any valid CSS color. Defaults to a semi-transparent white/dark\n * based on the current theme.\n *\n * @example\n * pinnedBg: 'rgba(239, 246, 255, 0.9)'\n */\n pinnedBg?: string;\n}\n\n/**\n * Shimmer bar widths (%) used for the layout-loading skeleton.\n * Cycled deterministically per (rowIndex × 7 + colIndex) so different cells\n * show different widths without randomness (no hydration mismatches in SSR).\n */\nconst SHIMMER_WIDTHS = [55, 70, 45, 80, 60, 50, 75, 65, 40, 72];\n\n/**\n * BoltTable — high-performance virtualized React table.\n *\n * Renders only the rows currently visible in the viewport using TanStack Virtual,\n * making it suitable for datasets of any size without performance degradation.\n *\n * @typeParam T - Your row data type. Must extend `DataRecord` (i.e. `Record<string, unknown>`).\n *\n * @example\n * // Minimal usage\n * <BoltTable\n * columns={[\n * { key: 'name', dataIndex: 'name', title: 'Name' },\n * { key: 'email', dataIndex: 'email', title: 'Email' },\n * ]}\n * data={users}\n * />\n *\n * @example\n * // Full-featured server-side example\n * <BoltTable<User>\n * columns={columns}\n * data={pageData}\n * rowKey=\"id\"\n * isLoading={isFetching}\n * pagination={{ current: page, pageSize: 20, total: totalCount }}\n * onPaginationChange={(p, size) => fetchPage(p, size)}\n * onSortChange={(key, dir) => refetch({ sortKey: key, sortDir: dir })}\n * onFilterChange={(filters) => refetch({ filters })}\n * rowSelection={{ selectedRowKeys, onChange: (keys) => setSelectedRowKeys(keys) }}\n * expandable={{\n * rowExpandable: (r) => r.hasDetails,\n * expandedRowRender: (r) => <DetailPanel record={r} />,\n * }}\n * accentColor=\"#6366f1\"\n * autoHeight={false}\n * />\n */\nexport default function BoltTable<T extends DataRecord = DataRecord>({\n columns: initialColumns,\n data,\n rowHeight = 40,\n expandedRowHeight = 200,\n maxExpandedRowHeight,\n accentColor = '#1890ff',\n className = '',\n classNames = {},\n styles = {},\n gripIcon,\n hideGripIcon,\n pagination,\n onPaginationChange,\n onColumnResize,\n onColumnOrderChange,\n onColumnPin,\n onColumnHide,\n rowSelection,\n expandable,\n rowKey = 'id',\n onEndReached,\n onEndReachedThreshold = 5,\n isLoading = false,\n onSortChange,\n onFilterChange,\n columnContextMenuItems,\n autoHeight = true,\n layoutLoading,\n emptyRenderer,\n}: BoltTableProps<T>) {\n // ─── Internal column state ─────────────────────────────────────────────────\n // BoltTable maintains its own copy of columns so it can track user-driven\n // changes (pinning, hiding) without mutating the parent's array.\n const [columns, setColumns] = useState<ColumnType<T>[]>(initialColumns);\n const [columnOrder, setColumnOrder] = useState<string[]>(() =>\n initialColumns.map((c) => c.key),\n );\n\n // The key being dragged (null when not dragging)\n const [activeId, setActiveId] = useState<string | null>(null);\n\n // ─── Sync columns from parent ──────────────────────────────────────────────\n // Uses a content fingerprint to detect real changes vs noise:\n // - key: column identity\n // - hidden: visibility state\n // - pinned: pin state\n // - width: rounded to integer to ignore sub-pixel jitter\n //\n // IMPORTANT: the fingerprint comparison happens inside a useEffect, not during\n // render, to avoid \"Cannot update a component while rendering a different\n // component\" errors when the parent re-renders with a new array reference.\n const columnsFingerprintRef = useRef('');\n const newFingerprint = initialColumns\n .map((c) => {\n const w =\n typeof c.width === 'number' ? Math.round(c.width) : (c.width ?? '');\n return `${c.key}:${!!c.hidden}:${c.pinned || ''}:${w}`;\n })\n .join('|');\n\n // Stable ref so the effect always reads the latest initialColumns\n // without adding it to the dependency array\n const initialColumnsRef = useRef(initialColumns);\n initialColumnsRef.current = initialColumns;\n\n React.useEffect(() => {\n if (columnsFingerprintRef.current === newFingerprint) return;\n columnsFingerprintRef.current = newFingerprint;\n setColumns(initialColumnsRef.current);\n setColumnOrder(initialColumnsRef.current.map((c) => c.key));\n }, [newFingerprint]);\n\n // ─── Persisted column widths ───────────────────────────────────────────────\n // Stored in a separate state bucket so pagination prop changes (which cause\n // BoltTable to re-render with a new `pagination` object) never reset widths\n // that the user has manually adjusted.\n const safeWidth = (w: unknown, fallback = 150): number =>\n typeof w === 'number' && Number.isFinite(w) ? w : fallback;\n\n const [columnWidths, setColumnWidths] = useState<Map<string, number>>(\n () => new Map(),\n );\n // Tracks which columns have been manually resized (used to prevent auto-width\n // recalculations from overriding user-set widths in future enhancements)\n const manuallyResizedRef = useRef<Set<string>>(new Set());\n\n // Merge persisted widths into columns — keeps widths stable across re-renders\n const columnsWithPersistedWidths = useMemo(\n () =>\n columns.map((col) => ({\n ...col,\n width: safeWidth(columnWidths.get(col.key) ?? col.width),\n })),\n [columns, columnWidths],\n );\n\n // ─── Expandable state ──────────────────────────────────────────────────────\n // Supports both controlled (expandedRowKeys from parent) and uncontrolled modes.\n const [internalExpandedKeys, setInternalExpandedKeys] = useState<\n Set<React.Key>\n >(() => {\n if (expandable?.defaultExpandAllRows) {\n return new Set(\n data.map((row, idx) => {\n if (typeof rowKey === 'function') return rowKey(row);\n if (typeof rowKey === 'string')\n return (row[rowKey] as React.Key) ?? idx;\n return idx;\n }),\n );\n }\n return new Set(expandable?.defaultExpandedRowKeys ?? []);\n });\n\n // Use a string fingerprint of the controlled keys to avoid Set reference\n // inequality causing unnecessary re-renders on every render\n const expandedKeysFingerprint = expandable?.expandedRowKeys\n ?.map(String)\n .join('|');\n const resolvedExpandedKeys = useMemo<Set<React.Key>>(() => {\n if (expandable?.expandedRowKeys !== undefined)\n return new Set(expandable.expandedRowKeys);\n return internalExpandedKeys;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [expandedKeysFingerprint, internalExpandedKeys]);\n\n const expandableRef = useRef(expandable);\n expandableRef.current = expandable;\n\n /**\n * Toggles the expanded state of a row.\n * Works in both controlled and uncontrolled modes:\n * - Controlled: derives new set from prop, calls onExpandedRowsChange\n * - Uncontrolled: updates internal state, calls onExpandedRowsChange\n */\n const toggleExpand = useCallback((key: React.Key) => {\n const exp = expandableRef.current;\n if (exp?.expandedRowKeys !== undefined) {\n const next = new Set(exp.expandedRowKeys);\n next.has(key) ? next.delete(key) : next.add(key);\n exp.onExpandedRowsChange?.(Array.from(next));\n } else {\n setInternalExpandedKeys((prev) => {\n const next = new Set(prev);\n next.has(key) ? next.delete(key) : next.add(key);\n exp?.onExpandedRowsChange?.(Array.from(next));\n return next;\n });\n }\n }, []);\n\n // ─── getRowKey ─────────────────────────────────────────────────────────────\n // Always returns a string. Normalizing to string means all key comparisons\n // are string-vs-string regardless of whether parent stores numbers or strings.\n const getRowKey = useCallback(\n (record: T, index: number): string => {\n if (typeof rowKey === 'function') return String(rowKey(record));\n if (typeof rowKey === 'string') {\n const val = record[rowKey];\n return val !== undefined && val !== null ? String(val) : String(index);\n }\n return String(index);\n },\n [rowKey],\n );\n\n // ─── Normalized selected keys ──────────────────────────────────────────────\n // Pre-normalized to strings once here so Cell never has to deal with\n // mixed number/string comparisons in its includes() checks.\n const normalizedSelectedKeys = useMemo<string[]>(\n () => (rowSelection?.selectedRowKeys ?? []).map((k) => String(k)),\n [rowSelection?.selectedRowKeys],\n );\n\n // ─── Inject expand column ─────────────────────────────────────────────────\n // Prepends a synthetic '__expand__' column with a render function that shows\n // the chevron toggle button. This approach keeps the expand logic centralized\n // in BoltTable rather than scattered across TableBody.\n const columnsWithExpand = useMemo(() => {\n if (!expandable?.rowExpandable) return columnsWithPersistedWidths;\n\n const expandColumn: ColumnType<T> = {\n key: '__expand__',\n dataIndex: '__expand__',\n title: '',\n width: 40,\n pinned: 'left',\n hidden: false,\n render: (_, record, index) => {\n const key = getRowKey(record, index);\n const canExpand = expandable.rowExpandable?.(record) ?? true;\n const isExpanded = resolvedExpandedKeys.has(key);\n\n if (!canExpand)\n return <span style={{ display: 'inline-block', width: 16 }} />;\n\n // Allow custom expand icons via expandable.expandIcon\n if (typeof (expandable as any).expandIcon === 'function') {\n return (expandable as { expandIcon?: (args: any) => React.ReactNode })\n .expandIcon!({\n expanded: isExpanded,\n onExpand: (_: T, e: React.MouseEvent) => {\n e.stopPropagation();\n toggleExpand(key);\n },\n record,\n });\n }\n\n // Default expand button: ChevronRight (collapsed) / ChevronDown (expanded)\n return (\n <button\n onClick={(e) => {\n e.stopPropagation();\n toggleExpand(key);\n }}\n style={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n background: 'none',\n border: 'none',\n cursor: 'pointer',\n padding: '2px',\n borderRadius: '3px',\n color: accentColor,\n }}\n >\n {isExpanded ? (\n <ChevronDown style={{ width: 14, height: 14 }} />\n ) : (\n <ChevronRight style={{ width: 14, height: 14 }} />\n )}\n </button>\n );\n },\n };\n\n return [expandColumn, ...columnsWithPersistedWidths];\n }, [\n expandable,\n columnsWithPersistedWidths,\n getRowKey,\n resolvedExpandedKeys,\n toggleExpand,\n accentColor,\n ]);\n\n // ─── Inject selection column ───────────────────────────────────────────────\n // The render function here is intentionally a no-op placeholder.\n // TableBody's Cell component handles the actual checkbox rendering for\n // __select__ cells using normalizedSelectedKeys passed as direct props.\n // This means selection changes never cause this memo or its siblings to re-run.\n const columnsWithSelection = useMemo(() => {\n if (!rowSelection) return columnsWithExpand;\n\n const selectionColumn: ColumnType<T> = {\n key: '__select__',\n dataIndex: '__select__',\n title: '',\n width: 48,\n pinned: 'left',\n hidden: false,\n render: () => null,\n };\n\n return [selectionColumn, ...columnsWithExpand];\n }, [rowSelection, columnsWithExpand]);\n\n // ─── DOM refs ──────────────────────────────────────────────────────────────\n const resizeOverlayRef = useRef<ResizeOverlayHandle>(null);\n const tableAreaRef = useRef<HTMLDivElement>(null);\n const [scrollAreaWidth, setScrollAreaWidth] = useState<number>(0);\n\n // ─── Scroll container width tracking ──────────────────────────────────────\n // Rules to prevent infinite loops:\n // 1. Only track WIDTH (never height — height changes cause DOM changes → loop).\n // 2. Guard with prevWidth ref — setState only fires when value actually changed.\n // 3. Debounce via requestAnimationFrame so multiple ResizeObserver entries\n // in the same frame collapse into one setState call.\n // 4. Use a callback ref (not useLayoutEffect([])) so the observer re-attaches\n // whenever the div mounts/unmounts (layoutLoading ternary swaps the element).\n const prevScrollAreaWidthRef = useRef<number>(0);\n const roRef = useRef<ResizeObserver | null>(null);\n const rafRef = useRef<number | null>(null);\n\n /**\n * Callback ref for the scroll container div.\n * Attaches a ResizeObserver to track the container's width for:\n * - Sizing the empty state panel to the visible viewport width\n * - Sizing expanded row panels to the visible viewport width\n *\n * Also keeps `tableAreaRef.current` in sync for all existing code\n * that reads it (virtualizer, scroll listeners, resize overlay).\n */\n const tableAreaCallbackRef = useCallback((el: HTMLDivElement | null) => {\n roRef.current?.disconnect();\n roRef.current = null;\n if (rafRef.current !== null) {\n cancelAnimationFrame(rafRef.current);\n rafRef.current = null;\n }\n\n (tableAreaRef as React.MutableRefObject<HTMLDivElement | null>).current =\n el;\n\n if (!el) return;\n\n const measure = () => {\n rafRef.current = null;\n const w = el.clientWidth;\n if (w !== prevScrollAreaWidthRef.current) {\n prevScrollAreaWidthRef.current = w;\n setScrollAreaWidth(w);\n }\n };\n\n measure();\n\n const ro = new ResizeObserver(() => {\n if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);\n rafRef.current = requestAnimationFrame(measure);\n });\n ro.observe(el);\n roRef.current = ro;\n }, []);\n\n // ─── Row hover (zero re-renders — pure DOM) ────────────────────────────────\n // Hover state is tracked via DOM attribute mutation (`data-hover`) instead of\n // React state. This means hovering over rows never causes any React re-renders.\n // The CSS injected in the JSX below targets `[data-row-key][data-hover] > div`\n // to apply the hover background across all column divs for the same row.\n const hoveredRowRef = useRef<string | null>(null);\n\n React.useEffect(() => {\n const el = tableAreaRef.current;\n if (!el) return;\n\n const setHover = (key: string | null) => {\n if (hoveredRowRef.current === key) return;\n // Remove data-hover from the previously hovered row's cells\n if (hoveredRowRef.current) {\n el.querySelectorAll(\n `[data-row-key=\"${hoveredRowRef.current}\"]`,\n ).forEach((n) => (n as HTMLElement).removeAttribute('data-hover'));\n }\n hoveredRowRef.current = key;\n // Add data-hover to all cells with the new hovered row key\n if (key) {\n el.querySelectorAll(`[data-row-key=\"${key}\"]`).forEach((n) =>\n (n as HTMLElement).setAttribute('data-hover', ''),\n );\n }\n };\n\n const onOver = (e: MouseEvent) => {\n const target = (e.target as HTMLElement).closest<HTMLElement>(\n '[data-row-key]',\n );\n setHover(target?.dataset.rowKey ?? null);\n };\n const onLeave = () => setHover(null);\n\n el.addEventListener('mouseover', onOver, { passive: true });\n el.addEventListener('mouseleave', onLeave, { passive: true });\n return () => {\n el.removeEventListener('mouseover', onOver);\n el.removeEventListener('mouseleave', onLeave);\n };\n }, []);\n\n // ─── Column resize state ───────────────────────────────────────────────────\n // Stored in a ref (not state) because resize tracking happens on mousemove\n // and must not trigger re-renders during the drag. React state is only\n // updated on mouseup (handleResizeEnd) when the final width is committed.\n const resizeStateRef = useRef<{\n columnKey: string;\n startX: number;\n startWidth: number;\n columnIndex: number;\n currentX: number;\n } | null>(null);\n\n // ─── DnD sensors ──────────────────────────────────────────────────────────\n // PointerSensor handles both mouse and touch drag events.\n const sensors = useSensors(useSensor(PointerSensor));\n\n /**\n * Called when the user starts dragging a column header.\n * System columns (__select__, __expand__) cannot be dragged.\n */\n const handleDragStart = (event: DragStartEvent) => {\n if (event.active.id === '__select__' || event.active.id === '__expand__')\n return;\n setActiveId(event.active.id as string);\n };\n\n /**\n * Called when the user drops a column header into a new position.\n * Updates column order state and notifies the parent via onColumnOrderChange.\n * The parent callback is deferred with setTimeout to avoid firing inside a\n * React updater (which would cause \"Cannot update a component while rendering\n * a different component\" if the parent calls its own setState).\n */\n const handleDragEnd = (event: DragEndEvent) => {\n const { active, over } = event;\n if (over && active.id !== over.id) {\n setColumnOrder((items) => {\n const oldIndex = items.indexOf(active.id as string);\n const newIndex = items.indexOf(over.id as string);\n const newOrder = arrayMove(items, oldIndex, newIndex);\n setTimeout(() => onColumnOrderChange?.(newOrder), 0);\n return newOrder;\n });\n }\n setActiveId(null);\n };\n\n /**\n * Called on mousedown of a column's resize handle.\n * Captures the start position and initial width, then shows the ResizeOverlay.\n * Pinned columns and system columns (__select__, __expand__) cannot be resized.\n */\n const handleResizeStart = (columnKey: string, e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n if (columnKey === '__select__' || columnKey === '__expand__') return;\n\n const columnIndex = columnsWithSelection.findIndex(\n (col) => col.key === columnKey,\n );\n if (columnIndex === -1) return;\n if (columnsWithSelection[columnIndex].pinned) return;\n\n const column = columnsWithSelection[columnIndex];\n const startWidth = column.width ?? 150;\n\n resizeStateRef.current = {\n columnKey,\n startX: e.clientX,\n startWidth,\n columnIndex,\n currentX: e.clientX,\n };\n\n // Show the resize overlay (vertical line + label)\n if (tableAreaRef.current) {\n const headerElement = tableAreaRef.current.querySelector(\n `[data-column-key=\"${columnKey}\"]`,\n );\n if (headerElement) {\n const areaRect = tableAreaRef.current.getBoundingClientRect();\n const headerRect = headerElement.getBoundingClientRect();\n const scrollTop = tableAreaRef.current.scrollTop;\n const scrollLeft = tableAreaRef.current.scrollLeft;\n const headerLeftInContent =\n headerRect.left - areaRect.left + scrollLeft;\n\n resizeOverlayRef.current?.show(\n headerRect.right,\n typeof column.title === 'string' ? column.title : String(column.key),\n areaRect,\n headerLeftInContent,\n 40, // minimum column width\n scrollTop,\n scrollLeft,\n headerLeftInContent + startWidth,\n );\n }\n }\n\n document.addEventListener('mousemove', handleResizeMove);\n document.addEventListener('mouseup', handleResizeEnd);\n };\n\n /**\n * Called on every mousemove during a column resize drag.\n * Updates the overlay line position via direct DOM mutation (no React state).\n */\n const handleResizeMove = (e: MouseEvent) => {\n if (!resizeStateRef.current) return;\n resizeStateRef.current.currentX = e.clientX;\n resizeOverlayRef.current?.move(e.clientX);\n };\n\n /**\n * Called on mouseup to commit the new column width.\n * Updates column widths state, notifies the parent, and hides the overlay.\n */\n const handleResizeEnd = React.useCallback(() => {\n if (!resizeStateRef.current) return;\n const { startX, startWidth, currentX, columnKey } = resizeStateRef.current;\n const finalWidth = Math.max(40, startWidth + (currentX - startX));\n\n manuallyResizedRef.current.add(columnKey);\n\n setColumnWidths((prev) => {\n const next = new Map(prev);\n next.set(columnKey, finalWidth);\n return next;\n });\n\n onColumnResize?.(columnKey, finalWidth);\n resizeOverlayRef.current?.hide();\n resizeStateRef.current = null;\n document.removeEventListener('mousemove', handleResizeMove);\n document.removeEventListener('mouseup', handleResizeEnd);\n }, [onColumnResize]);\n\n // ─── Column ordering & pinning ─────────────────────────────────────────────\n // Computes three groups: leftPinned, unpinned, rightPinned.\n // System columns (__select__, __expand__) are always prepended before the\n // user's column order, regardless of where they appear in columnOrder.\n const { leftPinned, unpinned, rightPinned } = useMemo(() => {\n const columnMap = new Map(columnsWithSelection.map((c) => [c.key, c]));\n const systemKeys = [\n ...(rowSelection ? ['__select__'] : []),\n ...(expandable ? ['__expand__'] : []),\n ];\n\n const visibleColumns = [...systemKeys, ...columnOrder]\n .map((key) => columnMap.get(key))\n .filter((col): col is ColumnType<T> => col !== undefined && !col.hidden);\n\n const left: ColumnType<T>[] = [],\n center: ColumnType<T>[] = [],\n right: ColumnType<T>[] = [];\n visibleColumns.forEach((col) => {\n if (col.pinned === 'left') left.push(col);\n else if (col.pinned === 'right') right.push(col);\n else center.push(col);\n });\n return { leftPinned: left, unpinned: center, rightPinned: right };\n }, [columnOrder, columnsWithSelection, rowSelection, expandable]);\n\n // Final ordered columns: left pinned → center → right pinned\n const orderedColumns = useMemo(\n () => [...leftPinned, ...unpinned, ...rightPinned],\n [leftPinned, unpinned, rightPinned],\n );\n\n // Total pixel width of all columns (used for minWidth on the grid)\n const totalTableWidth = useMemo(\n () =>\n orderedColumns\n .slice(0, -1)\n .reduce((sum, col) => sum + (col.width ?? 150), 0) +\n (orderedColumns.at(-1)?.width ?? 150),\n [orderedColumns],\n );\n\n // CSS gridTemplateColumns string — last column uses minmax() to stretch\n const gridTemplateColumns = useMemo(() => {\n if (orderedColumns.length === 0) return '';\n return orderedColumns\n .map((col, i) => {\n const w = col.width ?? 150;\n return i === orderedColumns.length - 1\n ? `minmax(${w}px, 1fr)`\n : `${w}px`;\n })\n .join(' ');\n }, [orderedColumns]);\n\n // Pixel offsets for pinned columns (left offset for left-pinned, right offset for right-pinned)\n const columnOffsets = useMemo(() => {\n const offsets = new Map<string, number>();\n let lo = 0;\n leftPinned.forEach((col) => {\n offsets.set(col.key, lo);\n lo += col.width ?? 150;\n });\n let ro = 0;\n for (let i = rightPinned.length - 1; i >= 0; i--) {\n const col = rightPinned[i];\n offsets.set(col.key, ro);\n ro += col.width ?? 150;\n }\n return offsets;\n }, [leftPinned, rightPinned]);\n\n /**\n * Updates a column's pinned state in internal column state and notifies parent.\n */\n const handleTogglePin = (\n columnKey: string,\n pinned: 'left' | 'right' | false,\n ) => {\n setColumns((prev) =>\n prev.map((col) => (col.key === columnKey ? { ...col, pinned } : col)),\n );\n onColumnPin?.(columnKey, pinned);\n };\n\n /**\n * Toggles a column's hidden state. Pinned columns cannot be hidden.\n * Notifies parent via onColumnHide.\n */\n const handleToggleHide = (columnKey: string) => {\n setColumns((prev) =>\n prev.map((col) => {\n if (col.key !== columnKey || col.pinned) return col;\n return { ...col, hidden: !col.hidden };\n }),\n );\n const column = columns.find((col) => col.key === columnKey);\n if (column && !column.pinned) onColumnHide?.(columnKey, !column.hidden);\n };\n\n // ─── Sorting ───────────────────────────────────────────────────────────────\n // When onSortChange is provided → server-side: delegate to parent, no local sort.\n // When undefined → client-side: sort locally inside BoltTable.\n const onSortChangeRef = useRef(onSortChange);\n onSortChangeRef.current = onSortChange;\n\n const [sortState, setSortState] = useState<{\n key: string;\n direction: SortDirection;\n }>({ key: '', direction: null });\n\n /**\n * Handles a sort request from a column header context menu.\n *\n * Toggle cycle (no explicit direction): unsorted → asc → desc → unsorted\n * Explicit direction: sets that direction, or clears it if already active.\n */\n const handleSort = useCallback(\n (columnKey: string, direction?: SortDirection) => {\n setSortState((prev) => {\n let next: SortDirection;\n if (direction !== undefined) {\n next =\n prev.key === columnKey && prev.direction === direction\n ? null\n : direction;\n } else {\n next =\n prev.key !== columnKey\n ? 'asc'\n : prev.direction === 'asc'\n ? 'desc'\n : prev.direction === 'desc'\n ? null\n : 'asc';\n }\n const state = { key: next ? columnKey : '', direction: next };\n onSortChangeRef.current?.(columnKey, next);\n return state;\n });\n },\n [],\n );\n\n // ─── Column filters ────────────────────────────────────────────────────────\n // When onFilterChange is provided → server-side: delegate to parent, no local filter.\n // When undefined → client-side: filter locally inside BoltTable.\n const [columnFilters, setColumnFilters] = useState<Record<string, string>>(\n {},\n );\n\n /**\n * Applies a filter value to a column.\n * Removes the key from the filters map when value is empty (cleared).\n * Notifies parent via onFilterChange with the full updated filters map.\n */\n const handleColumnFilter = useCallback(\n (columnKey: string, value: string) => {\n setColumnFilters((prev) => {\n const next = { ...prev };\n if (value) next[columnKey] = value;\n else delete next[columnKey];\n onFilterChange?.(next);\n return next;\n });\n },\n [onFilterChange],\n );\n\n /**\n * Clears the filter for a specific column.\n * Convenience wrapper around handleColumnFilter.\n */\n const handleClearFilter = useCallback(\n (columnKey: string) => {\n handleColumnFilter(columnKey, '');\n },\n [handleColumnFilter],\n );\n\n // ─── Local sort + filter ───────────────────────────────────────────────────\n // Only applies when the corresponding server-side callback is NOT provided.\n const onFilterChangeRef = useRef(onFilterChange);\n onFilterChangeRef.current = onFilterChange;\n const columnsLookupRef = useRef(initialColumns);\n columnsLookupRef.current = initialColumns;\n\n const processedData = useMemo(() => {\n let result = data;\n\n // Client-side filter (skipped when onFilterChange is provided)\n if (!onFilterChangeRef.current) {\n const filterKeys = Object.keys(columnFilters);\n if (filterKeys.length > 0) {\n result = result.filter((row) =>\n filterKeys.every((key) => {\n const col = columnsLookupRef.current.find((c) => c.key === key);\n if (typeof col?.filterFn === 'function') {\n return col.filterFn(columnFilters[key], row, col.dataIndex);\n }\n // Default: case-insensitive substring match\n const cellVal = String(row[key] ?? '').toLowerCase();\n return cellVal.includes(columnFilters[key].toLowerCase());\n }),\n );\n }\n }\n\n // Client-side sort (skipped when onSortChange is provided)\n if (!onSortChangeRef.current && sortState.key && sortState.direction) {\n const dir = sortState.direction === 'asc' ? 1 : -1;\n const key = sortState.key;\n const col = columnsLookupRef.current.find((c) => c.key === key);\n\n if (typeof col?.sorter === 'function') {\n const sorterFn = col.sorter;\n result = [...result].sort((a, b) => sorterFn(a, b) * dir);\n } else {\n result = [...result].sort((a, b) => {\n const aVal = a[key];\n const bVal = b[key];\n if (aVal == null && bVal == null) return 0;\n if (aVal == null) return 1;\n if (bVal == null) return -1;\n if (typeof aVal === 'number' && typeof bVal === 'number')\n return (aVal - bVal) * dir;\n return String(aVal).localeCompare(String(bVal)) * dir;\n });\n }\n }\n\n return result;\n }, [data, sortState, columnFilters]);\n\n // ─── Scroll to top when filters change ────────────────────────────────────\n // Prevents the user from being stuck on page 3 after narrowing the filter\n // results to only 1 page.\n const columnFiltersKey = Object.keys(columnFilters)\n .sort()\n .map((k) => `${k}:${columnFilters[k]}`)\n .join('|');\n React.useEffect(() => {\n tableAreaRef.current?.scrollTo({ top: 0 });\n }, [columnFiltersKey]);\n\n // ─── Client-side pagination ────────────────────────────────────────────────\n // When the parent passes ALL data and pagination is enabled, BoltTable\n // slices to the current page. Server-side paginated data (already one page)\n // passes through unmodified since data.length <= pageSize in that case.\n const pgEnabled = pagination !== false && !!pagination;\n const pgSize = pgEnabled ? (pagination.pageSize ?? 10) : 10;\n const pgCurrent = pgEnabled ? Number(pagination.current ?? 1) : 1;\n const needsClientPagination = pgEnabled && processedData.length > pgSize;\n\n const paginatedData = useMemo(() => {\n if (!needsClientPagination) return processedData;\n const start = (pgCurrent - 1) * pgSize;\n return processedData.slice(start, start + pgSize);\n }, [processedData, needsClientPagination, pgCurrent, pgSize]);\n\n // ─── Shimmer data ──────────────────────────────────────────────────────────\n // Full-screen shimmer: data is empty AND isLoading=true\n const shimmerCount = pgEnabled ? pgSize : 15;\n const showShimmer = isLoading && processedData.length === 0;\n const shimmerData = useMemo(() => {\n if (!showShimmer) return null;\n return Array.from(\n { length: shimmerCount },\n (_, i) =>\n ({\n [typeof rowKey === 'string' ? rowKey : 'id']: `__shimmer_${i}__`,\n }) as T,\n );\n }, [showShimmer, shimmerCount, rowKey]);\n\n // ─── Infinite scroll shimmer ───────────────────────────────────────────────\n // Appends shimmer rows below real data while loading the next page\n const INFINITE_SHIMMER_COUNT = 5;\n const infiniteLoadingShimmer = useMemo(() => {\n if (!isLoading || paginatedData.length === 0 || showShimmer) return null;\n if (pgEnabled) return null;\n return Array.from(\n { length: INFINITE_SHIMMER_COUNT },\n (_, i) =>\n ({\n [typeof rowKey === 'string' ? rowKey : 'id']: `__shimmer_${i}__`,\n }) as T,\n );\n }, [isLoading, paginatedData.length, showShimmer, pgEnabled, rowKey]);\n\n // Final data handed to the virtualizer\n const displayData = useMemo(() => {\n if (shimmerData) return shimmerData;\n if (infiniteLoadingShimmer)\n return [...paginatedData, ...infiniteLoadingShimmer];\n return paginatedData;\n }, [shimmerData, infiniteLoadingShimmer, paginatedData]);\n\n // ─── Expanded row height measurement ──────────────────────────────────────\n // Cache of measured content heights keyed by row key.\n // Updated by MeasuredExpandedRow via onExpandedRowResize.\n const measuredExpandedHeights = useRef<Map<string, number>>(new Map());\n\n // Debounce RAF ref — collapses rapid ResizeObserver callbacks into one virtualizer.measure()\n const expandedRowMeasureRafRef = useRef<number | null>(null);\n\n /**\n * Called by MeasuredExpandedRow when an expanded row's content height changes.\n * Updates the cached height and triggers a virtualizer re-measure.\n * Debounced via requestAnimationFrame to batch rapid resize events.\n */\n const handleExpandedRowResize = useCallback(\n (rk: string, contentHeight: number) => {\n const prev = measuredExpandedHeights.current.get(rk);\n const rounded = Math.round(contentHeight);\n if (prev === rounded) return;\n measuredExpandedHeights.current.set(rk, rounded);\n if (expandedRowMeasureRafRef.current !== null) {\n cancelAnimationFrame(expandedRowMeasureRafRef.current);\n }\n expandedRowMeasureRafRef.current = requestAnimationFrame(() => {\n expandedRowMeasureRafRef.current = null;\n rowVirtualizerRef.current?.measure();\n });\n },\n [],\n );\n\n // ─── Virtualizer ───────────────────────────────────────────────────────────\n const rowVirtualizer = useVirtualizer({\n count: displayData.length,\n getScrollElement: () => tableAreaRef.current,\n estimateSize: (index) => {\n if (shimmerData) return rowHeight;\n const key = getRowKey(displayData[index], index);\n if (!resolvedExpandedKeys.has(key)) return rowHeight;\n // Use measured height if available, otherwise fall back to expandedRowHeight estimate\n const cached = measuredExpandedHeights.current.get(key);\n return cached ? rowHeight + cached : rowHeight + expandedRowHeight;\n },\n overscan: 5, // Render 5 extra rows above and below the visible window\n getItemKey: (index) =>\n shimmerData\n ? `__shimmer_${index}__`\n : getRowKey(displayData[index], index),\n });\n\n const rowVirtualizerRef = useRef(rowVirtualizer);\n rowVirtualizerRef.current = rowVirtualizer;\n\n // Re-measure virtualizer when expanded keys change.\n // Uses a string fingerprint to avoid re-measuring when the Set reference\n // changes but the contents are identical.\n const resolvedExpandedKeysFingerprint = Array.from(resolvedExpandedKeys)\n .sort()\n .join(',');\n React.useLayoutEffect(() => {\n rowVirtualizer.measure();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [resolvedExpandedKeysFingerprint]);\n\n // ─── Infinite scroll / end-reach detection ────────────────────────────────\n // Fires onEndReached when the last visible row is within threshold rows of the end.\n const endReachedFiredRef = useRef(false);\n const onEndReachedRef = useRef(onEndReached);\n onEndReachedRef.current = onEndReached;\n const isLoadingRef = useRef(isLoading);\n isLoadingRef.current = isLoading;\n\n // Reset the end-reached guard when new data arrives or loading finishes.\n // The 200ms delay gives the table time to re-render with new data before\n // allowing onEndReached to fire again.\n React.useEffect(() => {\n const timer = setTimeout(() => {\n endReachedFiredRef.current = false;\n }, 200);\n return () => clearTimeout(timer);\n }, [data.length, isLoading]);\n\n React.useEffect(() => {\n const el = tableAreaRef.current;\n if (!el) return;\n\n const checkEndReached = () => {\n if (\n !onEndReachedRef.current ||\n displayData.length === 0 ||\n endReachedFiredRef.current ||\n isLoadingRef.current\n )\n return;\n const virtualItems = rowVirtualizer.getVirtualItems();\n if (virtualItems.length === 0) return;\n const lastVisibleIndex = virtualItems[virtualItems.length - 1].index;\n const distanceFromEnd = displayData.length - 1 - lastVisibleIndex;\n if (distanceFromEnd <= onEndReachedThreshold) {\n endReachedFiredRef.current = true;\n onEndReachedRef.current();\n }\n };\n\n el.addEventListener('scroll', checkEndReached, { passive: true });\n return () => el.removeEventListener('scroll', checkEndReached);\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [displayData.length, onEndReachedThreshold]);\n\n // The column currently being dragged (used to render the DragOverlay)\n const activeColumn = activeId\n ? orderedColumns.find((col) => col.key === activeId)\n : null;\n\n // ─── Pagination values ─────────────────────────────────────────────────────\n const currentPage = pgCurrent;\n const pageSize = pgSize;\n\n const rawTotal = pgEnabled\n ? (pagination.total ??\n (needsClientPagination ? processedData.length : data.length))\n : data.length;\n\n // Freeze the last known good total while loading to prevent \"Showing 1-0 of 0\"\n // flicker on every API call.\n const lastKnownTotalRef = useRef<number>(0);\n if (!isLoading || rawTotal > 0) {\n lastKnownTotalRef.current = rawTotal;\n }\n const total =\n isLoading && lastKnownTotalRef.current > 0\n ? lastKnownTotalRef.current\n : rawTotal;\n\n const totalPages = Math.max(1, Math.ceil(total / pageSize));\n\n const handlePageChange = (p: number) => {\n if (p >= 1 && p <= totalPages) onPaginationChange?.(p, pageSize);\n };\n const handlePageSizeChange = (s: number) => onPaginationChange?.(1, s);\n\n // Scroll to top when page changes in client-side pagination\n React.useEffect(() => {\n if (needsClientPagination) {\n tableAreaRef.current?.scrollTo({ top: 0 });\n }\n }, [pgCurrent, needsClientPagination]);\n\n /**\n * Computes the page numbers to show in the pagination footer.\n * Uses two distinct ellipsis string literals ('ellipsis-left', 'ellipsis-right')\n * so React never sees duplicate keys when both ellipses appear simultaneously.\n *\n * For ≤ 7 total pages, shows all page numbers.\n * For > 7 pages, shows: [1, ..., currentPage-1, currentPage, currentPage+1, ..., last]\n */\n const getPageNumbers = (): (\n | number\n | 'ellipsis-left'\n | 'ellipsis-right'\n )[] => {\n if (totalPages <= 7)\nreturn Array.from({ length: totalPages }, (_: unknown, i: number) => i + 1)\n\n const leftSibling = Math.max(currentPage - 1, 2);\n const showLeftEllipsis = leftSibling > 2;\n const rightSibling = Math.min(currentPage + 1, totalPages - 1);\n const showRightEllipsis = currentPage < totalPages - 3;\n\n if (!showLeftEllipsis && showRightEllipsis)\n return [1, 2, 3, 4, 5, 'ellipsis-right', totalPages];\n if (showLeftEllipsis && !showRightEllipsis)\n return [\n 1,\n 'ellipsis-left',\n...Array.from({ length: 5 }, (_: unknown, i: number) => totalPages - 4 + i),\n ];\n return [\n 1,\n 'ellipsis-left',\n leftSibling,\n currentPage,\n rightSibling,\n 'ellipsis-right',\n totalPages,\n ];\n };\n\n // ─── Height calculation ────────────────────────────────────────────────────\n // autoHeight=true → grows to fit rows, capped at MAX_AUTO_ROWS.\n // Uses maxHeight (not height) so a smaller parent\n // can still constrain it.\n // autoHeight=false → fills parent container via flex-1 / h-full.\n const HEADER_HEIGHT = 36;\n const MAX_AUTO_ROWS = 10;\n const naturalContentHeight = rowVirtualizer.getTotalSize() + HEADER_HEIGHT;\n const maxAutoHeight = MAX_AUTO_ROWS * rowHeight + HEADER_HEIGHT;\n const isEmpty = displayData.length === 0 && !showShimmer;\n const emptyMinHeight = 4 * rowHeight + HEADER_HEIGHT;\n\n const clampedAutoHeight = isEmpty\n ? emptyMinHeight\n : Math.min(naturalContentHeight, maxAutoHeight);\n\n return (\n <DndContext\n sensors={sensors}\n collisionDetection={closestCenter}\n onDragStart={handleDragStart}\n onDragEnd={handleDragEnd}\n >\n <div\n className={`flex ${autoHeight ? 'max-h-full' : 'h-full'} w-full flex-col ${className}`}\n >\n {/*\n * ── Injected CSS for hover/selection ──────────────────────────────\n * Row hover and selection backgrounds are handled via pure CSS attribute\n * selectors targeting data-hover and data-selected attributes.\n * This means no React re-renders occur during hover or selection changes.\n *\n * [data-row-key][data-hover] > div → hover background\n * [data-row-key][data-selected] > div → selected background\n */}\n <style>{`\n [data-row-key][data-hover] > div {\n background-color: ${styles.rowHover?.backgroundColor ?? `hsl(var(--muted) / 0.5)`};\n }\n [data-row-key][data-selected] > div {\n background-color: ${styles.rowSelected?.backgroundColor ?? `${accentColor}15`};\n }\n [data-row-key][data-selected][data-hover] > div {\n background-color: ${styles.rowSelected?.backgroundColor ?? `${accentColor}25`};\n }\n `}</style>\n\n {/*\n * ── Scroll wrapper ───────────────────────────────────────────────\n * autoHeight=true:\n * height + maxHeight = clampedAutoHeight snaps wrapper to content\n * flexShrink=1 allows a smaller flex parent to shrink it\n * autoHeight=false:\n * flex-1 fills remaining parent height (parent must have a height)\n */}\n <div\n className={`relative ${autoHeight ? '' : 'flex-1'}`}\n style={\n autoHeight\n ? {\n height: `${clampedAutoHeight}px`,\n maxHeight: `${clampedAutoHeight}px`,\n flexShrink: 1,\n flexGrow: 0,\n }\n : undefined\n }\n >\n {layoutLoading ? (\n /*\n * ── Layout loading skeleton ──────────────────────────────────\n * Shown when layoutLoading=true. Renders real column headers\n * (based on orderedColumns) alongside shimmer body rows.\n * Used for initial page load when column widths are not yet known.\n */\n <div\n className=\"absolute inset-0 overflow-auto\"\n style={{ contain: 'layout paint' }}\n >\n <div\n style={{\n display: 'grid',\n gridTemplateColumns,\n gridTemplateRows: '36px auto',\n minWidth: `${totalTableWidth}px`,\n width: '100%',\n position: 'relative',\n }}\n >\n {orderedColumns.map((column) => {\n const isPinned = !!column.pinned;\n const offset = columnOffsets.get(column.key);\n const isSystem =\n column.key === '__select__' || column.key === '__expand__';\n return (\n <div\n key={column.key}\n className={`flex h-9 items-center truncate border-t border-b ${\n isPinned\n ? `bg-background backdrop-blur ${classNames.pinnedHeader ?? ''}`\n : `bg-muted/40 backdrop-blur ${classNames.header ?? ''}`\n }`}\n style={{\n position: 'sticky',\n top: 0,\n zIndex: isPinned ? 13 : 10,\n ...(isPinned\n ? {\n [column.pinned as string]: offset ?? 0,\n ...styles.pinnedHeader,\n }\n : styles.header),\n paddingLeft: isSystem ? 0 : 8,\n paddingRight: isSystem ? 0 : 8,\n }}\n ></div>\n );\n })}\n\n <div style={{ gridColumn: '1 / -1' }}>\n {Array.from({ length: shimmerCount }).map((_, rowIndex) => (\n <div\n key={rowIndex}\n style={{\n display: 'grid',\n gridTemplateColumns,\n height: rowHeight,\n }}\n >\n {orderedColumns.map((column, colIndex) => {\n const isPinned = !!column.pinned;\n const offset = columnOffsets.get(column.key);\n const isSystem =\n column.key === '__select__' ||\n column.key === '__expand__';\n const widthPercent =\n SHIMMER_WIDTHS[\n (rowIndex * 7 + colIndex) % SHIMMER_WIDTHS.length\n ];\n return (\n <div\n key={column.key}\n className={`flex items-center border-b ${\n isPinned\n ? `bg-background ${classNames.pinnedCell ?? ''}`\n : ''\n }`}\n style={{\n ...(isPinned\n ? {\n position: 'sticky' as const,\n [column.pinned as string]: offset ?? 0,\n zIndex: 5,\n ...styles.pinnedCell,\n }\n : {}),\n paddingLeft: isSystem ? 0 : 8,\n paddingRight: isSystem ? 0 : 8,\n justifyContent: isSystem ? 'center' : undefined,\n }}\n >\n <div\n className=\"bg-muted-foreground/15 animate-pulse rounded\"\n style={{\n height: isSystem ? 16 : 14,\n width: isSystem ? 16 : `${widthPercent}%`,\n borderRadius: isSystem ? 3 : 4,\n animationDelay: `${(rowIndex * 7 + colIndex) * 50}ms`,\n }}\n />\n </div>\n );\n })}\n </div>\n ))}\n </div>\n </div>\n </div>\n ) : (\n /*\n * ── Main scroll container ────────────────────────────────────\n * absolute inset-0 so it fills whatever height the wrapper resolves to.\n * contain: layout paint — browser optimization hint that this element\n * is a layout and paint boundary (improves compositing performance).\n */\n <div\n ref={tableAreaCallbackRef}\n className=\"absolute inset-0 overflow-auto\"\n style={{ contain: 'layout paint' }}\n >\n {/* Resize overlay — positioned inside scroll container so it scrolls with content */}\n <ResizeOverlay ref={resizeOverlayRef} accentColor={accentColor} />\n\n {/*\n * ── CSS Grid ─────────────────────────────────────────────\n * Row 1 (36px): sticky column headers\n * Row 2 (1fr): table body (fills remaining space)\n *\n * The 1fr row lets the empty-state div use height:100% without\n * any JS measurement — it works in both autoHeight and flex modes.\n */}\n <div\n style={{\n display: 'grid',\n gridTemplateColumns,\n gridTemplateRows: '36px 1fr',\n minWidth: `${totalTableWidth}px`,\n height: '100%',\n width: '100%',\n position: 'relative',\n }}\n >\n {/* ── Column headers ─────────────────────────────────── */}\n <SortableContext\n items={columnOrder}\n strategy={horizontalListSortingStrategy}\n >\n {orderedColumns.map((column, visualIndex) => {\n // Selection column header — custom render with \"select all\" checkbox\n if (column.key === '__select__' && rowSelection) {\n return (\n <div\n key=\"__select__\"\n className={`bg-muted/40 sticky flex h-9 items-center justify-center truncate border-t border-b backdrop-blur ${classNames.header ?? ''} ${classNames.pinnedHeader ?? ''} `}\n style={{\n position: 'sticky',\n left: columnOffsets.get('__select__') ?? 0,\n top: 0,\n zIndex: 13,\n width: '48px',\n ...styles.header,\n ...styles.pinnedHeader,\n }}\n >\n {/* \"Select all\" checkbox — hidden in radio mode or when hideSelectAll=true */}\n {rowSelection.type !== 'radio' &&\n !rowSelection.hideSelectAll && (\n <input\n type=\"checkbox\"\n checked={\n data.length > 0 &&\n normalizedSelectedKeys.length === data.length\n }\n ref={(input) => {\n if (input) {\n // Indeterminate state: some (not all) rows are selected\n input.indeterminate =\n normalizedSelectedKeys.length > 0 &&\n normalizedSelectedKeys.length <\n data.length;\n }\n }}\n onChange={(e) => {\n if (e.target.checked) {\n const allKeys = data.map((row, idx) =>\n getRowKey(row, idx),\n );\n rowSelection.onSelectAll?.(\n true,\n data,\n data,\n );\n rowSelection.onChange?.(allKeys, data, {\n type: 'all',\n });\n } else {\n rowSelection.onSelectAll?.(false, [], data);\n rowSelection.onChange?.([], [], {\n type: 'all',\n });\n }\n }}\n className=\"cursor-pointer\"\n style={{ accentColor }}\n />\n )}\n </div>\n );\n }\n\n // Expand column header — empty cell (no content needed)\n if (column.key === '__expand__') {\n return (\n <div\n key=\"__expand__\"\n className={`bg-muted/40 sticky flex h-9 items-center justify-center truncate border-t border-b backdrop-blur ${classNames.header ?? ''} ${classNames.pinnedHeader ?? ''}`}\n style={{\n position: 'sticky',\n left: columnOffsets.get('__expand__') ?? 0,\n top: 0,\n zIndex: 13,\n width: '40px',\n ...styles.header,\n ...styles.pinnedHeader,\n }}\n />\n );\n }\n\n // Regular column header — drag/sort/filter/resize/context-menu\n return (\n <DraggableHeader\n key={column.key}\n column={column as ColumnType<DataRecord>}\n accentColor={accentColor}\n visualIndex={visualIndex}\n onResizeStart={handleResizeStart}\n styles={styles}\n classNames={classNames}\n gripIcon={gripIcon}\n hideGripIcon={hideGripIcon}\n stickyOffset={columnOffsets.get(column.key)}\n onTogglePin={handleTogglePin}\n onToggleHide={handleToggleHide}\n isLastColumn={visualIndex === orderedColumns.length - 1}\n sortDirection={\n sortState.key === column.key\n ? sortState.direction\n : null\n }\n onSort={handleSort}\n filterValue={columnFilters[column.key] ?? ''}\n onFilter={handleColumnFilter}\n onClearFilter={handleClearFilter}\n customContextMenuItems={columnContextMenuItems}\n />\n );\n })}\n </SortableContext>\n\n {isEmpty ? (\n /*\n * ── Empty state ────────────────────────────────────────\n * col-span-full + height:100% fills the 1fr body grid row.\n *\n * The inner div uses `position: sticky; left: 0` with a fixed\n * width (scrollAreaWidth) to viewport-lock the empty state panel.\n * Without this, the empty message would scroll horizontally\n * with the grid content when there are many columns.\n */\n <div\n className=\"col-span-full\"\n style={{ height: '100%', position: 'relative' }}\n >\n <div\n style={{\n position: 'sticky',\n left: 0,\n width:\n scrollAreaWidth > 0 ? `${scrollAreaWidth}px` : '100%',\n height: '100%',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n {emptyRenderer ?? (\n <div className=\"text-muted-foreground flex flex-col items-center gap-2 py-8\">\n <span className=\"text-sm\">No data</span>\n </div>\n )}\n </div>\n </div>\n ) : (\n /* ── Virtualized table body ─────────────────────────── */\n <TableBody\n data={displayData as DataRecord[]}\n orderedColumns={orderedColumns as ColumnType<DataRecord>[]}\n rowVirtualizer={rowVirtualizer}\n columnOffsets={columnOffsets}\n styles={styles}\n classNames={classNames}\n rowSelection={\n !showShimmer\n ? (rowSelection as\n | RowSelectionConfig<DataRecord>\n | undefined)\n : undefined\n }\n normalizedSelectedKeys={normalizedSelectedKeys}\n getRowKey={\n getRowKey as (record: DataRecord, index: number) => string\n }\n expandable={\n !showShimmer\n ? (expandable as\n | ExpandableConfig<DataRecord>\n | undefined)\n : undefined\n }\n resolvedExpandedKeys={resolvedExpandedKeys}\n rowHeight={rowHeight}\n totalTableWidth={totalTableWidth}\n scrollAreaWidth={scrollAreaWidth}\n accentColor={accentColor}\n scrollContainerRef={tableAreaRef}\n isLoading={showShimmer}\n onExpandedRowResize={handleExpandedRowResize}\n maxExpandedRowHeight={maxExpandedRowHeight}\n />\n )}\n </div>\n </div>\n )}\n </div>\n\n {/* ── Pagination footer ──────────────────────────────────────────────\n * Only rendered when pagination !== false.\n * Shows: [range info] [page buttons] [page size selector]\n */}\n {pagination !== false && (\n <div\n className=\"flex h-9 items-center justify-between border-t px-3 text-xs backdrop-blur\"\n style={{\n backgroundColor: 'hsl(var(--background)/0.4)',\n gap: '12px',\n }}\n >\n {/* Left section: \"X-Y of Z\" range indicator */}\n <div className=\"flex flex-1 items-center\">\n {(() => {\n const rangeStart =\n total > 0 ? (currentPage - 1) * pageSize + 1 : 0;\n const rangeEnd = Math.min(currentPage * pageSize, total);\n return pagination?.showTotal ? (\n <span className=\"text-muted-foreground text-xs\">\n Showing{' '}\n {pagination.showTotal(total, [rangeStart, rangeEnd])} of{' '}\n {total} items\n </span>\n ) : (\n <span className=\"text-muted-foreground text-xs\">\n {rangeStart}–{rangeEnd} of {total}\n </span>\n );\n })()}\n </div>\n\n {/* Center section: page number buttons */}\n <div className=\"flex flex-1 items-center justify-center gap-1\">\n <button\n onClick={() => handlePageChange(1)}\n disabled={currentPage === 1}\n className=\"inline-flex h-6 w-6 cursor-pointer items-center justify-center text-xs transition-colors disabled:cursor-not-allowed disabled:opacity-30\"\n title=\"First page\"\n >\n <ChevronsLeft className=\"h-3 w-3\" />\n </button>\n <button\n onClick={() => handlePageChange(currentPage - 1)}\n disabled={currentPage === 1}\n className=\"inline-flex h-6 w-6 cursor-pointer items-center justify-center text-xs transition-colors disabled:cursor-not-allowed disabled:opacity-30\"\n title=\"Previous page\"\n >\n <ChevronLeft className=\"h-3 w-3\" />\n </button>\n\n {getPageNumbers().map((page) => {\n if (page === 'ellipsis-left' || page === 'ellipsis-right') {\n return (\n <span\n key={page}\n className=\"text-muted-foreground px-1 text-xs select-none\"\n >\n ...\n </span>\n );\n }\n return (\n <button\n key={page}\n style={{\n color: page === currentPage ? accentColor : undefined,\n }}\n onClick={() => handlePageChange(page as number)}\n className=\"inline-flex h-6 min-w-6 cursor-pointer items-center justify-center rounded px-1.5 text-xs transition-colors\"\n >\n {page}\n </button>\n );\n })}\n\n <button\n onClick={() => handlePageChange(currentPage + 1)}\n disabled={currentPage === totalPages}\n className=\"inline-flex h-6 w-6 cursor-pointer items-center justify-center text-xs transition-colors disabled:cursor-not-allowed disabled:opacity-30\"\n title=\"Next page\"\n >\n <ChevronRight className=\"h-3 w-3\" />\n </button>\n <button\n onClick={() => handlePageChange(totalPages)}\n disabled={currentPage === totalPages}\n className=\"inline-flex h-6 w-6 cursor-pointer items-center justify-center text-xs transition-colors disabled:cursor-not-allowed disabled:opacity-30\"\n title=\"Last page\"\n >\n <ChevronsRight className=\"h-3 w-3\" />\n </button>\n </div>\n\n {/* Right section: rows-per-page selector */}\n <div className=\"flex flex-1 items-center justify-end gap-2\">\n <select\n value={pageSize}\n onChange={(e) => handlePageSizeChange(Number(e.target.value))}\n className=\"bg-background text-foreground hover:border-primary cursor-pointer rounded border px-1.5 py-0.5 text-xs\"\n style={{ height: '24px' }}\n >\n {[10, 15, 20, 25, 50, 100].map((size) => (\n <option key={size} value={size}>\n {size} / page\n </option>\n ))}\n </select>\n </div>\n </div>\n )}\n </div>\n\n {/*\n * ── Drag overlay ──────────────────────────────────────────────────────\n * Renders a \"ghost\" version of the dragged column header that follows\n * the cursor during a drag operation. Only shown when activeColumn is set.\n */}\n <DragOverlay>\n {activeColumn ? (\n <div\n className={`flex h-9 items-center truncate overflow-hidden border border-dashed shadow-md backdrop-blur ${classNames.header ?? ''} ${classNames.dragHeader ?? ''}`}\n style={{\n width: `${activeColumn.width ?? 150}px`,\n cursor: 'grabbing',\n ...styles.header,\n ...styles.dragHeader,\n }}\n >\n <div className=\"relative z-10 flex h-full flex-1 items-center gap-1 truncate overflow-hidden px-2 font-medium\">\n <GripVertical className=\"h-3 w-3 shrink-0\" />\n <div className=\"min-w-0 truncate overflow-hidden text-left text-ellipsis whitespace-nowrap select-none\">\n {typeof activeColumn.title === 'string'\n ? activeColumn.title\n : activeColumn.key}\n </div>\n </div>\n </div>\n ) : null}\n </DragOverlay>\n </DndContext>\n );\n}","'use client';\n\nimport { useSortable } from '@dnd-kit/sortable';\nimport {\n ArrowDownAZ,\n ArrowUpAZ,\n EyeOff,\n Filter,\n FilterX,\n GripVertical,\n Pin,\n PinOff,\n} from 'lucide-react';\nimport React, { CSSProperties, useEffect, useRef, useState } from 'react';\nimport { createPortal } from 'react-dom';\n\n\nimport { ClassNamesTypes, StylesTypes } from './BoltTable';\nimport type {\n ColumnContextMenuItem,\n ColumnType,\n DataRecord,\n SortDirection,\n} from './types';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// DraggableHeader\n//\n// Renders a single column header cell with the following capabilities:\n// • Drag-to-reorder — powered by @dnd-kit/sortable (disabled for pinned columns)\n// • Column resize — exposes a right-edge handle that calls onResizeStart\n// • Sort indicators — shows ArrowUpAZ / ArrowDownAZ when sorted\n// • Filter indicator — shows a small Filter icon when a filter is active\n// • Context menu — right-click reveals sort/filter/pin/hide actions\n// plus any custom items from columnContextMenuItems\n// • Unpin button — shown in place of the resize handle for pinned columns\n//\n// Performance: wrapped in React.memo with a custom comparator so headers only\n// re-render when their own column's props actually change. A sort change on\n// column A never causes column B to re-render.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Props for the DraggableHeader component.\n * This is an internal component used by BoltTable — all props are passed\n * automatically and you do not need to use DraggableHeader directly.\n */\ninterface DraggableHeaderProps {\n /**\n * The column definition for this header cell.\n * Controls width, pinning, sort/filter capabilities, title, and styling.\n */\n column: ColumnType<DataRecord>;\n\n /**\n * The visual position index of this column in the ordered column array.\n * Used to set the CSS `grid-column` placement so headers align with body cells.\n */\n visualIndex: number;\n\n /**\n * The accent color used for sort indicators, filter icons, the active sort\n * highlight in the context menu, and the resize handle hover line.\n *\n * @default '#1890ff'\n */\n accentColor?: string;\n\n /**\n * Called when the user presses down on the resize handle at the right edge\n * of this header cell. Starts the resize drag operation in BoltTable.\n *\n * @param columnKey - The key of the column being resized\n * @param event - The React mouse event from the resize handle mousedown\n */\n onResizeStart?: (columnKey: string, event: React.MouseEvent) => void;\n\n /**\n * Shared styling overrides for header cells.\n * `styles.header` applies to all headers; `styles.pinnedHeader` applies\n * additionally to pinned column headers.\n */\n styles?: StylesTypes;\n\n /**\n * Shared CSS class name overrides for header cells.\n * `classNames.header` applies to all headers; `classNames.pinnedHeader`\n * applies additionally to pinned column headers.\n */\n classNames?: ClassNamesTypes;\n\n /**\n * When `true`, the drag grip icon on the left of the header label is hidden.\n * The column can still be dragged; only the visual indicator is removed.\n *\n * @default false\n */\n hideGripIcon?: boolean;\n\n /**\n * A custom React node to use as the drag grip icon.\n * When omitted, the default `GripVertical` icon from lucide-react is used.\n *\n * @example\n * gripIcon={<MyCustomDragIcon />}\n */\n gripIcon?: React.ReactNode;\n\n /**\n * The pixel offset from the pinned edge (left or right) for this column.\n * Used to set `left` or `right` CSS on sticky-positioned pinned headers.\n * Calculated by BoltTable based on the total width of all preceding pinned columns.\n */\n stickyOffset?: number;\n\n /**\n * Called when the user pins or unpins this column via the context menu\n * or the unpin button shown on pinned headers.\n *\n * @param columnKey - The key of the column being toggled\n * @param pinned - The new pinned state: `'left'`, `'right'`, or `false` (unpinned)\n */\n onTogglePin?: (columnKey: string, pinned: 'left' | 'right' | false) => void;\n\n /**\n * Called when the user clicks \"Hide Column\" in the context menu.\n * The column's `hidden` property will be toggled in BoltTable's state.\n * Pinned columns cannot be hidden and this will never be called for them.\n *\n * @param columnKey - The key of the column being hidden\n */\n onToggleHide?: (columnKey: string) => void;\n\n /**\n * Whether this is the rightmost visible column.\n * When `true`, the header cell uses `width: 100%` instead of a fixed pixel\n * width so it stretches to fill any remaining horizontal space.\n *\n * @default false\n */\n isLastColumn?: boolean;\n\n /**\n * The current sort direction applied to this column.\n * - `'asc'` — column is sorted ascending (ArrowUpAZ icon shown)\n * - `'desc'` — column is sorted descending (ArrowDownAZ icon shown)\n * - `null` — column is not currently sorted (no icon shown)\n *\n * Passed from BoltTable's sort state; only set for the currently sorted column.\n */\n sortDirection?: SortDirection;\n\n /**\n * Called when the user clicks a sort option in the context menu.\n *\n * @param columnKey - The key of the column to sort\n * @param direction - The requested sort direction (`'asc'`, `'desc'`, or `undefined` to toggle)\n */\n onSort?: (columnKey: string, direction?: SortDirection) => void;\n\n /**\n * The current filter value active on this column.\n * When non-empty, a small Filter icon is shown in the header label.\n *\n * @default ''\n */\n filterValue?: string;\n\n /**\n * Called when the user submits a new filter value via the context menu input.\n *\n * @param columnKey - The key of the column being filtered\n * @param value - The filter string entered by the user\n */\n onFilter?: (columnKey: string, value: string) => void;\n\n /**\n * Called when the user clicks \"Clear Filter\" in the context menu.\n *\n * @param columnKey - The key of the column whose filter should be cleared\n */\n onClearFilter?: (columnKey: string) => void;\n\n /**\n * Additional custom items to append at the bottom of the right-click context menu.\n * These appear after the built-in sort/filter/pin/hide options.\n *\n * @example\n * customContextMenuItems={[\n * {\n * key: 'copy',\n * label: 'Copy column data',\n * icon: <CopyIcon />,\n * onClick: (columnKey) => copyColumn(columnKey),\n * }\n * ]}\n */\n customContextMenuItems?: ColumnContextMenuItem[];\n}\n\n/**\n * Returns `true` if the column should show sort UI (ascending/descending).\n * Columns are sortable by default; set `column.sortable = false` to disable.\n *\n * @param col - The column definition to check\n */\nfunction isColumnSortable(col: ColumnType<DataRecord>): boolean {\n return col.sortable !== false;\n}\n\n/**\n * Returns `true` if the column should show a filter input in the context menu.\n * Columns are filterable by default; set `column.filterable = false` to disable.\n *\n * @param col - The column definition to check\n */\nfunction isColumnFilterable(col: ColumnType<DataRecord>): boolean {\n return col.filterable !== false;\n}\n\n/**\n * DraggableHeader — a single column header cell for BoltTable.\n *\n * Features:\n * - **Drag to reorder**: grip icon on the left; dragging is disabled for pinned columns.\n * - **Resize handle**: a 12px wide invisible hit area on the right edge.\n * - **Sort indicators**: ArrowUpAZ / ArrowDownAZ shown when sorted.\n * - **Filter indicator**: small Filter icon when a filter is active.\n * - **Right-click context menu**: sort asc/desc, filter input, pin left/right, hide column,\n * plus any custom items passed via `customContextMenuItems`.\n * - **Unpin button**: replaces the resize handle on pinned columns.\n *\n * Wrapped in `React.memo` with a custom equality check — only re-renders when\n * its own column's data changes, preventing cascade re-renders across all headers\n * when a single column's sort/filter/width changes.\n *\n * @internal This is an internal BoltTable component. Use BoltTable directly.\n */\nconst DraggableHeader = React.memo(\n ({\n column,\n visualIndex,\n accentColor,\n onResizeStart,\n styles,\n classNames,\n hideGripIcon = false,\n gripIcon,\n stickyOffset,\n onTogglePin,\n onToggleHide,\n isLastColumn = false,\n sortDirection,\n onSort,\n filterValue = '',\n onFilter,\n onClearFilter,\n customContextMenuItems,\n }: DraggableHeaderProps) => {\n const effectivelySortable = isColumnSortable(column);\n const effectivelyFilterable = isColumnFilterable(column);\n\n /**\n * State for the right-click context menu.\n * `null` means hidden; `{ x, y }` means visible at those viewport coordinates.\n */\n const [contextMenu, setContextMenu] = useState<{\n x: number;\n y: number;\n } | null>(null);\n\n /**\n * When `true`, the filter text input is shown inside the context menu\n * instead of the \"Filter Column\" button.\n */\n const [showFilterInput, setShowFilterInput] = useState(false);\n\n const filterInputRef = useRef<HTMLInputElement>(null);\n const menuRef = useRef<HTMLDivElement>(null);\n\n // dnd-kit sortable hook — provides drag listeners, transform styles, and state\n const {\n attributes,\n listeners,\n setNodeRef,\n transition,\n isDragging, // true while this header is being dragged\n isOver, // true while another header is being dragged over this one\n } = useSortable({\n id: column.key,\n // Pinned columns cannot be dragged (their position is fixed by pinning)\n disabled: Boolean(column.pinned),\n });\n\nconst theme = typeof document !== 'undefined' &&\n document.documentElement.classList.contains('dark') ? 'dark' : 'light';\n\n // ── Close context menu when clicking outside it ─────────────────────────\n useEffect(() => {\n const handleClickOutside = (e: MouseEvent) => {\n if (menuRef.current && !menuRef.current.contains(e.target as Node)) {\n setContextMenu(null);\n }\n };\n if (contextMenu) {\n document.addEventListener('mousedown', handleClickOutside);\n return () =>\n document.removeEventListener('mousedown', handleClickOutside);\n }\n }, [contextMenu]);\n\n /**\n * Shows the context menu at the cursor position, adjusting to stay within\n * the viewport boundaries so it never renders partially off-screen.\n */\n const handleContextMenu = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n\n const menuWidth = 160;\n const menuHeight = 180;\n let x = e.clientX;\n let y = e.clientY;\n\n // Flip horizontally if the menu would overflow the right edge\n if (x + menuWidth > window.innerWidth) {\n x = window.innerWidth - menuWidth - 10;\n }\n\n // Flip vertically if the menu would overflow the bottom edge\n if (y + menuHeight > window.innerHeight) {\n y = window.innerHeight - menuHeight - 10;\n }\n\n setContextMenu({ x, y });\n };\n\n /**\n * Forwards the resize start event to BoltTable.\n * Pinned columns cannot be resized — the event is swallowed silently.\n */\n const handleResizeStart = (e: React.MouseEvent) => {\n e.preventDefault();\n e.stopPropagation();\n // Pinned columns cannot be resized\n if (column.pinned) return;\n onResizeStart?.(column.key, e);\n };\n\n const columnWidth = column.width ?? 150;\n const widthPx = `${columnWidth}px`;\n const isPinned = Boolean(column.pinned);\n\n // Dragging this header → lower z-index so it appears above body but below menus\n // Pinned headers → z-index 12 to stay above scrolling content\n // Normal headers → z-index 10\n const zIndex = isDragging ? 5 : isPinned ? 12 : 10;\n\n /**\n * Computed inline styles for the header cell.\n * Handles: sticky positioning, drag opacity/transition, pinned offset,\n * drag-over border highlight, and user-provided style overrides.\n */\n const style: CSSProperties = {\n position: 'sticky',\n top: 0,\n zIndex,\n // Last column stretches to fill remaining space; all others are fixed width\n width: isLastColumn ? '100%' : widthPx,\n minWidth: widthPx,\n ...(isLastColumn ? {} : { maxWidth: widthPx }),\n gridColumn: visualIndex + 1,\n gridRow: 1,\n // Fade out slightly while being dragged\n opacity: isDragging ? 0.3 : 1,\n transition: transition,\n borderWidth: '1px',\n // Show a dashed accent-colored border when another column is dragged over this one\n borderStyle: isOver ? ('dashed' as const) : 'solid',\n ...(isOver\n ? { borderColor: accentColor || '#1788ff' }\n : { borderLeftColor: 'transparent' }),\n // Sticky positioning for pinned columns\n ...(column.pinned === 'left' && stickyOffset !== undefined\n ? { left: `${stickyOffset}px`, position: 'sticky' as const }\n : {}),\n ...(column.pinned === 'right' && stickyOffset !== undefined\n ? { right: `${stickyOffset}px`, position: 'sticky' as const }\n : {}),\n // Pinned columns get a semi-transparent background so they visually\n // separate from scrolling content behind them\n ...(isPinned\n ? {\n backgroundColor:\n ((styles as any)?.pinnedBg ?? theme === 'dark')\n ? '#10182890'\n : '#f9fafb90',\n ...styles?.pinnedHeader,\n }\n : {}),\n // Column-level style overrides applied last (highest specificity)\n ...column.style,\n ...styles?.header,\n } as CSSProperties;\n\n const baseClasses =\n 'bg-muted/40 group relative truncate flex h-9 items-center overflow-hidden backdrop-blur ';\n const className = `${baseClasses} ${column.className ?? ''} ${classNames?.header ?? ''} ${classNames?.pinnedHeader ?? ''} `;\n\n return (\n <>\n {/*\n * ── Header cell ────────────────────────────────────────────────────\n * The outer div is the dnd-kit sortable node. It handles the drop\n * target detection and applies the transform/transition for smooth\n * reordering animations.\n */}\n <div\n ref={setNodeRef}\n data-column-key={column.key}\n style={style}\n className={className}\n onContextMenu={handleContextMenu}\n >\n {/*\n * ── Drag handle + label area ────────────────────────────────────\n * This inner div receives the dnd-kit listeners and attributes.\n * For pinned columns, listeners are omitted so they cannot be dragged.\n * `touch-none` prevents browser scroll interference during drag.\n */}\n <div\n {...(isPinned ? {} : attributes)}\n {...(isPinned ? {} : listeners)}\n className={`group relative z-10 flex h-full flex-1 touch-none items-center gap-1 truncate overflow-hidden px-2 font-medium ${\n isPinned ? 'cursor-default' : 'cursor-grab active:cursor-grabbing'\n }`}\n aria-label={\n isPinned\n ? `${column.key} column (pinned)`\n : `Drag ${column.key} column`\n }\n >\n {/*\n * Drag grip icon — hidden for pinned columns and when hideGripIcon=true.\n * Fades to full opacity on header hover.\n */}\n {hideGripIcon || isPinned\n ? null\n : (gripIcon ?? (\n <GripVertical className=\"h-3 w-3 shrink-0 opacity-35 group-hover:opacity-80\" />\n ))}\n\n {/*\n * Column title + sort/filter indicator icons.\n * `select-none` prevents text selection during drag.\n */}\n <div\n className={`flex min-w-0 items-center gap-1 truncate overflow-hidden text-left select-none`}\n >\n {column.title}\n\n {/* Sort ascending indicator */}\n {sortDirection === 'asc' && (\n <ArrowUpAZ\n className=\"h-3 w-3 shrink-0\"\n style={{ color: accentColor }}\n />\n )}\n\n {/* Sort descending indicator */}\n {sortDirection === 'desc' && (\n <ArrowDownAZ\n className=\"h-3 w-3 shrink-0\"\n style={{ color: accentColor }}\n />\n )}\n\n {/* Active filter indicator — shown when filterValue is non-empty */}\n {filterValue && (\n <Filter\n className=\"h-2.5 w-2.5 shrink-0\"\n style={{ color: accentColor }}\n />\n )}\n </div>\n </div>\n\n {/*\n * ── Unpin button (pinned columns only) ─────────────────────────\n * Replaces the resize handle for pinned columns. Clicking unpins\n * the column and restores it to its original position in the flow.\n */}\n {isPinned && (\n <button\n className=\"group/unpin relative h-full w-6 shrink-0 cursor-pointer border-0 bg-transparent p-0\"\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n onTogglePin?.(column.key, false);\n }}\n aria-label={`Unpin ${column.key} column`}\n title=\"Unpin column\"\n style={{ color: accentColor || '1788ff' }}\n >\n <PinOff className=\"mx-auto h-3 w-3\" />\n </button>\n )}\n\n {/*\n * ── Resize handle (non-pinned columns only) ────────────────────\n * A 12px-wide invisible hit area on the right edge of the header.\n * Shows a colored line on hover to indicate the resize target.\n * Pinned columns skip this — they cannot be resized.\n */}\n {!isPinned && (\n <button\n className=\"group/resize relative h-full w-3 shrink-0 cursor-col-resize border-0 bg-transparent p-0\"\n onMouseDown={handleResizeStart}\n aria-label={`Resize ${column.key} column`}\n >\n {/* Visual resize indicator line — only visible on hover */}\n <div\n className=\"absolute top-0 right-0 h-full w-0.5 opacity-0 transition-opacity group-hover/resize:opacity-100\"\n style={{\n backgroundColor: accentColor || '#1788ff',\n }}\n />\n </button>\n )}\n </div>\n\n {/*\n * ── Context menu (right-click) ─────────────────────────────────────\n * Rendered as a portal at document.body so it's never clipped by the\n * table's overflow:hidden containers.\n * Sections: Sort | Filter | Pin | Hide | Custom items\n */}\n {contextMenu &&\n typeof document !== 'undefined' &&\n createPortal(\n <div\n ref={menuRef}\n className=\"text-xxs fixed z-[9999] min-w-40 rounded-md border py-1 shadow-lg backdrop-blur\"\n style={{\n left: `${contextMenu.x}px`,\n top: `${contextMenu.y}px`,\n position: 'fixed',\n }}\n role=\"menu\"\n >\n {/* ── Sort options ─────────────────────────────────────────── */}\n {effectivelySortable && onSort && (\n <>\n <button\n className={`cusror-pointer flex w-full items-center gap-2 px-3 py-1.5 text-left ${sortDirection === 'asc' ? 'font-semibold' : ''}`}\n style={\n sortDirection === 'asc'\n ? { color: accentColor }\n : undefined\n }\n onClick={() => {\n onSort(column.key, 'asc');\n setContextMenu(null);\n }}\n >\n <ArrowUpAZ className=\"h-3 w-3\" />\n Sort Ascending\n </button>\n <button\n className={`cusror-pointer flex w-full items-center gap-2 px-3 py-1.5 text-left ${sortDirection === 'desc' ? 'font-semibold' : ''}`}\n style={\n sortDirection === 'desc'\n ? { color: accentColor }\n : undefined\n }\n onClick={() => {\n onSort(column.key, 'desc');\n setContextMenu(null);\n }}\n >\n <ArrowDownAZ className=\"h-3 w-3\" />\n Sort Descending\n </button>\n <div className=\"my-1 border-t dark:border-gray-700\" />\n </>\n )}\n\n {/* ── Filter option ─────────────────────────────────────────── */}\n {effectivelyFilterable && onFilter && (\n <>\n {showFilterInput ? (\n // Inline text input — pressing Enter applies the filter,\n // pressing Escape cancels and returns to the button\n <div className=\"flex items-center gap-1 px-2 py-1.5\">\n <input\n ref={filterInputRef}\n type=\"text\"\n autoFocus\n defaultValue={filterValue}\n placeholder=\"Filter...\"\n className=\"bg-background text-foreground w-full rounded border px-1.5 py-0.5 text-xs outline-none focus:border-blue-400\"\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n onFilter(\n column.key,\n (e.target as HTMLInputElement).value,\n );\n setShowFilterInput(false);\n setContextMenu(null);\n }\n if (e.key === 'Escape') {\n setShowFilterInput(false);\n }\n }}\n />\n </div>\n ) : (\n <button\n className=\"cusror-pointer flex w-full items-center gap-2 px-3 py-1.5 text-left\"\n onClick={() => {\n setShowFilterInput(true);\n }}\n >\n <Filter className=\"h-3 w-3\" />\n {filterValue\n ? `Filtered: \"${filterValue}\"`\n : 'Filter Column'}\n </button>\n )}\n {/* Clear filter — only shown when a filter is currently active */}\n {filterValue && (\n <button\n className=\"cusror-pointer flex w-full items-center gap-2 px-3 py-1.5 text-left text-red-500\"\n onClick={() => {\n onClearFilter?.(column.key);\n setShowFilterInput(false);\n setContextMenu(null);\n }}\n >\n <FilterX className=\"h-3 w-3\" />\n Clear Filter\n </button>\n )}\n <div className=\"my-1 border-t dark:border-gray-700\" />\n </>\n )}\n\n {/* ── Pin options ────────────────────────────────────────────── */}\n {/* Pin/unpin left */}\n <button\n className=\"cusror-pointer flex w-full items-center gap-2 px-3 py-1.5 text-left\"\n onClick={() => {\n onTogglePin?.(\n column.key,\n column.pinned === 'left' ? false : 'left',\n );\n setContextMenu(null);\n }}\n >\n {column.pinned === 'left' ? (\n <PinOff className=\"h-3 w-3\" />\n ) : (\n <Pin className=\"h-3 w-3\" />\n )}\n {column.pinned === 'left' ? 'Unpin Left' : 'Pin Left'}\n </button>\n\n {/* Pin/unpin right */}\n <button\n className=\"cusror-pointer flex w-full items-center gap-2 px-3 py-1.5 text-left\"\n onClick={() => {\n onTogglePin?.(\n column.key,\n column.pinned === 'right' ? false : 'right',\n );\n setContextMenu(null);\n }}\n >\n {column.pinned === 'right' ? (\n <PinOff className=\"h-3 w-3\" />\n ) : (\n <Pin className=\"h-3 w-3\" />\n )}\n {column.pinned === 'right' ? 'Unpin Right' : 'Pin Right'}\n </button>\n\n {/* ── Hide column (non-pinned only) ──────────────────────────── */}\n {!isPinned && (\n <>\n <div className=\"my-1 border-t dark:border-gray-700\" />\n <button\n className=\"cusror-pointer flex w-full items-center gap-2 px-3 py-1.5 text-left\"\n onClick={() => {\n onToggleHide?.(column.key);\n setContextMenu(null);\n }}\n >\n <EyeOff className=\"h-3 w-3\" />\n Hide Column\n </button>\n </>\n )}\n\n {/* ── Custom context menu items from parent ─────────────────── */}\n {customContextMenuItems && customContextMenuItems.length > 0 && (\n <>\n <div className=\"my-1 border-t dark:border-gray-700\" />\n {customContextMenuItems.map((item) => (\n <button\n key={item.key}\n disabled={item.disabled}\n className={`flex w-full items-center gap-2 px-3 py-1.5 text-left ${\n item.disabled\n ? 'cursor-not-allowed opacity-50'\n : 'cusror-pointer'\n } ${item.danger ? 'text-red-500' : ''}`}\n onClick={() => {\n item.onClick(column.key);\n setContextMenu(null);\n }}\n >\n {item.icon && (\n <span className=\"flex h-3 w-3 items-center justify-center\">\n {item.icon}\n </span>\n )}\n {item.label}\n </button>\n ))}\n </>\n )}\n </div>,\n document.body,\n )}\n </>\n );\n },\n // ── Custom memo comparator ─────────────────────────────────────────────────\n // Only re-render when props that actually affect this header's output change.\n // This prevents a sort/filter change on one column from re-rendering all others.\n (prevProps, nextProps) => {\n return (\n prevProps.column.width === nextProps.column.width &&\n prevProps.column.key === nextProps.column.key &&\n prevProps.column.pinned === nextProps.column.pinned &&\n prevProps.column.sortable === nextProps.column.sortable &&\n prevProps.column.filterable === nextProps.column.filterable &&\n prevProps.column.sorter === nextProps.column.sorter &&\n prevProps.column.filterFn === nextProps.column.filterFn &&\n prevProps.visualIndex === nextProps.visualIndex &&\n prevProps.stickyOffset === nextProps.stickyOffset &&\n prevProps.isLastColumn === nextProps.isLastColumn &&\n prevProps.sortDirection === nextProps.sortDirection &&\n prevProps.filterValue === nextProps.filterValue\n );\n },\n);\n\nDraggableHeader.displayName = 'DraggableHeader';\n\nexport default DraggableHeader;","'use client';\n\nimport React, { forwardRef, useImperativeHandle, useRef } from 'react';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ResizeOverlay\n//\n// A purely visual overlay that renders during column resize operations.\n// It consists of two DOM elements:\n// 1. A vertical line that follows the user's cursor, showing the new column edge.\n// 2. A floating label that displays the column name (and optionally its new width).\n//\n// This component is intentionally rendered as a direct child of the scroll\n// container (position: absolute) so that it scrolls with the table content.\n// All updates are made via direct DOM manipulation (no React state) to ensure\n// the overlay moves in real-time without triggering re-renders.\n//\n// Usage: Mount once inside BoltTable's scroll container, hold a ref to it,\n// and call show() / move() / hide() imperatively during mouse events.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * The imperative handle exposed by ResizeOverlay via `ref`.\n * All three methods must be called in sequence during a resize operation:\n * `show()` → `move()` (many times) → `hide()`.\n */\nexport interface ResizeOverlayHandle {\n /**\n * Makes the overlay visible and positions it at the start of a resize.\n * Call this on `mousedown` of the resize handle.\n *\n * @param viewportX - The initial mouse X position in viewport coordinates (e.clientX)\n * @param columnName - The name/title of the column being resized (shown in the label)\n * @param areaRect - The bounding rect of the scroll container (from getBoundingClientRect)\n * @param headerLeftLocal - The left edge of the column header in scroll-content coordinates\n * @param minSize - The minimum allowed column width in pixels (used to clamp the line)\n * @param scrollTop - Current vertical scroll offset of the container (scrollTop)\n * @param scrollLeft - Current horizontal scroll offset of the container (scrollLeft)\n * @param initialLineX - The initial X position of the vertical line in content coordinates\n */\n show: (\n viewportX: number,\n columnName: string,\n areaRect: DOMRect,\n headerLeftLocal: number,\n minSize: number,\n scrollTop: number,\n scrollLeft: number,\n initialLineX: number,\n ) => void;\n\n /**\n * Moves the vertical line to follow the mouse cursor during a resize drag.\n * Call this on every `mousemove` event while dragging.\n * The line is clamped to never go below the column's minimum width.\n *\n * @param viewportX - The current mouse X position in viewport coordinates (e.clientX)\n */\n move: (viewportX: number) => void;\n\n /**\n * Hides the overlay completely.\n * Call this on `mouseup` when the resize operation ends.\n */\n hide: () => void;\n}\n\n/**\n * Internal state cached during an active resize operation.\n * Stored in a ref so it never triggers re-renders.\n */\ninterface ResizeOverlayCachedState {\n /** Left edge of the column header in scroll-content coordinates */\n headerLeftLocal: number;\n /** Minimum column width — the line will never move left of (headerLeftLocal + minSize) */\n minSize: number;\n /** Left edge of the scroll container in viewport coordinates */\n areaLeft: number;\n /** Width of the scroll container in pixels */\n areaWidth: number;\n /** Measured width of the floating label element in pixels */\n labelWidth: number;\n /** Horizontal scroll offset at the time show() was called */\n scrollLeft: number;\n}\n\n/**\n * Props for the ResizeOverlay component.\n */\ninterface ResizeOverlayProps {\n /**\n * The accent color used for the resize line and label background.\n * Should match the `accentColor` prop passed to BoltTable for visual consistency.\n *\n * @default '#1778ff'\n *\n * @example\n * accentColor=\"#6366f1\"\n */\n accentColor?: string;\n}\n\n/**\n * ResizeOverlay — visual feedback component for column resize operations.\n *\n * Renders a colored vertical line and a floating column-name label that track\n * the user's cursor during a column resize drag. All DOM updates are direct\n * (bypassing React state) for zero-lag, 60fps movement.\n *\n * This component is an implementation detail of BoltTable and is not intended\n * to be used standalone. It is mounted once inside the scroll container and\n * controlled imperatively via the `ResizeOverlayHandle` ref.\n *\n * @example\n * // Inside BoltTable:\n * const resizeOverlayRef = useRef<ResizeOverlayHandle>(null);\n *\n * // On resize start:\n * resizeOverlayRef.current?.show(e.clientX, 'Name', areaRect, headerLeft, 40, scrollTop, scrollLeft, initX);\n *\n * // On mouse move:\n * resizeOverlayRef.current?.move(e.clientX);\n *\n * // On mouse up:\n * resizeOverlayRef.current?.hide();\n *\n * // In JSX:\n * <ResizeOverlay ref={resizeOverlayRef} accentColor={accentColor} />\n */\nconst ResizeOverlay = forwardRef<ResizeOverlayHandle, ResizeOverlayProps>(\n ({ accentColor = '#1778ff' }, ref) => {\n // Direct refs to the two DOM elements — updated imperatively, never via state\n const lineRef = useRef<HTMLDivElement>(null);\n const labelRef = useRef<HTMLDivElement>(null);\n\n /**\n * Cached state for the active resize operation.\n * Stored in a ref so updates don't trigger re-renders.\n * Reset on every call to show().\n */\n const stateRef = useRef<ResizeOverlayCachedState>({\n headerLeftLocal: 0,\n minSize: 40,\n areaLeft: 0,\n areaWidth: 0,\n labelWidth: 80,\n scrollLeft: 0,\n });\n\n useImperativeHandle(\n ref,\n () => ({\n show(\n _viewportX: number,\n columnName: string,\n areaRect: DOMRect,\n headerLeftLocal: number,\n minSize: number,\n scrollTop: number,\n scrollLeft: number,\n initialLineX: number,\n ) {\n const line = lineRef.current;\n const label = labelRef.current;\n if (!line || !label) return;\n\n // Position the vertical line at the right edge of the column header.\n // top and height are set to cover the full scroll container height.\n line.style.top = `${scrollTop}px`;\n line.style.height = `${areaRect.height}px`;\n line.style.left = `${initialLineX - 2.5}px`;\n line.style.display = 'block';\n\n // Show the column name label just to the right of the line\n label.textContent = `${columnName}`;\n label.style.top = `${scrollTop + 8}px`;\n label.style.left = `${initialLineX + 6}px`;\n label.style.display = 'block';\n\n // Measure label width now so move() can flip it to the left when near edge\n const labelWidth = label.offsetWidth ?? 80;\n\n // Cache all values needed by move() — avoids re-reading the DOM on every mousemove\n stateRef.current = {\n headerLeftLocal,\n minSize,\n areaLeft: areaRect.left,\n areaWidth: areaRect.width,\n labelWidth,\n scrollLeft,\n };\n },\n\n move(viewportX: number) {\n const line = lineRef.current;\n const label = labelRef.current;\n if (!line || !label) return;\n\n const {\n headerLeftLocal,\n minSize,\n areaLeft,\n areaWidth,\n labelWidth,\n scrollLeft,\n } = stateRef.current;\n\n // Convert viewport X → scroll-content X, then clamp to minimum column width\n const localX = viewportX - areaLeft + scrollLeft;\n const clampedLocalX = Math.max(localX, headerLeftLocal + minSize);\n\n // Move the vertical line\n line.style.left = `${clampedLocalX}px`;\n\n // Strip any \": Xpx\" suffix from a previous move (label shows name only)\n const currentText = label.textContent || '';\n const colonIndex = currentText.indexOf(':');\n if (colonIndex !== -1) {\n label.textContent = `${currentText.substring(0, colonIndex)}`;\n }\n\n // Flip the label to the left side of the line when it would overflow the right edge\n const labelViewportX = clampedLocalX - scrollLeft;\n const FLIP_MARGIN = 20;\n if (labelViewportX + labelWidth + FLIP_MARGIN > areaWidth) {\n label.style.left = `${clampedLocalX - labelWidth - 10}px`;\n } else {\n label.style.left = `${clampedLocalX + 6}px`;\n }\n },\n\n hide() {\n const line = lineRef.current;\n const label = labelRef.current;\n if (line) line.style.display = 'none';\n if (label) label.style.display = 'none';\n },\n }),\n [],\n );\n\n /**\n * Converts a hex color string to an rgba() string.\n * Used to create a soft glow shadow on the resize line.\n *\n * @param hex - A 6-digit hex color e.g. '#1778ff'\n * @param opacity - Opacity value between 0 and 1\n */\n const hexToRgba = (hex: string, opacity: number) => {\n const r = parseInt(hex.slice(1, 3), 16);\n const g = parseInt(hex.slice(3, 5), 16);\n const b = parseInt(hex.slice(5, 7), 16);\n return `rgba(${r}, ${g}, ${b}, ${opacity})`;\n };\n\n return (\n <>\n {/*\n * Vertical resize line\n * - position: absolute so it's placed within the scroll container\n * - willChange: left for GPU-composited movement (no layout thrash)\n * - pointerEvents: none so it never interferes with mouse events below it\n * - display: none by default — only shown during an active resize\n */}\n <div\n ref={lineRef}\n aria-hidden=\"true\"\n style={{\n display: 'none',\n position: 'absolute',\n top: 0,\n height: '0px',\n left: 0,\n width: '2px',\n zIndex: 30,\n pointerEvents: 'none',\n backgroundColor: accentColor,\n boxShadow: `0 0 4px ${hexToRgba(accentColor, 0.5)}`,\n willChange: 'left',\n }}\n />\n\n {/*\n * Floating column name label\n * - Positioned just to the right of the resize line (or flipped left near edge)\n * - White text on accent-colored background for high contrast\n * - pointerEvents: none so it never blocks clicks\n * - display: none by default — only shown during an active resize\n */}\n <div\n ref={labelRef}\n aria-hidden=\"true\"\n style={{\n display: 'none',\n position: 'absolute',\n top: '8px',\n left: 0,\n zIndex: 31,\n pointerEvents: 'none',\n backgroundColor: accentColor,\n color: 'white',\n fontSize: '11px',\n fontWeight: 600,\n lineHeight: 1,\n padding: '4px 8px',\n borderRadius: '5px',\n whiteSpace: 'nowrap',\n boxShadow: '0 2px 10px rgba(0,0,0,0.2)',\n userSelect: 'none',\n willChange: 'left',\n }}\n />\n </>\n );\n },\n);\n\nResizeOverlay.displayName = 'ResizeOverlay';\n\nexport default ResizeOverlay;","'use client';\n\nimport type { VirtualItem, Virtualizer } from '@tanstack/react-virtual';\nimport React, { useEffect, useMemo, useRef } from 'react';\n\n\nimport { ClassNamesTypes, StylesTypes } from './BoltTable';\nimport type {\n ColumnType,\n DataRecord,\n ExpandableConfig,\n RowSelectionConfig,\n} from './types';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// TableBody\n//\n// Renders the virtualized body of BoltTable. It is architected around the\n// CSS Grid layout that BoltTable establishes: one column per table column,\n// where each grid column contains an absolutely-positioned stack of cells.\n//\n// Architecture overview:\n//\n// ┌─────────────────────────────────────────────────────────────┐\n// │ Grid (gridTemplateColumns mirrors header) │\n// │ │\n// │ col 1 col 2 col 3 ... │\n// │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │\n// │ │ spacer │ │ spacer │ │ spacer │ ← height=totalSize │\n// │ │ │ │ │ │ │ │\n// │ │ cell 0 │ │ cell 0 │ │ cell 0 │ ← absolute pos │\n// │ │ cell 1 │ │ cell 1 │ │ cell 1 │ │\n// │ │ ... │ │ ... │ │ ... │ │\n// │ └──────────┘ └──────────┘ └──────────┘ │\n// │ │\n// │ expanded row overlay (gridColumn: 1/-1, z-index: 15) │\n// └─────────────────────────────────────────────────────────────┘\n//\n// Why this layout:\n// 1. Each column is a single sticky-capable block, so pinned columns can use\n// `position: sticky` without breaking the column alignment.\n// 2. Virtualizer only controls the Y-axis (which rows are rendered and their\n// top offset). The X-axis is handled entirely by CSS Grid.\n// 3. Expanded rows are in a separate overlay div that spans all columns,\n// positioned absolutely below their parent row and viewport-locked via\n// `position: sticky; left: 0`.\n//\n// Performance optimizations:\n// - Cell is wrapped in React.memo with a custom comparator. Selection and\n// expand cells only re-render when their specific row's state changes.\n// Normal cells only re-render when their value changes.\n// - Pinned column background is applied at the column-spacer level (not per\n// cell) so changing the scroll position never causes cell re-renders.\n// - MeasuredExpandedRow uses a ResizeObserver to report content height to\n// the virtualizer without causing React re-renders on every frame.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Props for the TableBody component.\n * All props are passed automatically by BoltTable — this is an internal component.\n */\ninterface TableBodyProps {\n /** The current page's row data (already sliced/filtered/sorted by BoltTable) */\n data: DataRecord[];\n\n /** The ordered visible columns (left pinned → unpinned → right pinned) */\n orderedColumns: ColumnType<DataRecord>[];\n\n /**\n * The TanStack Virtual row virtualizer instance.\n * Provides `getVirtualItems()` and `getTotalSize()` for rendering.\n */\n rowVirtualizer: Virtualizer<HTMLDivElement, Element>;\n\n /**\n * Map of column key → sticky offset in pixels.\n * For left-pinned columns: distance from the left edge.\n * For right-pinned columns: distance from the right edge.\n */\n columnOffsets: Map<string, number>;\n\n /** Shared style overrides passed down from BoltTable */\n styles?: StylesTypes;\n\n /** Shared class name overrides passed down from BoltTable */\n classNames?: ClassNamesTypes;\n\n /**\n * Row selection configuration.\n * When provided, the `__select__` column renders checkboxes or radio buttons.\n * `undefined` during shimmer loading to prevent selection UI on skeleton rows.\n */\n rowSelection?: RowSelectionConfig<DataRecord>;\n\n /**\n * Pre-normalized selected row keys (all converted to strings).\n * Normalized in BoltTable so Cell never has to deal with number/string mismatches.\n *\n * @default []\n */\n normalizedSelectedKeys?: string[];\n\n /**\n * Returns the string key for a given row record and index.\n * Derived from BoltTable's `rowKey` prop. Always returns a string.\n *\n * @param record - The row data object\n * @param index - The row's position in the data array\n */\n getRowKey?: (record: DataRecord, index: number) => string;\n\n /**\n * Expandable row configuration.\n * When provided, rows in `resolvedExpandedKeys` render an expanded content panel.\n * `undefined` during shimmer loading to prevent expand UI on skeleton rows.\n */\n expandable?: ExpandableConfig<DataRecord>;\n\n /**\n * The set of currently expanded row keys.\n * Used to determine whether to render the expanded content panel for each row.\n */\n resolvedExpandedKeys?: Set<React.Key>;\n\n /**\n * Height of each regular (non-expanded) row in pixels.\n * Must match the `rowHeight` prop passed to BoltTable.\n *\n * @default 40\n */\n rowHeight?: number;\n\n /**\n * Total pixel width of all columns combined.\n * Used to set `minWidth` on the column spacer so the grid never collapses\n * below the sum of all column widths.\n */\n totalTableWidth?: number;\n\n /**\n * The visible width of the scroll container in pixels.\n * Used to set the width of expanded row panels and the empty state div\n * so they fill exactly the visible viewport rather than the full content width.\n */\n scrollAreaWidth?: number;\n\n /**\n * The accent color used for the expand toggle button chevron icon.\n * Should match the `accentColor` prop on BoltTable.\n *\n * @default '#1890ff'\n */\n accentColor?: string;\n\n /**\n * Ref to the scroll container element.\n * Reserved for potential future use (e.g. programmatic scrolling from within TableBody).\n */\n scrollContainerRef?: React.RefObject<HTMLDivElement | null>;\n\n /**\n * When `true`, all cells render as animated shimmer skeletons instead of\n * real data. Used during initial loading when `data` is empty.\n *\n * @default false\n */\n isLoading?: boolean;\n\n /**\n * Called by `MeasuredExpandedRow` when an expanded row's content height changes.\n * BoltTable uses this to update the virtualizer's size estimate for that row,\n * triggering a re-layout so the expanded content is never clipped.\n *\n * @param rowKey - The string key of the row whose expanded height changed\n * @param contentHeight - The new content height in pixels (border-box)\n */\n onExpandedRowResize?: (rowKey: string, contentHeight: number) => void;\n\n /**\n * Optional maximum height in pixels for expanded row panels.\n * When set, the panel becomes scrollable if its content exceeds this height.\n * When omitted, the panel grows to its full content height.\n *\n * @example\n * maxExpandedRowHeight={300}\n */\n maxExpandedRowHeight?: number;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Cell\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Widths (as percentages) for shimmer skeleton bars, cycled per cell position */\nconst SHIMMER_WIDTHS = [55, 70, 45, 80, 60, 50, 75, 65];\n\n/**\n * Props for the Cell component.\n * All props are passed automatically by TableBody.\n */\ninterface CellProps {\n /** The raw cell value (`row[column.dataIndex]`) */\n value: unknown;\n\n /** The full row data object */\n record: DataRecord;\n\n /** The column definition for this cell */\n column: ColumnType<DataRecord>;\n\n /** The row's absolute index in the current data array (0-based) */\n rowIndex: number;\n\n /** Class name overrides from BoltTable */\n classNames?: ClassNamesTypes;\n\n /** Style overrides from BoltTable */\n styles?: StylesTypes;\n\n /** Whether this row is currently selected */\n isSelected?: boolean;\n\n /** Whether this row is currently expanded */\n isExpanded?: boolean;\n\n /** Row selection config (for `__select__` cells only) */\n rowSelection?: RowSelectionConfig<DataRecord>;\n\n /**\n * Pre-normalized selected row keys.\n * Used by the `__select__` cell to derive the new selection after a toggle\n * without risking a stale closure.\n */\n normalizedSelectedKeys?: string[];\n\n /** The string key for this row */\n rowKey?: string;\n\n /** All rows in the current view — needed to derive `selectedRows` after a toggle */\n allData?: DataRecord[];\n\n /** Row key resolver passed from BoltTable */\n getRowKey?: (record: DataRecord, index: number) => string;\n\n /** Accent color for checkbox/radio `accentColor` style */\n accentColor?: string;\n\n /**\n * When `true`, renders a shimmer skeleton instead of real content.\n * Triggered for rows whose key starts with `__shimmer_` or when `isLoading` is true.\n */\n isLoading?: boolean;\n}\n\n/**\n * Cell — renders a single table body cell.\n *\n * Handles three distinct render paths:\n *\n * 1. **Shimmer** (`isLoading=true`): renders an animated pulse skeleton bar.\n * If the column defines a `shimmerRender` function, that is used instead.\n *\n * 2. **Selection cell** (`column.key === '__select__'`): renders a checkbox or\n * radio button driven by `rowSelection` props. The actual checked state comes\n * from `normalizedSelectedKeys` (not `column.render`) so selection changes\n * never cause BoltTable's column memos to re-run.\n *\n * 3. **Normal cell**: calls `column.render(value, record, index)` if defined,\n * otherwise renders the raw value as a React node.\n *\n * Wrapped in `React.memo` with a custom comparator:\n * - `__select__` cells: only re-render when `isSelected` or `normalizedSelectedKeys` changes\n * - `__expand__` cells: only re-render when `isExpanded` changes\n * - Normal cells: only re-render when `value` or `rowIndex` changes\n */\nconst Cell = React.memo(\n ({\n value,\n record,\n column,\n rowIndex,\n classNames,\n styles,\n isSelected,\n rowSelection,\n rowKey,\n allData,\n getRowKey,\n accentColor,\n isLoading,\n }: CellProps) => {\n const justifyClass =\n column.key === '__select__' || column.key === '__expand__'\n ? 'justify-center'\n : '';\n const isPinned = Boolean(column.pinned);\n\n // ── 1. Shimmer state ──────────────────────────────────────────────────────\n // Skip shimmer for system columns (__select__, __expand__) — they have no\n // meaningful skeleton. Other columns render either a custom shimmerRender\n // or a default animated pulse bar.\n if (\n isLoading &&\n column.key !== '__select__' &&\n column.key !== '__expand__'\n ) {\n const shimmerContent = column.shimmerRender ? (\n column.shimmerRender()\n ) : (\n <div\n className=\"bg-muted-foreground/15 animate-pulse rounded\"\n style={{\n // Vary widths across cells so skeletons look more natural\n width: `${SHIMMER_WIDTHS[(rowIndex + column.key.length) % SHIMMER_WIDTHS.length]}%`,\n height: 14,\n }}\n />\n );\n\n return (\n <div\n className={`flex items-center overflow-hidden border-b px-2 ${column.className ?? ''} ${classNames?.cell ?? ''} ${isPinned ? (classNames?.pinnedCell ?? '') : ''}`}\n style={{\n height: '100%',\n ...column.style,\n ...(isPinned ? styles?.pinnedCell : undefined),\n }}\n >\n {shimmerContent}\n </div>\n );\n }\n\n // ── 2. Selection cell ─────────────────────────────────────────────────────\n // Rendered here (not via column.render) so that checking/unchecking a row\n // only re-renders its own Cell, never triggering BoltTable's column memos.\n if (column.key === '__select__' && rowSelection && rowKey !== undefined) {\n const checkboxProps = rowSelection.getCheckboxProps?.(record) ?? {\n disabled: false,\n };\n\n const content =\n rowSelection.type === 'radio' ? (\n <input\n type=\"radio\"\n checked={!!isSelected}\n disabled={checkboxProps.disabled}\n onChange={(e) => {\n e.stopPropagation();\n rowSelection.onSelect?.(record, true, [record], e.nativeEvent);\n rowSelection.onChange?.([rowKey], [record], { type: 'single' });\n }}\n className=\"cursor-pointer\"\n style={{ accentColor }}\n />\n ) : (\n <input\n type=\"checkbox\"\n checked={!!isSelected}\n disabled={checkboxProps.disabled}\n onChange={(e) => {\n e.stopPropagation();\n // Read from the live prop rather than the closure to avoid stale\n // state when the Cell memo skipped re-renders between selection changes\n const currentKeys = (rowSelection.selectedRowKeys ?? []).map(\n (k) => String(k),\n );\n const newSelected = isSelected\n ? currentKeys.filter((k) => k !== rowKey)\n : [...currentKeys, rowKey];\n const newSelectedRows = (allData ?? []).filter((row, idx) =>\n newSelected.includes(\n getRowKey ? getRowKey(row, idx) : String(idx),\n ),\n );\n rowSelection.onSelect?.(\n record,\n !isSelected,\n newSelectedRows,\n e.nativeEvent,\n );\n rowSelection.onChange?.(newSelected, newSelectedRows, {\n type: 'multiple',\n });\n }}\n className=\"cursor-pointer\"\n style={{ accentColor }}\n />\n );\n\n return (\n <div\n className={`flex items-center overflow-hidden border-b px-2 ${justifyClass} ${column.className ?? ''} ${classNames?.cell ?? ''} `}\n style={{\n height: '100%',\n ...column.style,\n ...(isPinned ? styles?.pinnedCell : undefined),\n }}\n >\n {content}\n </div>\n );\n }\n\n // ── 3. Normal cell ────────────────────────────────────────────────────────\n // Use column.render if provided, otherwise render the raw value.\n // Note: the expand cell (__expand__) is handled by the render function\n // injected into the column definition in BoltTable — it arrives here as\n // a normal cell with a render prop.\n const content = column.render\n ? column.render(value, record, rowIndex)\n : ((value as React.ReactNode) ?? '');\n\n return (\n <div\n className={`flex items-center truncate overflow-hidden border-b px-2 ${justifyClass} ${column.className ?? ''} ${classNames?.cell ?? ''} `}\n style={{\n height: '100%',\n ...column.style,\n ...(isPinned ? styles?.pinnedCell : undefined),\n }}\n >\n {content}\n </div>\n );\n },\n // ── Custom memo comparator ─────────────────────────────────────────────────\n // Minimizes re-renders:\n // - __select__ cells: re-render only when selection changes\n // - __expand__ cells: re-render only when expand state changes\n // - Normal cells: re-render only when value or rowIndex changes\n (prev, next) => {\n if (prev.isLoading !== next.isLoading) return false;\n if (prev.column.key === '__select__') {\n return (\n prev.isSelected === next.isSelected &&\n prev.normalizedSelectedKeys === next.normalizedSelectedKeys\n );\n }\n if (prev.column.key === '__expand__') {\n return prev.isExpanded === next.isExpanded;\n }\n return (\n prev.value === next.value &&\n prev.rowIndex === next.rowIndex &&\n prev.column.key === next.column.key\n );\n },\n);\nCell.displayName = 'Cell';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// MeasuredExpandedRow\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * MeasuredExpandedRow — wraps expanded row content and reports its height.\n *\n * Uses a `ResizeObserver` to watch for content height changes and calls\n * `onResize(rowKey, height)` whenever the height changes. BoltTable uses\n * this callback to update the virtualizer's size estimate for the row,\n * so the expanded panel is always fully visible without overflow.\n *\n * Height updates are debounced via `useRef` to prevent the ResizeObserver\n * from reporting the same height repeatedly (e.g. during scroll reflows).\n * Sub-pixel values are rounded to avoid infinite measurement loops.\n *\n * @internal Used only inside TableBody's expanded row overlay section.\n */\nconst MeasuredExpandedRow = React.memo(\n ({\n rowKey,\n onResize,\n children,\n }: {\n /** The string key of the row being measured */\n rowKey: string;\n /**\n * Called when the content height changes.\n * @param rowKey - The row key\n * @param contentHeight - The new border-box height in pixels (rounded to integer)\n */\n onResize: (rowKey: string, height: number) => void;\n /** The expanded row content to render and measure */\n children: React.ReactNode;\n }) => {\n const ref = useRef<HTMLDivElement>(null);\n\n // Keep onResize in a ref so the ResizeObserver callback always calls the\n // latest version without needing to re-subscribe on every render\n const onResizeRef = useRef(onResize);\n useEffect(() => {\n onResizeRef.current = onResize;\n }, [onResize]);\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n const observer = new ResizeObserver((entries) => {\n const height = entries[0]?.borderBoxSize?.[0]?.blockSize;\n if (height != null && height > 0) {\n onResizeRef.current(rowKey, height);\n }\n });\n observer.observe(el);\n return () => observer.disconnect();\n }, [rowKey]);\n\n return <div ref={ref}>{children}</div>;\n },\n);\nMeasuredExpandedRow.displayName = 'MeasuredExpandedRow';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// TableBody\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * TableBody — the virtualized body renderer for BoltTable.\n *\n * Renders the visible rows using TanStack Virtual's window virtualization.\n * Only the rows currently in (or near) the viewport are in the DOM.\n *\n * Layout strategy:\n * - One `<div>` per column, spanning the full virtual height (`totalSize`px).\n * - Inside each column div, only the visible rows are rendered as\n * `position: absolute` cells stacked at their `virtualRow.start` offset.\n * - Pinned columns use `position: sticky` on the column div itself, backed\n * by a semi-transparent background to visually separate from scrolling content.\n * - Expanded rows are rendered in a separate full-width overlay div that sits\n * on top of all column divs (z-index: 15) and uses `position: sticky; left: 0`\n * to stay viewport-locked during horizontal scroll.\n *\n * @internal This is an internal BoltTable component. Use BoltTable directly.\n */\nconst TableBody: React.FC<TableBodyProps> = ({\n data,\n orderedColumns,\n rowVirtualizer,\n columnOffsets,\n styles,\n classNames,\n rowSelection,\n normalizedSelectedKeys = [],\n getRowKey,\n expandable,\n resolvedExpandedKeys,\n rowHeight = 40,\n scrollAreaWidth,\n accentColor,\n isLoading = false,\n onExpandedRowResize,\n maxExpandedRowHeight,\n}) => {\n const virtualItems = rowVirtualizer.getVirtualItems();\n const totalSize = rowVirtualizer.getTotalSize();\nconst theme = typeof document !== 'undefined' &&\n document.documentElement.classList.contains('dark') ? 'dark' : 'light';\n\n /**\n * Pre-computed styles for each column's spacer div.\n * Memoized so column styles only recompute when columns, offsets,\n * total virtual height, or pinned styles change — not on every scroll.\n *\n * Each spacer div:\n * - Spans the full virtual height (`totalSize`px) so the absolute-positioned\n * cells inside it have a stable containing block for their `top` values.\n * - Is `position: sticky` for pinned columns (with left/right offset).\n * - Gets a semi-transparent background for pinned columns so they visually\n * overlay scrolling content behind them.\n */\n const columnStyles = useMemo(() => {\n return orderedColumns.map((col, colIndex) => {\n const stickyOffset = columnOffsets.get(col.key);\n const isPinned = Boolean(col.pinned);\n\n // z-index hierarchy:\n // - system columns (__select__, __expand__): 11 (above pinned, below menus)\n // - pinned columns: 2 (above scrolling content)\n // - normal columns: 0\n let zIndex = 0;\n if (col.key === '__select__' || col.key === '__expand__') zIndex = 11;\n else if (isPinned) zIndex = 2;\n\n const style: React.CSSProperties = {\n gridColumn: colIndex + 1,\n gridRow: 2,\n height: `${totalSize}px`,\n position: isPinned ? 'sticky' : 'relative',\n zIndex,\n };\n\n if (col.pinned === 'left' && stickyOffset !== undefined)\n style.left = `${stickyOffset}px`;\n else if (col.pinned === 'right' && stickyOffset !== undefined)\n style.right = `${stickyOffset}px`;\n\n if (isPinned) {\n // Frosted glass effect: backdrop-blur + semi-transparent background\n style.backdropFilter = 'blur(14px)';\n style.backgroundColor =\n ((styles as any)?.pinnedBg ?? theme === 'dark')\n ? '#10182890'\n : '#f9fafb90';\n if (styles?.pinnedCell) Object.assign(style, styles.pinnedCell);\n }\n\n return { key: col.key, style, isPinned };\n });\n }, [orderedColumns, columnOffsets, totalSize, styles]);\n\n return (\n <>\n {/*\n * ── Column spacer divs ───────────────────────────────────────────────\n * One div per column. Each div:\n * - Is `height: totalSize` to create the scrollable area\n * - Contains only the virtual (visible) rows as absolute children\n * - Is sticky for pinned columns\n */}\n {columnStyles.map((colStyle, colIndex) => {\n const col = orderedColumns[colIndex];\n\n return (\n <div\n className=\"truncate\"\n key={`spacer-${colStyle.key}`}\n style={colStyle.style}\n >\n {virtualItems.map((virtualRow: VirtualItem) => {\n const row = data[virtualRow.index];\n const rowKey = getRowKey\n ? getRowKey(row, virtualRow.index)\n : String(virtualRow.index);\n const isSelected = normalizedSelectedKeys.includes(rowKey);\n const isExpanded = resolvedExpandedKeys?.has(rowKey) ?? false;\n const cellValue = row[col.dataIndex];\n // Rows with shimmer keys or when isLoading=true render as skeletons\n const isRowShimmer = isLoading || rowKey.startsWith('__shimmer_');\n\n return (\n /*\n * Row wrapper div:\n * - data-row-key: used by BoltTable's DOM-based hover system\n * (mouseover reads this attribute to apply hover styles\n * across all column divs for the same row simultaneously)\n * - data-selected: presence/absence attribute consumed by the\n * CSS injected by BoltTable for selected row background\n * - Absolute positioned at virtualRow.start for virtualization\n * - Height = virtualRow.size (includes expanded row height)\n */\n <div\n key={`${rowKey}-${col.key}`}\n data-row-key={rowKey}\n data-selected={isSelected || undefined}\n style={{\n position: 'absolute',\n top: `${virtualRow.start}px`,\n left: 0,\n right: 0,\n height: `${virtualRow.size}px`,\n }}\n className=\"truncate\"\n >\n {/* The cell itself only occupies rowHeight, not the full virtualRow.size */}\n <div\n style={{ height: `${rowHeight}px`, position: 'relative' }}\n className=\"truncate\"\n >\n <Cell\n value={cellValue}\n record={row}\n column={col}\n rowIndex={virtualRow.index}\n classNames={classNames}\n styles={styles}\n isSelected={isSelected}\n isExpanded={isExpanded}\n rowSelection={rowSelection}\n normalizedSelectedKeys={normalizedSelectedKeys}\n rowKey={rowKey}\n allData={data}\n getRowKey={getRowKey}\n accentColor={accentColor}\n isLoading={isRowShimmer}\n />\n </div>\n </div>\n );\n })}\n </div>\n );\n })}\n\n {/*\n * ── Expanded row overlay ─────────────────────────────────────────────\n * A single full-width div that spans all grid columns (gridColumn: 1/-1).\n * It has the same total height as the column spacers so absolute\n * positioning of expanded panels uses the same coordinate space.\n *\n * Each expanded panel:\n * - Is positioned at virtualRow.start + rowHeight (directly below its row)\n * - Uses `position: sticky; left: 0; width: scrollAreaWidth` to stay\n * viewport-locked during horizontal scroll (pure CSS, no JS needed)\n * - Is wrapped in MeasuredExpandedRow to auto-size the virtualizer slot\n *\n * Why a separate overlay div instead of rendering inside each column?\n * Because the expanded content spans the full visible width, not one column.\n * A single overlay avoids duplicating the content per column.\n */}\n {expandable && (\n <div\n style={{\n gridColumn: '1 / -1',\n gridRow: 2,\n height: `${totalSize}px`,\n position: 'relative',\n zIndex: 15,\n // pointerEvents: none on the overlay so hover/click pass through\n // to the cells below for rows that are NOT expanded\n pointerEvents: 'none',\n }}\n >\n {virtualItems.map((virtualRow: VirtualItem) => {\n const row = data[virtualRow.index];\n const rk = getRowKey\n ? getRowKey(row, virtualRow.index)\n : String(virtualRow.index);\n\n // Skip rows that are not expanded\n if (!(resolvedExpandedKeys?.has(rk) ?? false)) return null;\n\n const expandedContent = (\n <div\n className={`${classNames?.expandedRow ?? ''}`}\n style={{\n // Sticky left:0 + fixed width = viewport-locked panel\n // regardless of how far the user has scrolled horizontally\n position: 'sticky',\n left: 0,\n zIndex: 5,\n width:\n scrollAreaWidth && scrollAreaWidth > 0\n ? `${scrollAreaWidth}px`\n : '100%',\n overflow: 'auto',\n // Restore pointer events so the expanded content is interactive\n pointerEvents: 'auto',\n borderBottom: '1px solid hsl(var(--border))',\n backgroundColor: 'hsl(var(--muted)/0.4)',\n padding: 20,\n // Optional max height — makes the panel scrollable for tall content\n ...(maxExpandedRowHeight\n ? { maxHeight: `${maxExpandedRowHeight}px` }\n : undefined),\n ...styles?.expandedRow,\n }}\n >\n {expandable.expandedRowRender(row, virtualRow.index, 0, true)}\n </div>\n );\n\n return (\n <div\n key={`expanded-${rk}`}\n style={{\n position: 'absolute',\n // Position immediately below the row's base height\n top: virtualRow.start + rowHeight,\n left: 0,\n right: 0,\n }}\n >\n {/*\n * Wrap in MeasuredExpandedRow when a resize callback is provided.\n * This lets the virtualizer know the actual content height so it\n * allocates the right space. Without this, the virtualizer uses\n * `expandedRowHeight` as an estimate, which may cause clipping.\n */}\n {onExpandedRowResize ? (\n <MeasuredExpandedRow\n rowKey={rk}\n onResize={onExpandedRowResize}\n >\n {expandedContent}\n </MeasuredExpandedRow>\n ) : (\n expandedContent\n )}\n </div>\n );\n })}\n </div>\n )}\n </>\n );\n};\n\nTableBody.displayName = 'TableBody';\n\nexport default TableBody;"],"mappings":"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","names":["GripVertical","React","useMemo","useRef","useState","useRef","Fragment","jsx","jsxs","React","useEffect","useRef","Fragment","jsx","jsxs","content","jsx","jsxs","SHIMMER_WIDTHS","useState","useRef","React","useMemo","_","GripVertical"]}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "bolt-table",
3
+ "version": "0.1.0",
4
+ "description": "Virtualized React table with column drag & drop, pinning, resizing, sorting, filtering, and pagination.",
5
+ "license": "MIT",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.cjs"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsup",
22
+ "prepublishOnly": "npm run build"
23
+ },
24
+ "peerDependencies": {
25
+ "@dnd-kit/core": ">=6",
26
+ "@dnd-kit/sortable": ">=7",
27
+ "@tanstack/react-virtual": "^3.13.22",
28
+ "lucide-react": ">=0.300.0",
29
+ "react": ">=18",
30
+ "react-dom": ">=18"
31
+ },
32
+ "devDependencies": {
33
+ "@types/react": "^18.0.0",
34
+ "@types/react-dom": "^19.2.3",
35
+ "tsup": "^8.0.0",
36
+ "typescript": "^5.0.0"
37
+ }
38
+ }