import type { ReactNode } from 'react'; import { useCallback } from 'react'; import type { GestureResponderHandlers } from 'react-native'; import { Pressable, TextInput, View } from 'react-native'; import { AddLine as Plus } from 'rn-motion-ui-icons/icons/add-line'; import { Delete2Line as Trash2 } from 'rn-motion-ui-icons/icons/delete-2-line'; import { DotsVerticalLine as GripVertical } from 'rn-motion-ui-icons/icons/dots-vertical-line'; import { UpLine as ChevronUp } from 'rn-motion-ui-icons/icons/up-line'; import { useIsRTL } from '../../../hooks/use-direction'; import { cn } from '../../../lib/cn'; import { MotiView } from '../../../moti/components/view'; import { useThemeColor } from '../../../theme/use-theme-color'; import { Text } from '../../typography/Text/text'; import type { SortDirection, TableColumn } from './table-types'; import { alignToJustifyClass, alignToTextClass, columnLayoutStyle } from './table-utils'; type SortIndicatorProps = { isActive: boolean; activeDirection: SortDirection | undefined; reduce: boolean; activeColor: string; mutedColor: string; sortIcon?: ReactNode; }; function SortIndicator({ isActive, activeDirection, reduce, activeColor, mutedColor, sortIcon }: SortIndicatorProps) { const rotate = isActive && activeDirection === 'desc' ? '180deg' : '0deg'; const opacity = isActive ? 1 : 0.35; return ( {sortIcon ?? } ); } type ColumnActionOverlayProps = { isRTL: boolean; header: ReactNode; insertColor: string; onInsert?: () => void; onDelete?: () => void; }; function ColumnActionOverlay({ isRTL, header, insertColor, onInsert, onDelete }: ColumnActionOverlayProps) { return ( {onInsert ? ( ) : null} {onDelete ? ( ) : null} ); } type ReorderGripProps = { isRTL: boolean; columnKey: string; mutedColor: string; gripHandlers: (key: string) => GestureResponderHandlers; testID?: string; }; function ReorderGrip({ isRTL, columnKey, mutedColor, gripHandlers, testID }: ReorderGripProps) { return ( ); } export type HeaderCellProps = { column: TableColumn; colIndex: number; /** Resolved pixel width for this column (from `computeColumnWidths`). */ colWidth?: number; isActive: boolean; activeDirection: SortDirection | undefined; isColPressed: boolean; isDragging: boolean; reorderable: boolean; hasColMenu: boolean; reduce: boolean; /** * Effective sortability for this column — `table-level sortable && column.sortable`. * When false the header renders as non-interactive even if `column.sortable` is true. */ sortEnabled: boolean; gripHandlers: (key: string) => GestureResponderHandlers; toggleSort: (key: string) => void; setPressedColKey: (key: string | null) => void; onColumnRename?: (key: string, value: string) => void; onInsertColumn?: (index: number, position: 'before' | 'after') => void; onDeleteColumn?: (key: string) => void; testID?: string; /** Replace the sort-direction chevron. Default: ``. */ sortIcon?: ReactNode; }; // One column header. Sort/menu/rename callbacks are stable useCallbacks bound to // this column so the header JSX never binds inline arrows. export function HeaderCell({ column, colIndex, colWidth, isActive, activeDirection, isColPressed, isDragging, reorderable, hasColMenu, reduce, sortEnabled, gripHandlers, toggleSort, setPressedColKey, onColumnRename, onInsertColumn, onDeleteColumn, testID, sortIcon, }: HeaderCellProps) { const isRTL = useIsRTL(); const textAlignClass = alignToTextClass(column.align, isRTL); const mutedForeground = useThemeColor('muted-foreground'); const foregroundForeground = useThemeColor('foreground'); const primaryForeground = useThemeColor('primary-foreground'); const handleLongPress = useCallback(() => { if (hasColMenu) setPressedColKey(isColPressed ? null : column.key); }, [hasColMenu, isColPressed, column.key, setPressedColKey]); const handleSort = useCallback(() => toggleSort(column.key), [toggleSort, column.key]); const handleRename = useCallback((v: string) => onColumnRename?.(column.key, v), [onColumnRename, column.key]); const handleInsertColumn = useCallback(() => { onInsertColumn?.(colIndex, 'before'); setPressedColKey(null); }, [onInsertColumn, colIndex, setPressedColKey]); const handleDeleteColumn = useCallback(() => { onDeleteColumn?.(column.key); setPressedColKey(null); }, [onDeleteColumn, column.key, setPressedColKey]); const dragOpacity = isDragging ? 0.5 : 1; const dragScale = isDragging ? 1.04 : 1; const dragTransition = { type: 'timing' as const, duration: reduce ? 0 : 180 }; return ( {/* Header content lifts (scale + fade) while its column is being dragged. */} {/* Reorder grip — a PanResponder here claims the drag so it never sorts. */} {reorderable ? ( ) : null} {onColumnRename && !sortEnabled ? ( ) : ( {column.header} {sortEnabled ? ( ) : null} )} {/* Column action overlay on long-press, pinned to the trailing edge — the left one under RTL, since `right` stays physical in both directions. */} {isColPressed && hasColMenu ? ( ) : null} ); }