@atlaskit/editor-plugin-insert-block
Version:
Insert block plugin for @atlaskit/editor-core
243 lines (241 loc) • 12.4 kB
JavaScript
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
import React, { useRef, useState } from 'react';
import { useIntl } from 'react-intl';
import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
import { getAriaKeyshortcuts, insertElements, ToolTipContent } from '@atlaskit/editor-common/keymaps';
import { toolbarInsertBlockMessages as messages } from '@atlaskit/editor-common/messages';
import { TOOLBAR_BUTTON_TEST_ID, useEditorToolbar } from '@atlaskit/editor-common/toolbar';
import { Popup } from '@atlaskit/editor-common/ui';
import { useSharedPluginStateSelector } from '@atlaskit/editor-common/use-shared-plugin-state-selector';
import { akEditorMenuZIndex } from '@atlaskit/editor-shared-styles';
import { ToolbarButton, ToolbarTooltip, AddIcon, useToolbarUI } from '@atlaskit/editor-toolbar';
import InsertMenu, { DEFAULT_HEIGHT } from '../ElementBrowser/InsertMenu';
import { LINK_BUTTON_KEY } from './hooks/filterDropdownItems';
import { useEmojiPickerPopup } from './hooks/useEmojiPickerPopup';
import { useInsertButtonState } from './hooks/useInsertButtonState';
import { useTableSelectorPopup } from './hooks/useTableSelectorPopup';
import { EmojiPickerPopup } from './popups/EmojiPickerPopup';
// This determines how the popup should fit. We prefer the insert menu
// opening on the bottom as we have a search bar and should only open on
// top if there is more than sufficient room.
var FIT_HEIGHT_BUFFER = 100;
export var InsertButton = function InsertButton(_ref) {
var api = _ref.api,
breakpoint = _ref.breakpoint,
_ref$showElementBrows = _ref.showElementBrowserLink,
showElementBrowserLink = _ref$showElementBrows === void 0 ? false : _ref$showElementBrows,
_ref$isFullPageAppear = _ref.isFullPageAppearance,
isFullPageAppearance = _ref$isFullPageAppear === void 0 ? false : _ref$isFullPageAppear,
tableSelectorSupported = _ref.tableSelectorSupported,
nativeStatusSupported = _ref.nativeStatusSupported,
horizontalRuleEnabled = _ref.horizontalRuleEnabled,
expandEnabled = _ref.expandEnabled,
insertMenuItems = _ref.insertMenuItems,
numberOfButtons = _ref.numberOfButtons,
onInsertBlockType = _ref.onInsertBlockType,
toolbarConfig = _ref.toolbarConfig;
var _useEditorToolbar = useEditorToolbar(),
editorView = _useEditorToolbar.editorView;
var _useToolbarUI = useToolbarUI(),
isDisabled = _useToolbarUI.isDisabled,
popupsMountPoint = _useToolbarUI.popupsMountPoint,
popupsBoundariesElement = _useToolbarUI.popupsBoundariesElement,
popupsScrollableElement = _useToolbarUI.popupsScrollableElement;
var _useIntl = useIntl(),
formatMessage = _useIntl.formatMessage;
var _useState = useState(false),
_useState2 = _slicedToArray(_useState, 2),
insertMenuOpen = _useState2[0],
setInsertMenuOpen = _useState2[1];
var insertButtonRef = useRef(null);
var emojiPickerPopup = useEmojiPickerPopup({
api: api,
buttonRef: insertButtonRef
});
var tableSelectorPopup = useTableSelectorPopup({
api: api,
buttonRef: insertButtonRef
});
var showMediaPicker = useSharedPluginStateSelector(api, 'media.showMediaPicker');
var _useInsertButtonState = useInsertButtonState({
api: api,
breakpoint: breakpoint,
editorView: editorView || undefined,
horizontalRuleEnabled: horizontalRuleEnabled,
insertMenuItems: insertMenuItems,
nativeStatusSupported: nativeStatusSupported,
numberOfButtons: numberOfButtons,
tableSelectorSupported: tableSelectorSupported,
expandEnabled: expandEnabled,
showElementBrowserLink: showElementBrowserLink,
toolbarConfig: toolbarConfig
}),
dropdownItems = _useInsertButtonState.dropdownItems,
emojiProvider = _useInsertButtonState.emojiProvider,
isTypeAheadAllowed = _useInsertButtonState.isTypeAheadAllowed;
if (!(api !== null && api !== void 0 && api.insertBlock)) {
return null;
}
var toggleInsertMenuOpen = function toggleInsertMenuOpen(newState) {
setInsertMenuOpen(newState);
};
var onPopupUnmount = function onPopupUnmount() {
requestAnimationFrame(function () {
return api === null || api === void 0 ? void 0 : api.core.actions.focus();
});
};
var onClick = function onClick() {
toggleInsertMenuOpen(!insertMenuOpen);
};
var onItemActivated = function onItemActivated(_ref2) {
var _api$core, _api$hyperlink, _api$imageUpload, _api$mention, _api$taskDecision, _api$rule, _api$core3, _api$date, _api$placeholderText, _api$layout, _api$core4, _api$status, _api$expand;
var item = _ref2.item,
inputMethod = _ref2.inputMethod;
if (!editorView) {
return;
}
var state = editorView.state,
dispatch = editorView.dispatch;
// need to do this before inserting nodes so scrollIntoView works properly
if (!editorView.hasFocus()) {
editorView.focus();
}
switch (item.value.name) {
case LINK_BUTTON_KEY:
api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(api === null || api === void 0 || (_api$hyperlink = api.hyperlink) === null || _api$hyperlink === void 0 ? void 0 : _api$hyperlink.commands.showLinkToolbar(inputMethod));
break;
case 'table':
// workaround to solve race condition where cursor is not placed correctly inside table
queueMicrotask(function () {
var _api$table, _api$table$actions$in, _api$table$actions;
api === null || api === void 0 || (_api$table = api.table) === null || _api$table === void 0 || (_api$table$actions$in = (_api$table$actions = _api$table.actions).insertTable) === null || _api$table$actions$in === void 0 || _api$table$actions$in.call(_api$table$actions, {
action: ACTION.INSERTED,
actionSubject: ACTION_SUBJECT.DOCUMENT,
actionSubjectId: ACTION_SUBJECT_ID.TABLE,
attributes: {
inputMethod: inputMethod
},
eventType: EVENT_TYPE.TRACK
})(state, dispatch);
});
break;
case 'table selector':
tableSelectorPopup.toggle(inputMethod);
break;
case 'image upload':
if (api !== null && api !== void 0 && (_api$imageUpload = api.imageUpload) !== null && _api$imageUpload !== void 0 && _api$imageUpload.actions.startUpload) {
api.imageUpload.actions.startUpload()(state, dispatch);
}
break;
case 'media':
if (showMediaPicker) {
var _api$mediaInsert, _api$core2, _api$mediaInsert2;
api !== null && api !== void 0 && (_api$mediaInsert = api.mediaInsert) !== null && _api$mediaInsert !== void 0 && _api$mediaInsert.commands.showMediaInsertPopup ? api === null || api === void 0 || (_api$core2 = api.core) === null || _api$core2 === void 0 ? void 0 : _api$core2.actions.execute(api === null || api === void 0 || (_api$mediaInsert2 = api.mediaInsert) === null || _api$mediaInsert2 === void 0 ? void 0 : _api$mediaInsert2.commands.showMediaInsertPopup()) : showMediaPicker === null || showMediaPicker === void 0 ? void 0 : showMediaPicker();
}
break;
case 'mention':
api === null || api === void 0 || (_api$mention = api.mention) === null || _api$mention === void 0 || (_api$mention = _api$mention.actions) === null || _api$mention === void 0 || _api$mention.openTypeAhead(inputMethod);
break;
case 'emoji':
emojiPickerPopup.toggle(inputMethod);
break;
case 'codeblock':
case 'blockquote':
case 'panel':
onInsertBlockType === null || onInsertBlockType === void 0 || onInsertBlockType(item.value.name)(state, dispatch);
break;
case 'action':
case 'decision':
var listType = item.value.name === 'action' ? 'taskList' : 'decisionList';
api === null || api === void 0 || (_api$taskDecision = api.taskDecision) === null || _api$taskDecision === void 0 || _api$taskDecision.actions.insertTaskDecision(listType, inputMethod)(state, dispatch);
break;
case 'horizontalrule':
api === null || api === void 0 || (_api$rule = api.rule) === null || _api$rule === void 0 || _api$rule.actions.insertHorizontalRule(inputMethod)(state, dispatch);
break;
case 'date':
api === null || api === void 0 || (_api$core3 = api.core) === null || _api$core3 === void 0 || _api$core3.actions.execute(api === null || api === void 0 || (_api$date = api.date) === null || _api$date === void 0 || (_api$date = _api$date.commands) === null || _api$date === void 0 ? void 0 : _api$date.insertDate({
inputMethod: inputMethod
}));
break;
case 'placeholder text':
api === null || api === void 0 || (_api$placeholderText = api.placeholderText) === null || _api$placeholderText === void 0 || _api$placeholderText.actions.showPlaceholderFloatingToolbar(state, dispatch);
break;
case 'layout':
api === null || api === void 0 || (_api$layout = api.layout) === null || _api$layout === void 0 || _api$layout.actions.insertLayoutColumns(inputMethod)(state, dispatch);
break;
case 'status':
api === null || api === void 0 || (_api$core4 = api.core) === null || _api$core4 === void 0 || _api$core4.actions.execute(api === null || api === void 0 || (_api$status = api.status) === null || _api$status === void 0 || (_api$status = _api$status.commands) === null || _api$status === void 0 ? void 0 : _api$status.insertStatus(inputMethod));
break;
case 'expand':
api === null || api === void 0 || (_api$expand = api.expand) === null || _api$expand === void 0 || _api$expand.actions.insertExpand(state, dispatch);
break;
default:
if (item && item.onClick) {
item.onClick();
}
break;
}
toggleInsertMenuOpen(false);
};
var onInsert = function onInsert(_ref3) {
var item = _ref3.item;
onItemActivated({
item: item,
inputMethod: INPUT_METHOD.INSERT_MENU
});
};
var toggleVisibility = function toggleVisibility() {
toggleInsertMenuOpen(!insertMenuOpen);
};
return /*#__PURE__*/React.createElement(React.Fragment, null, insertMenuOpen && insertButtonRef.current && editorView && /*#__PURE__*/React.createElement(Popup, {
target: insertButtonRef.current,
fitHeight: DEFAULT_HEIGHT + FIT_HEIGHT_BUFFER,
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: onPopupUnmount,
focusTrap: true,
zIndex: akEditorMenuZIndex,
preventOverflow: true,
alignX: "right"
}, /*#__PURE__*/React.createElement(InsertMenu, {
editorView: editorView,
dropdownItems: dropdownItems,
onInsert: onInsert,
toggleVisiblity: toggleVisibility,
showElementBrowserLink: showElementBrowserLink,
pluginInjectionApi: api,
isFullPageAppearance: isFullPageAppearance
})), emojiProvider && /*#__PURE__*/React.createElement(EmojiPickerPopup, {
isOpen: emojiPickerPopup.isOpen,
targetRef: insertButtonRef,
emojiProvider: Promise.resolve(emojiProvider),
onSelection: emojiPickerPopup.handleSelectedEmoji,
onClickOutside: emojiPickerPopup.handleClickOutside,
onEscapeKeydown: emojiPickerPopup.handleEscapeKeydown,
onUnmount: emojiPickerPopup.onPopupUnmount,
popupsMountPoint: popupsMountPoint,
popupsBoundariesElement: popupsBoundariesElement,
popupsScrollableElement: popupsScrollableElement
}), /*#__PURE__*/React.createElement(ToolbarTooltip, {
content: /*#__PURE__*/React.createElement(ToolTipContent, {
description: formatMessage(messages.insertMenu),
keymap: insertElements
})
}, /*#__PURE__*/React.createElement(ToolbarButton, {
iconBefore: /*#__PURE__*/React.createElement(AddIcon, {
size: "small",
label: formatMessage(messages.insertMenu)
}),
ariaKeyshortcuts: getAriaKeyshortcuts(insertElements),
ref: insertButtonRef,
onClick: onClick,
isSelected: insertMenuOpen,
isDisabled: !isTypeAheadAllowed || isDisabled,
testId: TOOLBAR_BUTTON_TEST_ID.INSERT
})));
};