remirror/remirror

View on GitHub
packages/remirror__extension-react-tables/src/components/table-cell-menu.tsx

Summary

Maintainability
A
0 mins
Test Coverage
F
17%
import React, { useState } from 'react';
import { PositionerPortal } from '@remirror/react-components';
import { useCommands } from '@remirror/react-core';
import { useEditorEvent, usePositioner } from '@remirror/react-hooks';
import { ComponentsTheme } from '@remirror/theme';

import { menuCellPositioner } from '../block-positioner';
import { borderWidth } from '../const';

export interface TableCellMenuComponentProps {
  popupOpen: boolean;
  setPopupOpen: React.Dispatch<React.SetStateAction<boolean>>;
}

type TableCellMenuComponent = React.ComponentType<TableCellMenuComponentProps>;

export interface TableCellMenuProps {
  Component?: TableCellMenuComponent;
}

const DefaultTableCellMenuButton: React.FC<TableCellMenuComponentProps> = ({ setPopupOpen }) => (
  <button
    onClick={() => {
      setPopupOpen(true);
    }}
    onMouseDown={(event) => {
      // Stop the parent component from listening the onMouseDown event
      event.preventDefault();
      event.stopPropagation();
    }}
    style={{
      position: 'relative',
      right: '0px',
      top: '0px',
      height: '16px',
      width: '16px',
      border: '1px solid blue',
      fontSize: '10px',
      lineHeight: '10px',
      cursor: 'pointer',
    }}
    className={ComponentsTheme.BUTTON}
  >
    v
  </button>
);

interface DefaultTableCellMenuItemProps {
  label: string;
  onClick: () => void;
  disabled?: boolean;
}
const DefaultTableCellMenuItem: React.FC<DefaultTableCellMenuItemProps> = (props) => (
  <button disabled={props.disabled} onClick={props.onClick} className={ComponentsTheme.MENU_ITEM}>
    {props.label}
  </button>
);

const DefaultTableCellMenuPopup: React.FC<TableCellMenuComponentProps> = ({
  setPopupOpen,
  popupOpen,
}) => {
  const commands = useCommands();

  // close the popup after clicking
  const handleClick = (command: () => void) => () => {
    command();
    setPopupOpen(false);
  };

  // Notice that we won't close the popup after changing the cell background
  // because we want users to quick try multiple colors.
  const setTableCellBackground = (color: string | null) => () => {
    commands.setTableCellBackground(color);
  };

  return (
    <div
      style={{
        position: 'absolute',
        backgroundColor: 'white',
        width: '200px',
        border: '1px solid lightgray',
        display: popupOpen ? 'flex' : 'none',
        flexDirection: 'column',
      }}
      className={ComponentsTheme.MENU}
    >
      <DefaultTableCellMenuItem label='Color teal' onClick={setTableCellBackground('teal')} />
      <DefaultTableCellMenuItem
        label='Color pink'
        onClick={setTableCellBackground('rgba(255,100,100,0.3)')}
      />
      <DefaultTableCellMenuItem label='Remove color' onClick={setTableCellBackground(null)} />

      <DefaultTableCellMenuItem
        label='Add row above'
        onClick={handleClick(commands.addTableRowBefore)}
      />
      <DefaultTableCellMenuItem
        label='Add row below'
        onClick={handleClick(commands.addTableRowAfter)}
      />
      <DefaultTableCellMenuItem
        label='Add column before'
        onClick={handleClick(commands.addTableColumnBefore)}
      />
      <DefaultTableCellMenuItem
        label='Add column after'
        onClick={handleClick(commands.addTableColumnAfter)}
      />
      <DefaultTableCellMenuItem
        label='Merge cells'
        onClick={handleClick(commands.mergeTableCells)}
        disabled={!commands.mergeTableCells.enabled()}
      />
      <DefaultTableCellMenuItem
        label='Split cells'
        onClick={handleClick(commands.splitTableCell)}
        disabled={!commands.splitTableCell.enabled()}
      />
      <DefaultTableCellMenuItem
        label='Remove column'
        onClick={handleClick(commands.deleteTableColumn)}
      />
      <DefaultTableCellMenuItem label='Remove row' onClick={handleClick(commands.deleteTableRow)} />
      <DefaultTableCellMenuItem label='Remove table' onClick={handleClick(commands.deleteTable)} />
    </div>
  );
};

const DefaultTableCellMenuComponent: React.FC<TableCellMenuComponentProps> = (props) => (
  <>
    <DefaultTableCellMenuButton {...props} />
    <DefaultTableCellMenuPopup {...props} />
  </>
);

const TableCellMenu: React.FC<TableCellMenuProps> = ({
  Component = DefaultTableCellMenuComponent,
}) => {
  const position = usePositioner(menuCellPositioner, []);
  const { ref, width, height, x, y } = position;
  const [popupOpen, setPopupOpen] = useState(false);

  // Hide the popup when users click.
  useEditorEvent('mousedown', () => {
    if (popupOpen) {
      setPopupOpen(false);
    }

    return false;
  });

  return (
    <PositionerPortal>
      <div
        ref={ref}
        style={{
          position: 'absolute',
          left: x,
          top: y,
          width: width + borderWidth,
          height: height + borderWidth,
          zIndex: 100,
          pointerEvents: 'none',

          // place the child into the top-left corner
          display: 'flex',
          justifyContent: 'flex-end',
          alignItems: 'flex-start',

          // for debug:
          // backgroundColor: 'lightpink',
          // opacity: 0.5,
        }}
      >
        <div
          style={{
            zIndex: 100,
            pointerEvents: 'initial',
          }}
        >
          <Component popupOpen={popupOpen} setPopupOpen={setPopupOpen} />
        </div>
      </div>
    </PositionerPortal>
  );
};

export { TableCellMenu };