UNPKG

@atlaskit/editor-plugin-insert-block

Version:

Insert block plugin for @atlaskit/editor-core

849 lines (833 loc) 39.3 kB
import _defineProperty from "@babel/runtime/helpers/defineProperty"; import _extends from "@babel/runtime/helpers/extends"; /* eslint-disable @atlaskit/ui-styling-standard/no-imported-style-values */ /** * @jsxRuntime classic * @jsx jsx */ import React from 'react'; /* eslint-disable @atlaskit/ui-styling-standard/use-compiled, @typescript-eslint/consistent-type-imports */ import { css, jsx } from '@emotion/react'; import { injectIntl } from 'react-intl'; import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics'; import { toolbarInsertBlockMessages as messages } from '@atlaskit/editor-common/messages'; import { buttonGroupStyle, separatorStyles, wrapperStyle } from '@atlaskit/editor-common/styles'; import { Popup, TableSelectorPopup } from '@atlaskit/editor-common/ui'; import { ToolbarButton } from '@atlaskit/editor-common/ui-menu'; import { OutsideClickTargetRefContext, withReactEditorViewOuterListeners as withOuterListeners } from '@atlaskit/editor-common/ui-react'; import { akEditorMenuZIndex } from '@atlaskit/editor-shared-styles'; import { EmojiPicker as AkEmojiPicker } from '@atlaskit/emoji/picker'; // Ignored via go/ees005 // eslint-disable-next-line import/no-namespace import { fg } from '@atlaskit/platform-feature-flags'; import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments'; import { LINK_BUTTON_KEY } from '../toolbar-components/hooks/filterDropdownItems'; import { BlockInsertMenu } from './block-insert-menu'; import { createItems } from './create-items'; // Ignored via go/ees005 // eslint-disable-next-line import/no-named-as-default /** * Checks if an element is detached (i.e. not in the current document) */ // eslint-disable-next-line @atlaskit/platform/no-direct-document-usage -- Existing DOM check surfaced by this mechanical PR. const isDetachedElement = el => !document.body.contains(el); const TABLE_SELECTOR_STRING = 'table selector'; // TODO: ED-26959 - Jenga team will create a component for a split button using this css const getHoverStyles = selector => `&:hover ${selector} { background: ${"var(--ds-background-neutral-subtle-hovered, #0515240F)"}; &:hover { background: ${"var(--ds-background-neutral-hovered, #0B120E24)"}; } }`; const EmojiPicker = props => { const setOutsideClickTargetRef = React.useContext(OutsideClickTargetRefContext); // Ignored via go/ees005 // eslint-disable-next-line react/jsx-props-no-spreading return jsx(AkEmojiPicker, _extends({ onPickerRef: setOutsideClickTargetRef }, props)); }; const EmojiPickerWithListeners = withOuterListeners(EmojiPicker); export const tableButtonWrapper = ({ isTableSelectorOpen, isButtonDisabled }) => // eslint-disable-next-line @atlaskit/design-system/no-css-tagged-template-expression -- Needs manual remediation due to mixins css` display: flex; ${!isTableSelectorOpen && !isButtonDisabled && getHoverStyles('.table-selector-toolbar-btn')} ${!isTableSelectorOpen && !isButtonDisabled && getHoverStyles('.table-toolbar-btn')} .table-toolbar-btn { border-top-right-radius: ${"var(--ds-radius-large, 0px)"}; border-bottom-right-radius: ${"var(--ds-radius-large, 0px)"}; margin-right: ${"var(--ds-space-025, 2px)"}; padding: ${"var(--ds-space-0, 0px)"}; & > span { min-width: 16px; margin: ${"var(--ds-space-0, 0px)"}; } } .table-selector-toolbar-btn { padding: ${"var(--ds-space-0, 0px)"}; & > span { margin: ${"var(--ds-space-0, 0px)"}; width: 16px !important; display: flex; justify-content: center; } border-top-left-radius: ${"var(--ds-radius-large, 0px)"} !important; border-bottom-left-radius: ${"var(--ds-radius-large, 0px)"} !important; } `; // eslint-disable-next-line @repo/internal/react/no-class-components export class ToolbarInsertBlock extends React.PureComponent { constructor(..._args) { super(..._args); _defineProperty(this, "tableButtonRef", /*#__PURE__*/React.createRef()); _defineProperty(this, "tableSelectorButtonRef", /*#__PURE__*/React.createRef()); _defineProperty(this, "state", { isPlusMenuOpen: false, emojiPickerOpen: false, isOpenedByKeyboard: false, buttons: [], dropdownItems: [], isTableSelectorOpen: false, isTableSelectorOpenedByKeyboard: false }); _defineProperty(this, "onOpenChange", attrs => { const state = { isPlusMenuOpen: attrs.isPlusMenuOpen, emojiPickerOpen: this.state.emojiPickerOpen }; if (this.state.emojiPickerOpen && !attrs.open) { state.emojiPickerOpen = false; } this.setState(state, () => { const { dispatchAnalyticsEvent } = this.props; if (!dispatchAnalyticsEvent) { return; } const { isPlusMenuOpen } = this.state; if (isPlusMenuOpen) { return dispatchAnalyticsEvent({ action: ACTION.OPENED, actionSubject: ACTION_SUBJECT.PLUS_MENU, eventType: EVENT_TYPE.UI }); } return dispatchAnalyticsEvent({ action: ACTION.CLOSED, actionSubject: ACTION_SUBJECT.PLUS_MENU, eventType: EVENT_TYPE.UI }); }); }); _defineProperty(this, "togglePlusMenuVisibility", event => { const { isPlusMenuOpen } = this.state; const { pluginInjectionApi } = this.props; if (pluginInjectionApi && fg('platform_editor_ease_of_use_metrics')) { pluginInjectionApi.core.actions.execute(({ tr }) => { if (isPlusMenuOpen) { var _pluginInjectionApi$m; (_pluginInjectionApi$m = pluginInjectionApi.metrics) === null || _pluginInjectionApi$m === void 0 ? void 0 : _pluginInjectionApi$m.commands.startActiveSessionTimer()({ tr }); } else { var _pluginInjectionApi$m2; (_pluginInjectionApi$m2 = pluginInjectionApi.metrics) === null || _pluginInjectionApi$m2 === void 0 ? void 0 : _pluginInjectionApi$m2.commands.handleIntentToStartEdit({ shouldStartTimer: false, shouldPersistActiveSession: true })({ tr }); } return tr; }); } this.onOpenChange({ isPlusMenuOpen: !isPlusMenuOpen }); if ((event === null || event === void 0 ? void 0 : event.key) === 'Escape') { var _ref, _ref$deref; (_ref = this.plusButtonRef || this.dropdownButtonRef) === null || _ref === void 0 ? void 0 : (_ref$deref = _ref.deref()) === null || _ref$deref === void 0 ? void 0 : _ref$deref.focus(); } }); _defineProperty(this, "toggleEmojiPicker", (inputMethod = INPUT_METHOD.TOOLBAR) => { this.setState(prevState => ({ emojiPickerOpen: !prevState.emojiPickerOpen }), () => { if (this.state.emojiPickerOpen) { const { dispatchAnalyticsEvent } = this.props; if (dispatchAnalyticsEvent) { dispatchAnalyticsEvent({ action: ACTION.OPENED, actionSubject: ACTION_SUBJECT.PICKER, actionSubjectId: ACTION_SUBJECT_ID.PICKER_EMOJI, attributes: { inputMethod }, eventType: EVENT_TYPE.UI }); } } }); }); _defineProperty(this, "handleEmojiPressEscape", () => { var _this$emojiButtonRef, _this$emojiButtonRef$; this.toggleEmojiPicker(INPUT_METHOD.KEYBOARD); (_this$emojiButtonRef = this.emojiButtonRef) === null || _this$emojiButtonRef === void 0 ? void 0 : (_this$emojiButtonRef$ = _this$emojiButtonRef.deref()) === null || _this$emojiButtonRef$ === void 0 ? void 0 : _this$emojiButtonRef$.focus(); }); _defineProperty(this, "handleEmojiClickOutside", e => { // Ignore click events for detached elements. // Workaround for FS-1322 - where two onClicks fire - one when the upload button is // still in the document, and one once it's detached. Does not always occur, and // may be a side effect of a react render optimisation // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting if (e.target && !isDetachedElement(e.target)) { this.toggleEmojiPicker(INPUT_METHOD.TOOLBAR); } }); _defineProperty(this, "getToolbarButtonTestId", btn => { const buttonTestIds = { media: 'media-attachment-toolbar-button' }; return buttonTestIds[btn.value.name] || String(btn.content); }); _defineProperty(this, "handleToolbarRef", buttonName => ref => { if (!ref) { return; } switch (buttonName) { case 'emoji': this.emojiButtonRef = new WeakRef(ref); break; case 'media': this.mediaButtonRef = new WeakRef(ref); break; } }); _defineProperty(this, "handlePlusButtonRef", ref => { if (ref) { this.plusButtonRef = new WeakRef(ref); } }); _defineProperty(this, "handleDropDownButtonRef", ref => { if (ref) { this.dropdownButtonRef = new WeakRef(ref); } }); _defineProperty(this, "toggleTableSelector", (_inputMethod = INPUT_METHOD.TOOLBAR) => { this.setState(prevState => ({ isTableSelectorOpen: !prevState.isTableSelectorOpen })); }); _defineProperty(this, "handleSelectedTableSize", (rowsCount, colsCount) => { this.insertTableWithSize(INPUT_METHOD.TOOLBAR, rowsCount, colsCount)(); this.toggleTableSelector(); }); _defineProperty(this, "handleTableSelectorPressEscape", () => { var _this$tableSelectorBu; this.toggleTableSelector(INPUT_METHOD.KEYBOARD); (_this$tableSelectorBu = this.tableSelectorButtonRef.current) === null || _this$tableSelectorBu === void 0 ? void 0 : _this$tableSelectorBu.focus(); }); _defineProperty(this, "handleTableSelectorClickOutside", e => { // Ignore click events for detached elements. // Ignored via go/ees005 // eslint-disable-next-line @atlaskit/editor/no-as-casting if (e.target && !isDetachedElement(e.target)) { this.toggleTableSelector(INPUT_METHOD.TOOLBAR); } }); _defineProperty(this, "handleClick", () => { this.togglePlusMenuVisibility(); }); _defineProperty(this, "handleOpenByKeyboard", event => { if (event.key === 'Enter' || event.key === ' ') { this.setState({ ...this.state, isOpenedByKeyboard: true }); event.preventDefault(); this.togglePlusMenuVisibility(); } }); _defineProperty(this, "handleTableSelectorOpenByKeyboard", event => { if (event.key === 'Enter' || event.key === ' ') { this.setState({ isTableSelectorOpenedByKeyboard: true }); } }); _defineProperty(this, "toggleLinkPanel", inputMethod => { var _pluginInjectionApi$c, _pluginInjectionApi$c2, _pluginInjectionApi$h; const { pluginInjectionApi } = this.props; return (_pluginInjectionApi$c = pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$c2 = pluginInjectionApi.core) === null || _pluginInjectionApi$c2 === void 0 ? void 0 : _pluginInjectionApi$c2.actions.execute(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$h = pluginInjectionApi.hyperlink) === null || _pluginInjectionApi$h === void 0 ? void 0 : _pluginInjectionApi$h.commands.showLinkToolbar(inputMethod))) !== null && _pluginInjectionApi$c !== void 0 ? _pluginInjectionApi$c : false; }); _defineProperty(this, "insertMention", inputMethod => { var _pluginInjectionApi$m3, _pluginInjectionApi$m4, _pluginInjectionApi$m5; const { editorView, pluginInjectionApi } = this.props; if (!editorView) { return true; } const pluginState = pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$m3 = pluginInjectionApi.mention) === null || _pluginInjectionApi$m3 === void 0 ? void 0 : _pluginInjectionApi$m3.sharedState.currentState(); if (pluginState && pluginState.canInsertMention === false) { return false; } return Boolean(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$m4 = pluginInjectionApi.mention) === null || _pluginInjectionApi$m4 === void 0 ? void 0 : (_pluginInjectionApi$m5 = _pluginInjectionApi$m4.actions) === null || _pluginInjectionApi$m5 === void 0 ? void 0 : _pluginInjectionApi$m5.openTypeAhead(inputMethod)); }); _defineProperty(this, "insertTable", inputMethod => { const { pluginInjectionApi, editorView } = this.props; const { state, dispatch } = editorView; // workaround to solve race condition where cursor is not placed correctly inside table queueMicrotask(() => { var _pluginInjectionApi$t, _pluginInjectionApi$t2, _pluginInjectionApi$t3; pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$t = pluginInjectionApi.table) === null || _pluginInjectionApi$t === void 0 ? void 0 : (_pluginInjectionApi$t2 = (_pluginInjectionApi$t3 = _pluginInjectionApi$t.actions).insertTable) === null || _pluginInjectionApi$t2 === void 0 ? void 0 : _pluginInjectionApi$t2.call(_pluginInjectionApi$t3, { action: ACTION.INSERTED, actionSubject: ACTION_SUBJECT.DOCUMENT, actionSubjectId: ACTION_SUBJECT_ID.TABLE, attributes: { inputMethod }, eventType: EVENT_TYPE.TRACK })(state, dispatch); }); }); _defineProperty(this, "insertTableWithSize", (inputMethod, rowsCount, colsCount) => () => { const { pluginInjectionApi } = this.props; // workaround to solve race condition where cursor is not placed correctly inside table queueMicrotask(() => { var _pluginInjectionApi$c3, _pluginInjectionApi$t4; pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$c3 = pluginInjectionApi.core) === null || _pluginInjectionApi$c3 === void 0 ? void 0 : _pluginInjectionApi$c3.actions.execute(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$t4 = pluginInjectionApi.table) === null || _pluginInjectionApi$t4 === void 0 ? void 0 : _pluginInjectionApi$t4.commands.insertTableWithSize(rowsCount, colsCount, INPUT_METHOD.PICKER)); }); }); _defineProperty(this, "createDate", inputMethod => { var _pluginInjectionApi$c4, _pluginInjectionApi$d, _pluginInjectionApi$d2; const { pluginInjectionApi } = this.props; pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$c4 = pluginInjectionApi.core) === null || _pluginInjectionApi$c4 === void 0 ? void 0 : _pluginInjectionApi$c4.actions.execute(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$d = pluginInjectionApi.date) === null || _pluginInjectionApi$d === void 0 ? void 0 : (_pluginInjectionApi$d2 = _pluginInjectionApi$d.commands) === null || _pluginInjectionApi$d2 === void 0 ? void 0 : _pluginInjectionApi$d2.insertDate({ inputMethod })); return true; }); _defineProperty(this, "createPlaceholderText", () => { var _pluginInjectionApi$p; const { editorView, pluginInjectionApi } = this.props; pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$p = pluginInjectionApi.placeholderText) === null || _pluginInjectionApi$p === void 0 ? void 0 : _pluginInjectionApi$p.actions.showPlaceholderFloatingToolbar(editorView.state, editorView.dispatch); return true; }); _defineProperty(this, "insertLayoutColumns", inputMethod => { var _pluginInjectionApi$l; const { editorView, pluginInjectionApi } = this.props; pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$l = pluginInjectionApi.layout) === null || _pluginInjectionApi$l === void 0 ? void 0 : _pluginInjectionApi$l.actions.insertLayoutColumns(inputMethod)(editorView.state, editorView.dispatch); return true; }); _defineProperty(this, "createStatus", inputMethod => { var _pluginInjectionApi$c5, _pluginInjectionApi$s, _pluginInjectionApi$s2; const { pluginInjectionApi } = this.props; return Boolean(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$c5 = pluginInjectionApi.core) === null || _pluginInjectionApi$c5 === void 0 ? void 0 : _pluginInjectionApi$c5.actions.execute(pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$s = pluginInjectionApi.status) === null || _pluginInjectionApi$s === void 0 ? void 0 : (_pluginInjectionApi$s2 = _pluginInjectionApi$s.commands) === null || _pluginInjectionApi$s2 === void 0 ? void 0 : _pluginInjectionApi$s2.insertStatus(inputMethod))); }); _defineProperty(this, "openMediaPicker", inputMethod => { const { onShowMediaPicker, dispatchAnalyticsEvent } = this.props; if (onShowMediaPicker) { var _this$mediaButtonRef, _this$props$popupsMou, _this$props$popupsMou2; const ref = (_this$mediaButtonRef = this.mediaButtonRef) === null || _this$mediaButtonRef === void 0 ? void 0 : _this$mediaButtonRef.deref(); const args = ref ? { ref, mountPoint: (_this$props$popupsMou = this.props.popupsMountPoint) !== null && _this$props$popupsMou !== void 0 ? _this$props$popupsMou : ref } : undefined; const argsWithUpdatedMountPoint = ref !== null && ref !== void 0 && ref.parentElement ? { ref, mountPoint: (_this$props$popupsMou2 = this.props.popupsMountPoint) !== null && _this$props$popupsMou2 !== void 0 ? _this$props$popupsMou2 : ref.parentElement } : undefined; onShowMediaPicker(fg('platform_editor_nov_a11y_fixes') ? argsWithUpdatedMountPoint : args); if (dispatchAnalyticsEvent) { dispatchAnalyticsEvent({ action: ACTION.OPENED, actionSubject: ACTION_SUBJECT.PICKER, actionSubjectId: ACTION_SUBJECT_ID.PICKER_MEDIA, attributes: { inputMethod }, eventType: EVENT_TYPE.UI }); } } return true; }); _defineProperty(this, "insertTaskDecision", (name, inputMethod) => () => { var _pluginInjectionApi$t5, _pluginInjectionApi$t6; const { editorView: { state, dispatch }, pluginInjectionApi } = this.props; const listType = name === 'action' ? 'taskList' : 'decisionList'; return (_pluginInjectionApi$t5 = pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$t6 = pluginInjectionApi.taskDecision) === null || _pluginInjectionApi$t6 === void 0 ? void 0 : _pluginInjectionApi$t6.actions.insertTaskDecision(listType, inputMethod)(state, dispatch)) !== null && _pluginInjectionApi$t5 !== void 0 ? _pluginInjectionApi$t5 : false; }); _defineProperty(this, "insertHorizontalRule", inputMethod => { var _pluginInjectionApi$r, _pluginInjectionApi$r2; const { editorView: { state, dispatch }, pluginInjectionApi } = this.props; return (_pluginInjectionApi$r = pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$r2 = pluginInjectionApi.rule) === null || _pluginInjectionApi$r2 === void 0 ? void 0 : _pluginInjectionApi$r2.actions.insertHorizontalRule(inputMethod)(state, dispatch)) !== null && _pluginInjectionApi$r !== void 0 ? _pluginInjectionApi$r : false; }); _defineProperty(this, "insertExpand", () => { var _pluginInjectionApi$e, _pluginInjectionApi$e2; const { editorView: { state, dispatch }, pluginInjectionApi } = this.props; return (_pluginInjectionApi$e = pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$e2 = pluginInjectionApi.expand) === null || _pluginInjectionApi$e2 === void 0 ? void 0 : _pluginInjectionApi$e2.actions.insertExpand(state, dispatch)) !== null && _pluginInjectionApi$e !== void 0 ? _pluginInjectionApi$e : false; }); _defineProperty(this, "insertBlockType", itemName => () => { const { editorView, onInsertBlockType } = this.props; const { state, dispatch } = editorView; // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion onInsertBlockType(itemName)(state, dispatch); return true; }); _defineProperty(this, "handleSelectedEmoji", emojiId => { var _pluginInjectionApi$c6, _pluginInjectionApi$e3; const { pluginInjectionApi } = this.props; this.props.editorView.focus(); pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$c6 = pluginInjectionApi.core) === null || _pluginInjectionApi$c6 === void 0 ? void 0 : _pluginInjectionApi$c6.actions.execute((_pluginInjectionApi$e3 = pluginInjectionApi.emoji) === null || _pluginInjectionApi$e3 === void 0 ? void 0 : _pluginInjectionApi$e3.commands.insertEmoji(emojiId, INPUT_METHOD.PICKER)); this.toggleEmojiPicker(); return true; }); _defineProperty(this, "onItemActivated", ({ item, inputMethod }) => { const { editorView, handleImageUpload, expandEnabled } = this.props; // need to do this before inserting nodes so scrollIntoView works properly if (!editorView.hasFocus()) { editorView.focus(); } switch (item.value.name) { case LINK_BUTTON_KEY: this.toggleLinkPanel(inputMethod); break; case 'table': this.insertTable(inputMethod); break; case 'table selector': this.toggleTableSelector(inputMethod); break; case 'image upload': if (handleImageUpload) { const { state, dispatch } = editorView; handleImageUpload()(state, dispatch); } break; case 'media': this.openMediaPicker(inputMethod); break; case 'mention': this.insertMention(inputMethod); break; case 'emoji': this.toggleEmojiPicker(inputMethod); break; case 'codeblock': case 'blockquote': case 'panel': this.insertBlockType(item.value.name)(); break; case 'action': case 'decision': this.insertTaskDecision(item.value.name, inputMethod)(); break; case 'horizontalrule': this.insertHorizontalRule(inputMethod); break; case 'date': this.createDate(inputMethod); break; case 'placeholder text': this.createPlaceholderText(); break; case 'layout': this.insertLayoutColumns(inputMethod); break; case 'status': this.createStatus(inputMethod); break; // https://product-fabric.atlassian.net/browse/ED-8053 // @ts-ignore: OK to fallthrough to default case 'expand': if (expandEnabled) { this.insertExpand(); break; } // eslint-disable-next-line no-fallthrough default: if (item && item.onClick) { item.onClick(); break; } } this.setState({ isPlusMenuOpen: false }); }); _defineProperty(this, "insertToolbarMenuItem", btn => this.onItemActivated({ item: btn, inputMethod: INPUT_METHOD.TOOLBAR })); _defineProperty(this, "insertInsertMenuItem", ({ item }) => this.onItemActivated({ item, inputMethod: INPUT_METHOD.INSERT_MENU })); } static getDerivedStateFromProps(props, state) { var _props$pluginInjectio, _props$pluginInjectio2, _props$pluginInjectio3; const [buttons, dropdownItems] = createItems({ isTypeAheadAllowed: props.isTypeAheadAllowed, tableSupported: props.tableSupported, tableSelectorSupported: props.tableSelectorSupported, mediaUploadsEnabled: props.mediaUploadsEnabled, mediaSupported: props.mediaSupported, isEditorOffline: props.isEditorOffline, imageUploadSupported: props.imageUploadSupported, imageUploadEnabled: props.imageUploadEnabled, mentionsSupported: props.mentionsSupported, mentionsDisabled: props.mentionsDisabled, actionSupported: props.actionSupported, decisionSupported: props.decisionSupported, linkSupported: props.linkSupported, linkDisabled: props.linkDisabled, emojiDisabled: props.emojiDisabled, hasEmojiPlugin: !!((_props$pluginInjectio = props.pluginInjectionApi) !== null && _props$pluginInjectio !== void 0 && _props$pluginInjectio.emoji), hasMentionsPlugin: !!((_props$pluginInjectio2 = props.pluginInjectionApi) !== null && _props$pluginInjectio2 !== void 0 && _props$pluginInjectio2.mention), hasMediaPlugin: !!((_props$pluginInjectio3 = props.pluginInjectionApi) !== null && _props$pluginInjectio3 !== void 0 && _props$pluginInjectio3.media), nativeStatusSupported: props.nativeStatusSupported, dateEnabled: props.dateEnabled, placeholderTextEnabled: props.placeholderTextEnabled, horizontalRuleEnabled: props.horizontalRuleEnabled, layoutSectionEnabled: props.layoutSectionEnabled, expandEnabled: props.expandEnabled, showElementBrowserLink: props.showElementBrowserLink, emojiProvider: props.emojiProvider, availableWrapperBlockTypes: props.availableWrapperBlockTypes, insertMenuItems: props.insertMenuItems, schema: props.editorView.state.schema, numberOfButtons: props.buttons, formatMessage: props.intl.formatMessage }); return { ...state, buttons, dropdownItems }; } componentDidUpdate(prevProps) { // If number of visible buttons changed, close emoji picker and table selector if (prevProps.buttons !== this.props.buttons) { this.setState({ emojiPickerOpen: false, isTableSelectorOpen: false }); } if (this.state.isOpenedByKeyboard) { var _this$dropdownButtonR, _this$dropdownButtonR2; const downArrowEvent = new KeyboardEvent('keydown', { bubbles: true, key: 'ArrowDown' }); (_this$dropdownButtonR = this.dropdownButtonRef) === null || _this$dropdownButtonR === void 0 ? void 0 : (_this$dropdownButtonR2 = _this$dropdownButtonR.deref()) === null || _this$dropdownButtonR2 === void 0 ? void 0 : _this$dropdownButtonR2.dispatchEvent(downArrowEvent); this.setState({ ...this.state, isOpenedByKeyboard: false }); } if (this.state.isTableSelectorOpen) { this.setState({ isTableSelectorOpenedByKeyboard: false }); } if (this.props.showElementBrowser !== prevProps.showElementBrowser) { this.handleClick(); this.setState({ ...this.state, isPlusMenuOpen: this.props.showElementBrowser }); } } renderPopup() { const { emojiPickerOpen } = this.state; const { popupsMountPoint, popupsBoundariesElement, popupsScrollableElement, emojiProvider } = this.props; const dropdownEmoji = this.state.dropdownItems.some(({ value: { name } }) => name === 'emoji'); const dropDownButtonRef = this.plusButtonRef; const ref = dropdownEmoji ? dropDownButtonRef : this.emojiButtonRef; if (!emojiPickerOpen || !ref || !emojiProvider) { return null; } const onUnmount = () => { requestAnimationFrame(() => { var _this$props$pluginInj, _this$props$pluginInj2, _this$props$pluginInj3; return (_this$props$pluginInj = this.props.pluginInjectionApi) === null || _this$props$pluginInj === void 0 ? void 0 : (_this$props$pluginInj2 = _this$props$pluginInj.core) === null || _this$props$pluginInj2 === void 0 ? void 0 : (_this$props$pluginInj3 = _this$props$pluginInj2.actions) === null || _this$props$pluginInj3 === void 0 ? void 0 : _this$props$pluginInj3.focus(); }); }; return jsx(Popup, { target: ref.deref(), fitHeight: 350, fitWidth: 350 // eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed) , offset: [0, 3], mountTo: popupsMountPoint, boundariesElement: popupsBoundariesElement, scrollableElement: popupsScrollableElement, onUnmount: onUnmount, focusTrap: true, zIndex: akEditorMenuZIndex }, jsx(EmojiPickerWithListeners, { emojiProvider: emojiProvider, onSelection: this.handleSelectedEmoji, handleClickOutside: this.handleEmojiClickOutside, handleEscapeKeydown: this.handleEmojiPressEscape })); } renderTableSelectorPopup() { var _this$tableButtonRef$; const { isTableSelectorOpen, isTableSelectorOpenedByKeyboard } = this.state; const { popupsMountPoint, popupsBoundariesElement, popupsScrollableElement, pluginInjectionApi } = this.props; const ref = (_this$tableButtonRef$ = this.tableButtonRef.current) !== null && _this$tableButtonRef$ !== void 0 ? _this$tableButtonRef$ : undefined; if (!isTableSelectorOpen) { return null; } // We use focusTrap in the Popup. When we insert a table via popup, // the popup closes, focusTrap gets destroyed and the popup detaches. // The focus gets set to the body element. onUnmount method sets focus on the editor right before the // Popup will be unmounted to ensure that the new table has a selection with a blinking cursor. // So we can start typing right away. const onUnmount = () => { var _pluginInjectionApi$c7, _pluginInjectionApi$c8; return pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$c7 = pluginInjectionApi.core) === null || _pluginInjectionApi$c7 === void 0 ? void 0 : (_pluginInjectionApi$c8 = _pluginInjectionApi$c7.actions) === null || _pluginInjectionApi$c8 === void 0 ? void 0 : _pluginInjectionApi$c8.focus(); }; return jsx(TableSelectorPopup, { allowOutsideSelection: true, target: ref, onUnmount: onUnmount, onSelection: this.handleSelectedTableSize, popupsMountPoint: popupsMountPoint, popupsBoundariesElement: popupsBoundariesElement, popupsScrollableElement: popupsScrollableElement, handleClickOutside: this.handleTableSelectorClickOutside, handleEscapeKeydown: this.handleTableSelectorPressEscape, isOpenedByKeyboard: isTableSelectorOpenedByKeyboard }); } render() { var _tableButton, _tableButton2, _tableButton3, _tableButton4, _tableButton5, _tableButton6, _tableButton7, _tableSelectorButton, _tableSelectorButton2, _tableSelectorButton3, _tableSelectorButton4, _tableSelectorButton5, _tableSelectorButton6, _this$props$isDisable, _this$plusButtonRef, _this$props$pluginInj4; const { buttons, dropdownItems, emojiPickerOpen, isTableSelectorOpen } = this.state; const { isDisabled, isReducedSpacing, editorAppearance } = this.props; const isFullPageAppearance = ['full-page', 'full-width'].includes(editorAppearance !== null && editorAppearance !== void 0 ? editorAppearance : ''); const isTableButtonVisible = buttons.some(({ value }) => value.name === 'table'); const isTableSizeVisible = buttons.some(({ value }) => value.name === 'table selector'); if (buttons.length === 0 && dropdownItems.length === 0) { return null; } const toolbarButtons = []; let tableSelectorButton; let tableButton; // Seperate table buttons from toolbar buttons for (const btn of buttons) { if (btn.value.name === TABLE_SELECTOR_STRING) { tableSelectorButton = btn; } else if (btn.value.name === 'table' && this.props.tableSelectorSupported) { tableButton = btn; } else { toolbarButtons.push(btn); } } return jsx("span", { css: // eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage, @atlaskit/ui-styling-standard/no-imported-style-values buttonGroupStyle }, toolbarButtons.map(btn => { return jsx(ToolbarButton, { item: btn, testId: this.getToolbarButtonTestId(btn), ref: this.handleToolbarRef(btn.value.name), key: btn.value.name, spacing: isReducedSpacing ? 'none' : 'default', disabled: isDisabled || btn.isDisabled, iconBefore: btn.elemBefore, selected: btn.value.name === 'emoji' && emojiPickerOpen || btn.isActive, title: btn.title, "aria-label": btn['aria-label'], "aria-haspopup": btn['aria-haspopup'], "aria-keyshortcuts": btn['aria-keyshortcuts'], onItemClick: this.insertToolbarMenuItem }); }), this.props.tableSelectorSupported && (isTableButtonVisible || isTableSizeVisible) && jsx("div", { // eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage css: tableButtonWrapper({ isTableSelectorOpen, isButtonDisabled: (_tableButton = tableButton) === null || _tableButton === void 0 ? void 0 : _tableButton.isDisabled }) }, isTableButtonVisible && jsx(ToolbarButton // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop, @atlaskit/design-system/no-unsafe-style-overrides -- Ignored via go/DSP-18766 , { className: "table-toolbar-btn", item: tableButton, ref: this.tableButtonRef, testId: String((_tableButton2 = tableButton) === null || _tableButton2 === void 0 ? void 0 : _tableButton2.content), key: (_tableButton3 = tableButton) === null || _tableButton3 === void 0 ? void 0 : _tableButton3.value.name, spacing: isReducedSpacing ? 'none' : 'default', disabled: isDisabled || ((_tableButton4 = tableButton) === null || _tableButton4 === void 0 ? void 0 : _tableButton4.isDisabled), iconBefore: (_tableButton5 = tableButton) === null || _tableButton5 === void 0 ? void 0 : _tableButton5.elemBefore, selected: ((_tableButton6 = tableButton) === null || _tableButton6 === void 0 ? void 0 : _tableButton6.isActive) || isTableSelectorOpen, title: (_tableButton7 = tableButton) === null || _tableButton7 === void 0 ? void 0 : _tableButton7.title, "aria-label": tableButton ? tableButton['aria-label'] : undefined, "aria-haspopup": tableButton ? tableButton['aria-haspopup'] : undefined, "aria-keyshortcuts": tableButton ? tableButton['aria-keyshortcuts'] : undefined, onItemClick: this.insertToolbarMenuItem }), isTableButtonVisible && jsx(ToolbarButton // eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop, @atlaskit/design-system/no-unsafe-style-overrides -- Ignored via go/DSP-18766 , { className: "table-selector-toolbar-btn", item: tableSelectorButton, testId: String((_tableSelectorButton = tableSelectorButton) === null || _tableSelectorButton === void 0 ? void 0 : _tableSelectorButton.content), key: (_tableSelectorButton2 = tableSelectorButton) === null || _tableSelectorButton2 === void 0 ? void 0 : _tableSelectorButton2.value.name, ref: this.tableSelectorButtonRef, spacing: isReducedSpacing ? 'none' : 'default', disabled: isDisabled || ((_tableSelectorButton3 = tableSelectorButton) === null || _tableSelectorButton3 === void 0 ? void 0 : _tableSelectorButton3.isDisabled), iconBefore: (_tableSelectorButton4 = tableSelectorButton) === null || _tableSelectorButton4 === void 0 ? void 0 : _tableSelectorButton4.elemBefore, selected: ((_tableSelectorButton5 = tableSelectorButton) === null || _tableSelectorButton5 === void 0 ? void 0 : _tableSelectorButton5.isActive) || isTableSelectorOpen, title: (_tableSelectorButton6 = tableSelectorButton) === null || _tableSelectorButton6 === void 0 ? void 0 : _tableSelectorButton6.title, "aria-label": tableSelectorButton ? tableSelectorButton['aria-label'] : undefined, "aria-haspopup": tableSelectorButton ? tableSelectorButton['aria-haspopup'] : undefined, "aria-keyshortcuts": tableSelectorButton ? tableSelectorButton['aria-keyshortcuts'] : undefined, onItemClick: this.insertToolbarMenuItem, onKeyDown: this.handleTableSelectorOpenByKeyboard })), jsx("span", { css: wrapperStyle }, this.renderPopup(), this.renderTableSelectorPopup(), jsx(BlockInsertMenu, { popupsMountPoint: this.props.popupsMountPoint, popupsBoundariesElement: this.props.popupsBoundariesElement, popupsScrollableElement: this.props.popupsScrollableElement, disabled: (_this$props$isDisable = this.props.isDisabled) !== null && _this$props$isDisable !== void 0 ? _this$props$isDisable : false, editorView: this.props.editorView, spacing: this.props.isReducedSpacing ? 'none' : 'default', label: this.props.intl.formatMessage(messages.insertMenu), open: this.state.isPlusMenuOpen, plusButtonRef: (_this$plusButtonRef = this.plusButtonRef) === null || _this$plusButtonRef === void 0 ? void 0 : _this$plusButtonRef.deref(), items: this.state.dropdownItems, onRef: this.handleDropDownButtonRef, onPlusButtonRef: this.handlePlusButtonRef, onClick: this.handleClick, onKeyDown: this.handleOpenByKeyboard, onInsert: this.insertInsertMenuItem, togglePlusMenuVisibility: this.togglePlusMenuVisibility, showElementBrowserLink: this.props.showElementBrowserLink || false, pluginInjectionApi: this.props.pluginInjectionApi, isFullPageAppearance: isFullPageAppearance })), (!((_this$props$pluginInj4 = this.props.pluginInjectionApi) !== null && _this$props$pluginInj4 !== void 0 && _this$props$pluginInj4.primaryToolbar) && this.props.showSeparator || isFullPageAppearance && editorExperiment('platform_editor_controls', 'variant1')) && /* eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage */ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-imported-style-values, @atlaskit/design-system/consistent-css-prop-usage -- Ignored via go/DSP-18766 jsx("span", { css: separatorStyles })); } } // eslint-disable-next-line @typescript-eslint/ban-types const _default_1 = injectIntl(ToolbarInsertBlock); export default _default_1;