Skip to content
Snippets Groups Projects
table-cell-element.tsx 4.26 KiB
Newer Older
Leon Jung's avatar
Leon Jung committed
'use client';

import React from 'react';

import { cn, withProps, withRef } from '@udecode/cn';
import { useElement } from '@udecode/plate-common/react';
import { useBlockSelected } from '@udecode/plate-selection/react';
import {
  TableRowPlugin,
  useTableCellElement,
  useTableCellElementResizable,
  useTableCellElementResizableState,
  useTableCellElementState,
} from '@udecode/plate-table/react';

import { blockSelectionVariants } from './block-selection';
import { PlateElement } from './plate-element';
import { ResizeHandle } from './resizable';

export const TableCellElement = withRef<
  typeof PlateElement,
  {
    hideBorder?: boolean;
    isHeader?: boolean;
  }
>(({ children, className, hideBorder, isHeader, style, ...props }, ref) => {
  const { element } = props;

  const rowElement = useElement(TableRowPlugin.key);
  const isSelectingRow = useBlockSelected(rowElement.id as string);

  const {
    borders,
    colIndex,
    colSpan,
    hovered,
    hoveredLeft,
    isSelectingCell,
    readOnly,
    rowIndex,
    rowSize,
    selected,
  } = useTableCellElementState();
  const { props: cellProps } = useTableCellElement({ element: props.element });
  const resizableState = useTableCellElementResizableState({
    colIndex,
    colSpan,
    rowIndex,
  });

  const { bottomProps, hiddenLeft, leftProps, rightProps } =
    useTableCellElementResizable(resizableState);

  return (
    <PlateElement
      ref={ref}
      as={isHeader ? 'th' : 'td'}
      className={cn(
        className,
        'h-full overflow-visible border-none bg-white p-0 dark:bg-zinc-950',
        hideBorder && 'before:border-none',
        element.background ? 'bg-[--cellBackground]' : 'bg-white dark:bg-zinc-950',
        !hideBorder &&
          cn(
            isHeader && 'text-left [&_>_*]:m-0',
            'before:size-full',
            selected && 'before:z-10 before:bg-zinc-100 dark:before:bg-zinc-800',
Leon Jung's avatar
Leon Jung committed
            'before:absolute before:box-border before:select-none before:content-[""]',
Leon Jung's avatar
Leon Jung committed
            borders &&
              cn(
                borders.bottom?.size &&
                  `before:border-b before:border-b-border`,
                borders.right?.size && `before:border-r before:border-r-border`,
                borders.left?.size && `before:border-l before:border-l-border`,
                borders.top?.size && `before:border-t before:border-t-border`
              )
          )
      )}
      {...cellProps}
      {...props}
      style={
        {
          '--cellBackground': element.background,
          ...style,
        } as React.CSSProperties
      }
    >
      <div
        className='relative z-20 box-border h-full px-4 py-2'
        style={{
          minHeight: rowSize,
        }}
      >
        {children}
      </div>

      {!isSelectingCell && (
        <div
          className='group absolute top-0 size-full select-none'
          contentEditable={false}
          suppressContentEditableWarning={true}
        >
          {!readOnly && (
            <>
              <ResizeHandle
                {...rightProps}
                className='-top-3 right-[-5px] w-[10px]'
              />
              <ResizeHandle
                {...bottomProps}
                className='bottom-[-5px] h-[10px]'
              />
              {!hiddenLeft && (
                <ResizeHandle
                  {...leftProps}
                  className='-top-3 left-[-5px] w-[10px]'
                />
              )}

              {hovered && (
                <div
                  className={cn(
                    'absolute -top-3 z-30 h-[calc(100%_+_12px)] w-1 bg-zinc-950 dark:bg-zinc-300',
                    'right-[-1.5px]'
                  )}
                />
              )}
              {hoveredLeft && (
                <div
                  className={cn(
                    'absolute -top-3 z-30 h-[calc(100%_+_12px)] w-1 bg-zinc-950 dark:bg-zinc-300',
                    'left-[-1.5px]'
                  )}
                />
              )}
            </>
          )}
        </div>
      )}

      {isSelectingRow && (
        <div className={blockSelectionVariants()} contentEditable={false} />
      )}
    </PlateElement>
  );
});

TableCellElement.displayName = 'TableCellElement';

export const TableCellHeaderElement = withProps(TableCellElement, {
  isHeader: true,
});