UNPKG

@atlaskit/editor-plugin-table

Version:

Table plugin for the @atlaskit/editor

223 lines (222 loc) • 11.3 kB
import _defineProperty from "@babel/runtime/helpers/defineProperty"; function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } /** * @jsxRuntime classic * @jsx jsx */ import React, { useEffect } from 'react'; // eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766 import { css, jsx } from '@emotion/react'; import { injectIntl } from 'react-intl'; import { ACTION_SUBJECT } from '@atlaskit/editor-common/analytics/types/enums'; import { ErrorBoundary } from '@atlaskit/editor-common/error-boundary'; import { focusToContextMenuTrigger } from '@atlaskit/editor-common/keymaps'; import { messages } from '@atlaskit/editor-common/messages/table'; import Popup from '@atlaskit/editor-common/Popup'; import ToolbarButton from '@atlaskit/editor-common/ui-menu/ToolbarButton'; import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/useSharedPluginStateWithSelector'; import { findDomRefAtPos } from '@atlaskit/editor-prosemirror/utils'; import { akEditorSmallZIndex } from '@atlaskit/editor-shared-styles/constants'; import ExpandIcon from '@atlaskit/icon/core/chevron-down'; import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled'; import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals'; import { toggleActiveTableMenu, toggleContextualMenu } from '../../pm-plugins/commands'; import { getPluginState } from '../../pm-plugins/plugin-factory'; import { isNativeStickySupported } from '../../pm-plugins/utils/sticky-header'; import { TableCssClassName as ClassName } from '../../types'; import { getTableZIndexes } from '../sticky-header-z-index'; // Ignored via go/ees005 // eslint-disable-next-line import/no-named-as-default import FixedButton from './FixedButton'; import { tableFloatingCellButtonSelectedStyles, tableFloatingCellButtonStyles } from './styles'; import { useCellLayoutVersion } from './useCellLayoutVersion'; var BUTTON_OFFSET = 3; var CONTEXTUAL_MENU_BUTTON_Z_INDEX = 2; var anchorStyles = css({ position: 'absolute', positionVisibility: 'anchors-visible', zIndex: CONTEXTUAL_MENU_BUTTON_Z_INDEX }); var FloatingContextualButtonInner = /*#__PURE__*/React.memo(function (props) { var api = props.api, editorView = props.editorView, isContextualMenuOpen = props.isContextualMenuOpen, mountPoint = props.mountPoint, scrollableElement = props.scrollableElement, stickyHeader = props.stickyHeader, tableWrapper = props.tableWrapper, targetCellPosition = props.targetCellPosition, isCellMenuOpenByKeyboard = props.isCellMenuOpenByKeyboard, formatMessage = props.intl.formatMessage; // : Props & WrappedComponentProps var _useSharedPluginState = useSharedPluginStateWithSelector(api, ['table'], function (states) { var _states$tableState; return { activeTableMenu: (_states$tableState = states.tableState) === null || _states$tableState === void 0 ? void 0 : _states$tableState.activeTableMenu }; }), activeTableMenu = _useSharedPluginState.activeTableMenu; var isCellMenuOpen = expValEquals('platform_editor_table_menu_updates', 'isEnabled', true) ? (activeTableMenu === null || activeTableMenu === void 0 ? void 0 : activeTableMenu.type) === 'cell' : isContextualMenuOpen; var handleClick = function handleClick() { var state = editorView.state, dispatch = editorView.dispatch; if (expValEquals('platform_editor_table_menu_updates', 'isEnabled', true)) { if (!api) { return; } var _getPluginState = getPluginState(state), currentActiveTableMenu = _getPluginState.activeTableMenu; api.core.actions.execute(function (_ref) { var tr = _ref.tr; toggleActiveTableMenu({ type: 'cell', openedBy: 'mouse' }, currentActiveTableMenu, api)({ tr: tr }); return tr; }); return; } // Clicking outside the dropdown handles toggling the menu closed // (otherwise these two toggles combat each other). // In the event a user clicks the chevron button again // That will count as clicking outside the dropdown and // will be toggled appropriately if (!isContextualMenuOpen) { toggleContextualMenu()(state, dispatch); } }; var domAtPos = editorView.domAtPos.bind(editorView); var targetCellRef = findDomRefAtPos(targetCellPosition, domAtPos); // Re-renders this component when the cell may have moved, so both FixedButton and Popup // reposition: Popup recalculates its position whenever it receives new props. var cellLayoutVersion = useCellLayoutVersion(targetCellRef); useEffect(function () { if (isCellMenuOpenByKeyboard && !isCellMenuOpen) { var state = editorView.state, dispatch = editorView.dispatch; // open the menu when the keyboard shortcut is pressed if (expValEquals('platform_editor_table_menu_updates', 'isEnabled', true)) { if (!api) { return; } var _getPluginState2 = getPluginState(state), currentActiveTableMenu = _getPluginState2.activeTableMenu; api.core.actions.execute(function (_ref2) { var tr = _ref2.tr; toggleActiveTableMenu({ type: 'cell', openedBy: 'keyboard' }, currentActiveTableMenu, api)({ tr: tr }); return tr; }); return; } toggleContextualMenu()(state, dispatch); } }, [isCellMenuOpenByKeyboard, isCellMenuOpen, editorView, api]); if (!targetCellRef || !(targetCellRef instanceof HTMLElement)) { return null; } var labelCellOptions = formatMessage(messages.cellOptions); var button = jsx("div", { css: [ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/design-system/consistent-css-prop-usage -- Ignored via go/DSP-18766 tableFloatingCellButtonStyles(), // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/design-system/consistent-css-prop-usage -- Ignored via go/DSP-18766 isCellMenuOpen && tableFloatingCellButtonSelectedStyles()] }, jsx(ToolbarButton // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766 , { className: ClassName.CONTEXTUAL_MENU_BUTTON, selected: isCellMenuOpen, title: labelCellOptions, keymap: focusToContextMenuTrigger, onClick: handleClick, iconBefore: jsx(ExpandIcon, { label: "", color: "currentColor", size: "small" }), "aria-label": labelCellOptions, "aria-expanded": isCellMenuOpen // Stable anchor used by the Post Office changeboarding spotlight // (cc-editor-table-cell-menu-changes) to target the cell options button. , testId: expValEquals('platform_editor_table_menu_updates', 'isEnabled', true) ? 'pm-table-contextual-menu-button' : undefined })); var parentSticky = targetCellRef.parentElement && targetCellRef.parentElement.className.indexOf('sticky') > -1; var parentStickyNative = targetCellRef.parentElement && (tableWrapper === null || tableWrapper === void 0 ? void 0 : tableWrapper.classList.contains(ClassName.TABLE_NODE_WRAPPER_NO_OVERFLOW)); if (parentStickyNative && targetCellRef.nodeName === 'TH' && isNativeStickySupported()) { var _targetCellRef$parent; /* We need to default to checking the anchor style because there may be a conflict with the block controls plugin not using the data attribute value and setting the `anchor-name` style property independently of the data attribute. */ var rowAnchorName = (_targetCellRef$parent = targetCellRef.parentElement) === null || _targetCellRef$parent === void 0 ? void 0 : _targetCellRef$parent.style.getPropertyValue('anchor-name'); var colAnchorName = targetCellRef.style.getPropertyValue('anchor-name'); if (rowAnchorName === '') { var _targetCellRef$parent2; rowAnchorName = (_targetCellRef$parent2 = targetCellRef.parentElement) === null || _targetCellRef$parent2 === void 0 ? void 0 : _targetCellRef$parent2.dataset.nodeAnchor; } if (colAnchorName === '') { colAnchorName = targetCellRef === null || targetCellRef === void 0 ? void 0 : targetCellRef.dataset.nodeAnchor; } if (rowAnchorName && colAnchorName) { return jsx("div", { css: anchorStyles, style: _objectSpread({ // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop top: "calc(".concat(BUTTON_OFFSET, "px + anchor(").concat(rowAnchorName, " top))"), right: "calc(".concat(BUTTON_OFFSET, "px + anchor(").concat(colAnchorName, " right))"), // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop positionAnchor: colAnchorName }, isExperimentEnabled('platform_editor_table_cell_menu_btn_zindex') && { // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766 zIndex: getTableZIndexes().headerButton }) // need to do this because CSSProperties doesn't have positionAnchor property even though it's a valid CSS property , "data-testid": "table-cell-options-anchor-wrapper" }, button); } } if (stickyHeader && parentSticky && tableWrapper) { return jsx(FixedButton, { cellLayoutVersion: cellLayoutVersion, offset: BUTTON_OFFSET, stickyHeader: stickyHeader, tableWrapper: tableWrapper, targetCellPosition: targetCellPosition, targetCellRef: targetCellRef, mountTo: tableWrapper, isContextualMenuOpen: isCellMenuOpen }, button); } return jsx(Popup, { alignX: "right", alignY: "start", target: targetCellRef, mountTo: tableWrapper || mountPoint, boundariesElement: targetCellRef, scrollableElement: scrollableElement // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) , offset: [BUTTON_OFFSET, -BUTTON_OFFSET], forcePlacement: true, allowOutOfBounds: true, zIndex: akEditorSmallZIndex }, button); }); var FloatingContextualButton = injectIntl(FloatingContextualButtonInner); export default function (props) { return jsx(ErrorBoundary, { component: ACTION_SUBJECT.FLOATING_CONTEXTUAL_BUTTON, dispatchAnalyticsEvent: props.dispatchAnalyticsEvent, fallbackComponent: null }, jsx(FloatingContextualButton // Ignored via go/ees005 // eslint-disable-next-line react/jsx-props-no-spreading , props)); }