;
/** Specifies which cells this cell relates to (used on `| ` acting as a row header). */
scope?: 'col' | 'row' | 'colgroup' | 'rowgroup';
/** Space-separated list of header cell IDs this cell is described by. */
headers?: string;
/** Number of columns this cell spans. Standard HTML ` | ` attribute. */
colSpan?: number;
/** Number of rows this cell spans. Standard HTML ` | ` attribute. */
rowSpan?: number;
children?: ReactNode;
/**
* StyleX styles for layout customization (margins, positioning, sizing).
* Must be a `stylex.create()` value — not an inline style object.
*/
xstyle?: StyleXStyles | StyleXStyles[];
/**
* Right-click actions rendered as a context menu around the cell content.
* The cell owns the wrapper so it controls how the menu interacts with its
* padding / content. Empty or undefined renders no menu.
*/
contextMenuActions?: TableContextActions;
}
const densityStyles = {
compact: {
k8WAf4: "xu0wf1k",
kg3NbH: "xf314gf",
kGuDYH: "xjm74w1",
kB7OPa: "x9f619",
$$css: true as const
},
balanced: {
k8WAf4: "xce4md1",
kg3NbH: "xrrkdod",
kGuDYH: "xjm74w1",
kB7OPa: "x9f619",
$$css: true as const
},
spacious: {
k8WAf4: "x8o8v82",
kg3NbH: "x1pzlopt",
kGuDYH: "xjm74w1",
kB7OPa: "x9f619",
$$css: true as const
}
};
// When a cell owns a context menu, its padding moves from the ` | ` onto the
// right-click trigger wrapper so the *entire* cell (padding included) opens the
// menu. Without this, right-clicking the padding ring near a cell edge falls
// through to the browser's native menu. Paired with `contextMenuCellStyles` on
// the ` | ` (font/box-sizing only — no padding).
const densityTextStyles = {
compact: {
kGuDYH: "xjm74w1",
kB7OPa: "x9f619",
$$css: true as const
},
balanced: {
kGuDYH: "xjm74w1",
kB7OPa: "x9f619",
$$css: true as const
},
spacious: {
kGuDYH: "xjm74w1",
kB7OPa: "x9f619",
$$css: true as const
}
};
const densityPaddingStyles = {
compact: {
k8WAf4: "xu0wf1k",
kg3NbH: "xf314gf",
$$css: true as const
},
balanced: {
k8WAf4: "xce4md1",
kg3NbH: "xrrkdod",
$$css: true as const
},
spacious: {
k8WAf4: "x8o8v82",
kg3NbH: "x1pzlopt",
$$css: true as const
}
};
// The trigger wrapper fills the whole cell (a ` | ` child with height:100%
// stretches to the row height) so a right-click anywhere in the cell opens the
// menu. It carries the density padding relocated off the ` | `.
const triggerFillStyles = {
fill: {
k1xSpc: "x1lliihq",
kB7OPa: "x9f619",
kZKoxP: "x5yr21d",
kzqmXN: "xh8yej3",
$$css: true as const
}
};
// `height:100%` on the ` | ` is the CSS trick that lets a block child resolve
// its own `height:100%` against the row height — so the trigger fills the cell
// vertically (top/bottom padding included) and is fully right-clickable.
const contextMenuCellStyles = {
cell: {
kZKoxP: "x5yr21d",
$$css: true as const
}
};
const dividerRowStyles = {
cell: {
kt9PQ7: "x92x3c3 x1wp2rvj",
kfdmCh: "x1q0q8m5",
kL6WhQ: "xw8gpjh",
$$css: true as const
}
};
const dividerColumnStyles = {
cell: {
ke9TFa: "xw8tdv1 x1qlsc9e",
k8ry5P: "x18b5jzi",
kBCPoo: "x1gejf6u",
$$css: true as const
}
};
const verticalAlignStyles = {
middle: {
kXLuUW: "xxymvpz",
$$css: true as const
},
top: {
kXLuUW: "x16dsc37",
$$css: true as const
},
bottom: {
kXLuUW: "x3ajldb",
$$css: true as const
}
};
/**
* TableCell — a ` | ` wrapper for children/streaming mode.
*
* When used inside ``, inherits styling from the table context
* (density padding, divider borders). When used standalone, renders a plain `| `.
*
* @example
* ```
*
* Alice
* 30
*
* ```
*/
export function TableCell({
children,
xstyle,
ref,
className: incomingClassName,
style: incomingStyle,
contextMenuActions,
...props
}: TableCellProps) {
const ctx = useTableContext();
const hasContextMenu = typeof contextMenuActions === 'function' || Array.isArray(contextMenuActions) && contextMenuActions.length > 0;
// When the cell owns a context menu, its density padding is relocated onto
// the right-click trigger wrapper (see `content` below) so the entire cell —
// padding included — opens the menu. Otherwise padding lives on the ` | `.
const cellStyles: StyleXStyles[] = ctx ? [hasContextMenu ? densityTextStyles[ctx.density] : densityStyles[ctx.density], ctx.textOverflow === 'truncate' ? overflowStyles.cell : wrapStyles.cell, containerEdgeStyles[ctx.density], verticalAlignStyles[ctx.verticalAlign], ...buildDividerStyles(ctx, dividerRowStyles.cell, dividerColumnStyles.cell), ...(hasContextMenu ? [contextMenuCellStyles.cell] : [])] : [];
// The cell owns the context-menu wrapper so it controls how the menu
// interacts with its padding / content. No-op when no actions. When actions
// exist we make the trigger fill the cell and carry the density padding so a
// right-click anywhere in the cell (including the padding ring) opens it.
const triggerXstyle = hasContextMenu && ctx ? [triggerFillStyles.fill, densityPaddingStyles[ctx.density]] : hasContextMenu ? triggerFillStyles.fill : undefined;
const content = wrapInTableContextMenu(children, contextMenuActions, triggerXstyle);
return |
{content}
| ;
}
TableCell.displayName = 'TableCell';
|