// Copyright (c) Meta Platforms, Inc. and affiliates. 'use client'; /** * @file TableCell.tsx * @input React, StyleX, TableContext, theme tokens, useTableCellStyles * @output Exports TableCell component, TableCellProps * @position Sub-component; used inside Table children mode * * SYNC: When modified, update: * - /packages/core/src/Table/Table.doc.mjs * - /packages/core/src/Table/index.ts * - /packages/cli/assets/templates/blocks/components/Table/ (showcase blocks) */ import type { ReactNode } from 'react'; import type { BaseProps } from "../BaseProps.js"; import * as stylex from "../style-runtime.js"; import "../theme/tokens.stylex.js"; import { borderVars, colorVars, spacingVars, typeScaleVars } from "../theme/tokens.stylex.js"; import type { StyleXStyles } from "../theme/types.js"; import type { TableContextActions } from "./types.js"; import { wrapInTableContextMenu } from "./tableContextMenu.js"; import "./table.stylex.js"; import { overflowStyles, wrapStyles, containerEdgeStyles, tableRowMarker } from "./table.stylex.js"; import { useTableContext, buildDividerStyles, mergeXStyle } from "./useTableCellStyles.js"; import { mergeProps } from "../utils/index.js"; import { themeProps } from "../utils/themeProps.js"; /** Props for TableCell — thin `` wrapper */ export interface TableCellProps extends BaseProps { /** Ref forwarded to the root element */ ref?: React.Ref; /** 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 `; } TableCell.displayName = 'TableCell';
`. * * @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}