@atlaskit/editor-plugin-table
Version:
Table plugin for the @atlaskit/editor
221 lines (216 loc) • 9.42 kB
JavaScript
import _extends from "@babel/runtime/helpers/extends";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import React from 'react';
import { injectIntl } from 'react-intl';
import { ACTION, ACTION_SUBJECT, CONTENT_COMPONENT, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
import { Popup } from '@atlaskit/editor-common/ui';
import { closestElement } from '@atlaskit/editor-common/utils';
import { findDomRefAtPos } from '@atlaskit/editor-prosemirror/utils';
import { akEditorTableCellOnStickyHeaderZIndex } from '@atlaskit/editor-shared-styles';
import { CellSelection } from '@atlaskit/editor-tables/cell-selection';
import { TableMap } from '@atlaskit/editor-tables/table-map';
import { findTable } from '@atlaskit/editor-tables/utils';
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
import { insertColumnWithAnalytics, insertRowWithAnalytics } from '../../pm-plugins/commands/commands-with-analytics';
import { checkIfNumberColumnEnabled } from '../../pm-plugins/utils/nodes';
import { TableCssClassName as ClassName } from '../../types';
import getPopupOptions from './getPopupOptions';
import InsertButton, { DragAndDropInsertButton } from './InsertButton';
// Ignored via go/ees005
// eslint-disable-next-line @repo/internal/react/no-class-components, @typescript-eslint/no-explicit-any
export class FloatingInsertButton extends React.Component {
constructor(props) {
super(props);
this.insertColumn = this.insertColumn.bind(this);
this.insertRow = this.insertRow.bind(this);
}
render() {
const {
tableNode,
editorView,
insertColumnButtonIndex,
insertRowButtonIndex,
tableRef,
mountPoint,
boundariesElement,
isHeaderColumnEnabled,
isHeaderRowEnabled,
isDragAndDropEnabled,
dispatchAnalyticsEvent,
isChromelessEditor
} = this.props;
// TODO: ED-26961 - temporarily disable insert button for first column and row https://atlassian.slack.com/archives/C05U8HRQM50/p1698363744682219?thread_ts=1698209039.104909&cid=C05U8HRQM50
if (isDragAndDropEnabled && (insertColumnButtonIndex === 0 || insertRowButtonIndex === 0)) {
return null;
}
const type = typeof insertColumnButtonIndex !== 'undefined' ? 'column' : typeof insertRowButtonIndex !== 'undefined' ? 'row' : null;
if (!tableNode || !tableRef || !type) {
return null;
}
// We can’t display the insert button for row|colum index 0
// when the header row|colum is enabled, this feature will be change on the future
if (type === 'column' && isHeaderColumnEnabled && insertColumnButtonIndex === 0 || type === 'row' && isHeaderRowEnabled && insertRowButtonIndex === 0) {
return null;
}
const {
state: {
tr
}
} = editorView;
if (tr.selection instanceof CellSelection && (tr.selection.isColSelection() || tr.selection.isRowSelection())) {
return null;
}
const tablePos = findTable(tr.selection);
if (!tablePos) {
return null;
}
// the tableNode props is not always latest (when you type some text in a cell, it's not updated yet)
// we need to get the latest one by calling findTable(tr.selection)
const cellPosition = this.getCellPosition(type, tablePos === null || tablePos === void 0 ? void 0 : tablePos.node);
if (!cellPosition) {
return null;
}
const domAtPos = editorView.domAtPos.bind(editorView);
const pos = cellPosition + tablePos.start + 1;
let target;
try {
target = findDomRefAtPos(pos, domAtPos);
} catch (error) {
// eslint-disable-next-line no-console
console.warn(error);
if (dispatchAnalyticsEvent) {
const payload = {
action: ACTION.ERRORED,
actionSubject: ACTION_SUBJECT.CONTENT_COMPONENT,
eventType: EVENT_TYPE.OPERATIONAL,
attributes: {
component: CONTENT_COMPONENT.FLOATING_INSERT_BUTTON,
selection: editorView.state.selection.toJSON(),
position: pos,
docSize: editorView.state.doc.nodeSize,
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
error: error === null || error === void 0 ? void 0 : error.toString()
}
};
dispatchAnalyticsEvent(payload);
}
}
if (!target || !(target instanceof HTMLElement)) {
return null;
}
const targetCellRef = type === 'row' ? closestElement(target, 'tr') : closestElement(target, 'td, th');
if (!targetCellRef) {
return null;
}
const tableContainerWrapper = closestElement(targetCellRef, `.${ClassName.TABLE_CONTAINER}`);
const tableWrapper = closestElement(targetCellRef, `.${ClassName.TABLE_NODE_WRAPPER}`);
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const index = type === 'column' ? insertColumnButtonIndex : insertRowButtonIndex;
const hasNumberedColumns = checkIfNumberColumnEnabled(editorView.state.selection);
// Fixed the 'add column button' not visible issue when sticky header is enabled
// By setting the Popup z-index higher than the sticky header z-index ( common-styles.ts tr.sticky)
// Only when inserting a column, otherwise set to undefined
// Need to set z-index in the Popup, set z-index in the <InsertButton /> will not work
const zIndex = expValEquals('platform_editor_table_sticky_header_improvements', 'cohort', 'test_with_overflow') || type === 'column' ? akEditorTableCellOnStickyHeaderZIndex : undefined;
return /*#__PURE__*/React.createElement(Popup, _extends({
target: targetCellRef,
mountTo: tableContainerWrapper || mountPoint,
boundariesElement: tableContainerWrapper || boundariesElement
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
,
scrollableElement: tableWrapper,
forcePlacement: true,
allowOutOfBounds: true
// Ignored via go/ees005
// eslint-disable-next-line react/jsx-props-no-spreading
}, getPopupOptions(type, index, hasNumberedColumns, !!isDragAndDropEnabled, tableContainerWrapper), {
zIndex: zIndex
}), isDragAndDropEnabled ? /*#__PURE__*/React.createElement(DragAndDropInsertButton, {
type: type,
tableRef: tableRef,
onMouseDown: type === 'column' ? this.insertColumn : this.insertRow,
hasStickyHeaders: this.props.hasStickyHeaders || false,
isChromelessEditor: isChromelessEditor
}) : /*#__PURE__*/React.createElement(InsertButton, {
type: type,
tableRef: tableRef,
onMouseDown: type === 'column' ? this.insertColumn : this.insertRow,
hasStickyHeaders: this.props.hasStickyHeaders || false
}));
}
getCellPosition(type, tableNode) {
const {
insertColumnButtonIndex,
insertRowButtonIndex
} = this.props;
const tableMap = TableMap.get(tableNode);
if (type === 'column') {
// This condition is to make typescript happy.
// Previously insertColumnButtonIndex - 1 would produce NaN and return null anyway.
if (insertColumnButtonIndex === undefined) {
return null;
}
const columnIndex = insertColumnButtonIndex === 0 ? 0 : insertColumnButtonIndex - 1;
if (columnIndex > tableMap.width - 1) {
return null;
}
return tableMap.positionAt(0, columnIndex, tableNode);
} else {
// This condition is to make typescript happy.
// Previously insertRowButtonIndex - 1 would produce NaN and return null anyway.
if (insertRowButtonIndex === undefined) {
return null;
}
const rowIndex = insertRowButtonIndex === 0 ? 0 : insertRowButtonIndex - 1;
if (rowIndex > tableMap.height - 1) {
return null;
}
return tableMap.positionAt(rowIndex, 0, tableNode);
}
}
insertRow(event) {
const {
editorView,
insertRowButtonIndex,
editorAnalyticsAPI
} = this.props;
if (typeof insertRowButtonIndex !== 'undefined') {
event.preventDefault();
const {
state,
dispatch
} = editorView;
insertRowWithAnalytics(editorAnalyticsAPI)(INPUT_METHOD.BUTTON, {
index: insertRowButtonIndex,
moveCursorToInsertedRow: true
})(state, dispatch);
}
}
insertColumn(event) {
const {
editorView,
insertColumnButtonIndex,
editorAnalyticsAPI,
getEditorFeatureFlags,
isTableScalingEnabled,
isCommentEditor
} = this.props;
if (typeof insertColumnButtonIndex !== 'undefined') {
event.preventDefault();
const {
tableWithFixedColumnWidthsOption = false
} = getEditorFeatureFlags ? getEditorFeatureFlags() : {};
const shouldUseIncreasedScalingPercent = isTableScalingEnabled && (tableWithFixedColumnWidthsOption || isCommentEditor);
const {
state,
dispatch
} = editorView;
insertColumnWithAnalytics(this.props.api, editorAnalyticsAPI, isTableScalingEnabled, tableWithFixedColumnWidthsOption, shouldUseIncreasedScalingPercent, isCommentEditor)(INPUT_METHOD.BUTTON, insertColumnButtonIndex)(state, dispatch, editorView);
}
}
}
_defineProperty(FloatingInsertButton, "displayName", 'FloatingInsertButton');
export default injectIntl(FloatingInsertButton);