@atlaskit/editor-plugin-insert-block
Version:
Insert block plugin for @atlaskit/editor-core
849 lines (833 loc) • 39.3 kB
JavaScript
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;