import React, { forwardRef, useMemo, useRef, useEffect, useState, useCallback, } from 'react' import { Edit, Save, X, ChevronRight, ChevronDown, MoreHorizontal, } from 'lucide-react' import { styled, css } from 'styled-components' import { motion } from 'framer-motion' import { TableBodyProps } from './types' import { getFrozenLeftOffset, getFrozenRightOffset } from '../TableHeader/utils' import TableCell from '../TableCell' import Block from '../../Primitives/Block/Block' import { FOUNDATION_THEME } from '../../../tokens' import Menu from '../../Menu/Menu' import { MenuAlignment, MenuSide } from '../../Menu/types' import Skeleton from '../../Skeleton/Skeleton' import { getSkeletonState } from '../../Skeleton/utils' import { useRowFlip } from '../hooks/useRowFlip' import { Button, ButtonType, ButtonSize, ButtonSubType } from '../../Button' import { Checkbox, CheckboxSize } from '../../Checkbox' import { Tooltip } from '../../Tooltip' import type { TableTokenType } from '../dataTable.tokens' import { useResponsiveTokens } from '../../../hooks/useResponsiveTokens' import { useTheme } from '../../../context/ThemeContext' import { TooltipSide, TooltipAlign, TooltipSize } from '../../Tooltip/types' const TableRow = styled.tr<{ $tableToken: TableTokenType $isClickable?: boolean $customBackgroundColor?: string $hasCustomBackground?: boolean $isSticky?: boolean $headerHeight?: string }>` border-bottom: ${({ $tableToken }) => $tableToken.dataTable.table.body.cell.borderTop}; background-color: ${({ $tableToken }) => $tableToken.dataTable.table.body.row.backgroundColor}; ${({ $customBackgroundColor, $isClickable, $hasCustomBackground, $isSticky, $headerHeight, $tableToken, }) => css` ${$customBackgroundColor && css` background-color: ${$customBackgroundColor} !important; `} ${$isClickable && css` cursor: pointer; `} ${$isSticky && css` position: sticky; top: ${$headerHeight || '46px'}; z-index: 15; `} ${!$hasCustomBackground && css` &:hover, &[data-focused-row='true'] { background-color: ${$tableToken.dataTable.table.body.row[ '&:hover' ].backgroundColor} !important; td { background-color: ${$tableToken.dataTable.table.body.row[ '&:hover' ].backgroundColor} !important; } } `} `} ` const StyledTableCell = styled.td<{ $tableToken: TableTokenType $width?: string $customBackgroundColor?: string $hasCustomBackground?: boolean $isFirstRow?: boolean }>` ${({ $tableToken }) => $tableToken.dataTable.table.body.cell} padding: ${FOUNDATION_THEME.unit[12]} ${FOUNDATION_THEME.unit[16]}; text-align: left; vertical-align: middle; background-color: ${({ $tableToken }) => $tableToken.dataTable.table.body.row.backgroundColor}; ${({ $isFirstRow, $tableToken }) => !$isFirstRow && css` border-top: ${$tableToken.dataTable.table.body.cell.borderTop}; `} ${({ $width, $customBackgroundColor, $hasCustomBackground, $tableToken, }) => css` ${$width && css` width: ${$width}; min-width: ${$width}; max-width: ${$width}; `} ${$customBackgroundColor && css` background-color: ${$customBackgroundColor} !important; `} overflow: hidden; box-sizing: border-box; ${!$hasCustomBackground && css` tr:hover & { background-color: ${$tableToken.dataTable.table.body.row[ '&:hover' ].backgroundColor} !important; } `} `} ` const ExpandedCell = styled.td<{ $tableToken: TableTokenType }>` ${({ $tableToken }) => $tableToken.dataTable.table.body.cell.expandable} background-color: ${({ $tableToken }) => $tableToken.dataTable.table.body.row['&:hover'] .backgroundColor} !important; position: relative; overflow: hidden; z-index: 0; ` const ExpandedRow = styled(TableRow)` background-color: ${({ $tableToken }) => $tableToken.dataTable.table.body.row['&:hover'].backgroundColor}; ` const ExpandButton = styled.button<{ $tableToken: TableTokenType }>` display: flex; align-items: center; justify-content: center; padding: ${FOUNDATION_THEME.unit[4]}; border: ${({ $tableToken }) => $tableToken.dataTable.table.body.cell.expandable.expandButton.border}; background: ${({ $tableToken }) => $tableToken.dataTable.table.body.cell.expandable.expandButton .backgroundColor}; cursor: ${({ $tableToken }) => $tableToken.dataTable.table.body.cell.expandable.expandButton.cursor}; border-radius: ${({ $tableToken }) => $tableToken.dataTable.table.body.cell.expandable.expandButton .borderRadius}; color: ${({ $tableToken }) => $tableToken.dataTable.table.body.cell.expandable.expandButton.color}; transition: ${({ $tableToken }) => $tableToken.dataTable.table.body.cell.expandable.expandButton .transition}; &:hover { background-color: ${({ $tableToken }) => $tableToken.dataTable.table.body.cell.expandable.expandButton[ '&:hover' ].backgroundColor}; color: ${({ $tableToken }) => $tableToken.dataTable.table.body.cell.expandable.expandButton[ '&:hover' ].color}; } visibility: visible; opacity: 1; &:focus-visible { outline: ${({ $tableToken }) => $tableToken.header.actionIcons.columnManagerTrigger?.focusVisible .outline}; } ` type ActionsCellProps = { row: Record index: number isEditing: boolean enableInlineEdit?: boolean rowActions?: { showEditAction?: boolean slot1?: { id: string text?: string buttonType?: ButtonType size?: ButtonSize subType?: ButtonSubType leadingIcon?: React.ReactNode trailingIcon?: React.ReactNode disabled?: | boolean | ((row: Record, index: number) => boolean) hidden?: | boolean | ((row: Record, index: number) => boolean) onClick: (row: Record, index: number) => void } slot2?: { id: string text?: string buttonType?: ButtonType size?: ButtonSize subType?: ButtonSubType leadingIcon?: React.ReactNode trailingIcon?: React.ReactNode disabled?: | boolean | ((row: Record, index: number) => boolean) hidden?: | boolean | ((row: Record, index: number) => boolean) onClick: (row: Record, index: number) => void } } onEditRow: (rowId: unknown) => void onSaveRow: (rowId: unknown) => void onCancelEdit: (rowId: unknown) => void idField: string } const ActionsCell: React.FC = ({ row, index, isEditing, enableInlineEdit, rowActions, onEditRow, onSaveRow, onCancelEdit, idField, }) => { const containerRef = useRef(null) const [visibleButtons, setVisibleButtons] = useState(0) const [shouldShowMenu, setShouldShowMenu] = useState(false) // Build all available buttons const allButtons = useMemo(() => { const buttons: Array<{ id: string type: 'edit' | 'save' | 'cancel' | 'slot1' | 'slot2' element: React.ReactNode menuItem?: { label: string onClick: () => void disabled?: boolean leadingSlot?: React.ReactNode } }> = [] // Edit actions if (enableInlineEdit && rowActions?.showEditAction !== false) { if (isEditing) { buttons.push({ id: 'save', type: 'save', element: (