remirror/remirror

View on GitHub
packages/remirror__extension-react-tables/src/components/table-insert-button-trigger.ts

Summary

Maintainability
A
35 mins
Test Coverage
F
7%
import { EditorView } from '@remirror/pm';
import { ExtensionTablesTheme } from '@remirror/theme';

import { borderWidth } from '../const';
import { getCellAxisByMouseEvent } from '../utils/controller';
import { getRelativeCoord, h } from '../utils/dom';
import { setNodeAttrs } from '../utils/prosemirror';
import { CellAxis, FindTable } from '../utils/types';
import { InsertButtonAttrs } from './table-insert-button';

enum TriggerAreaType {
  ADD_COLUMN_BEFORE = 1,
  ADD_COLUMN_AFTER = 2,
  ADD_ROW_BEFORE = 3,
  ADD_ROW_AFTER = 4,
}

function buildInsertButtonAttrs(
  type: TriggerAreaType,
  triggerRect: DOMRect,
  editorDom: Element,
  cellAxis: CellAxis,
): InsertButtonAttrs {
  const { row, col } = cellAxis;
  const relativeCoord = getRelativeCoord(triggerRect, editorDom);

  switch (type) {
    case TriggerAreaType.ADD_COLUMN_BEFORE:
      return {
        triggerRect,
        x: relativeCoord.x - borderWidth,
        y: relativeCoord.y + 12,
        row: -1,
        col: col,
      };
    case TriggerAreaType.ADD_COLUMN_AFTER:
      return {
        triggerRect,
        x: relativeCoord.x + triggerRect.width,
        y: relativeCoord.y + 12,
        row: -1,
        col: col + 1,
      };
    case TriggerAreaType.ADD_ROW_BEFORE:
      return {
        triggerRect,
        x: relativeCoord.x + 12,
        y: relativeCoord.y + 5 - borderWidth,
        row: row,
        col: -1,
      };
    default:
      return {
        triggerRect,
        x: relativeCoord.x + 12,
        y: relativeCoord.y + 5 + triggerRect.height,
        row: row + 1,
        col: -1,
      };
  }
}

function showButton(
  trigger: HTMLElement,
  findTable: FindTable,
  type: TriggerAreaType,
  axis: CellAxis,
  view: EditorView,
): void {
  const triggerRect = trigger?.getBoundingClientRect();

  if (!triggerRect || !(triggerRect.width || triggerRect.height)) {
    return;
  }

  const tableResult = findTable();

  if (!tableResult) {
    return;
  }

  const insertButtonAttrs = buildInsertButtonAttrs(type, triggerRect, view.dom, axis);
  view.dispatch(setNodeAttrs(view.state.tr, tableResult.pos, { insertButtonAttrs }));
}

const TriggerArea = ({
  isTopLeft,
  view,
  findTable,
}: {
  isTopLeft: boolean; // is the top half part (in the row controller case) or left half part (in the column controller case)
  view: EditorView;
  findTable: FindTable;
}) => {
  const trigger = h('div', {
    className: ExtensionTablesTheme.TABLE_CONTROLLER_TRIGGER_AREA,
    onMouseEnter: (event) => {
      const axis = getCellAxisByMouseEvent(view, event);

      if (axis) {
        let type: TriggerAreaType | undefined;

        if (axis.row > 0) {
          type = isTopLeft ? TriggerAreaType.ADD_ROW_BEFORE : TriggerAreaType.ADD_ROW_AFTER;
        } else if (axis.col > 0) {
          type = isTopLeft ? TriggerAreaType.ADD_COLUMN_BEFORE : TriggerAreaType.ADD_COLUMN_AFTER;
        }

        if (type) {
          showButton(trigger, findTable, type, axis, view);
        }
      }
    },
  });

  return trigger;
};

const TableInsertButtonTrigger = ({
  view,
  findTable,
}: {
  view: EditorView;
  findTable: FindTable;
}): HTMLElement[] => [
  TriggerArea({ view, findTable, isTopLeft: true }),
  TriggerArea({ view, findTable, isTopLeft: false }),
];

export default TableInsertButtonTrigger;