@atlaskit/editor-plugin-insert-block
Version:
Insert block plugin for @atlaskit/editor-core
267 lines (264 loc) • 14.2 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.sortFeaturedItems = exports.default = exports.DEFAULT_HEIGHT = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _react = require("react");
var _react2 = require("@emotion/react");
var _reactIntl = require("react-intl");
var _CellMeasurer = require("react-virtualized/dist/commonjs/CellMeasurer");
var _analytics = require("@atlaskit/editor-common/analytics");
var _elementBrowser = require("@atlaskit/editor-common/element-browser");
var _hooks = require("@atlaskit/editor-common/hooks");
var _quickInsert = require("@atlaskit/editor-common/quick-insert");
var _uiReact = require("@atlaskit/editor-common/ui-react");
var _editorPluginConnectivity = require("@atlaskit/editor-plugin-connectivity");
var _expVal = require("@atlaskit/tmp-editor-statsig/expVal");
var _excluded = ["children"];
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) { (0, _defineProperty2.default)(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
*/ /* eslint-disable @atlaskit/ui-styling-standard/use-compiled, @typescript-eslint/consistent-type-imports, jsdoc/require-description -- Ignored via go/DSP-18766; jsdoc debt surfaced by this mechanical PR */
var DEFAULT_HEIGHT = exports.DEFAULT_HEIGHT = 560;
/**
* Exported helper to allow testing of InsertMenu pinning logic.
*
* The `cc_fd_db_top_editor_toolbar` experiment adds new logic to sort elements by `priority`.
* This newer implementation matches how the quick insert menu sorts elements.
*/
var sortFeaturedItems = exports.sortFeaturedItems = function sortFeaturedItems(featuredItems, formatMessage) {
if (['new-description', 'orig-description'].includes((0, _expVal.expVal)('cc_fd_db_top_editor_toolbar', 'cohort', 'control')) || (0, _expVal.expValNoExposure)('cc_fd_wb_jira_quick_insert_experiment', 'isEnabled', false) || ['slot-two', 'slot-four'].includes((0, _expVal.expValNoExposure)('cc_fd_cwr_quick_insert', 'cohort', 'control'))) {
// Sort by priority (lower first) on the concatenated list so items
// with "priority" are at the top (e.g. Whiteboard before Database)
return featuredItems.slice(0).sort(function (a, b) {
return (a.priority || Number.POSITIVE_INFINITY) - (b.priority || Number.POSITIVE_INFINITY);
});
}
// NOTE: this is *not* the ideal way to approach this. Old logic sort whiteboards to top
var DIAGRAM_KEY = 'whiteboard-extension:create-diagram';
var isDiagram = function isDiagram(item) {
return item.key === DIAGRAM_KEY;
};
var featuredWhiteboardsPresent = featuredItems.some(isDiagram);
if (featuredWhiteboardsPresent) {
var pin = function pin(key) {
var idx = featuredItems.findIndex(function (item) {
return item.key === key;
});
var filtered = featuredItems.filter(function (item) {
return !isDiagram(item);
});
if (idx === -1) {
return filtered;
}
var picked = _objectSpread(_objectSpread({}, featuredItems[idx]), {}, {
description: formatMessage(_quickInsert.messages.featuredWhiteboardDescription)
});
return [picked].concat((0, _toConsumableArray2.default)(filtered));
};
return pin(DIAGRAM_KEY);
}
return featuredItems;
};
var selector = function selector(states) {
var _states$connectivityS;
return {
connectivityMode: (_states$connectivityS = states.connectivityState) === null || _states$connectivityS === void 0 ? void 0 : _states$connectivityS.mode
};
};
var InsertMenu = function InsertMenu(_ref) {
var _pluginInjectionApi$q6, _pluginInjectionApi$q7;
var editorView = _ref.editorView,
dropdownItems = _ref.dropdownItems,
showElementBrowserLink = _ref.showElementBrowserLink,
onInsert = _ref.onInsert,
toggleVisiblity = _ref.toggleVisiblity,
pluginInjectionApi = _ref.pluginInjectionApi;
var _useState = (0, _react.useState)(0),
_useState2 = (0, _slicedToArray2.default)(_useState, 2),
itemCount = _useState2[0],
setItemCount = _useState2[1];
var _useState3 = (0, _react.useState)(DEFAULT_HEIGHT),
_useState4 = (0, _slicedToArray2.default)(_useState3, 2),
height = _useState4[0],
setHeight = _useState4[1];
var _useIntl = (0, _reactIntl.useIntl)(),
formatMessage = _useIntl.formatMessage;
var cache = (0, _react.useMemo)(function () {
return new _CellMeasurer.CellMeasurerCache({
fixedWidth: true,
defaultHeight: _elementBrowser.ELEMENT_ITEM_HEIGHT,
minHeight: _elementBrowser.ELEMENT_ITEM_HEIGHT
});
}, []);
(0, _react.useLayoutEffect)(function () {
// Figure based on visuals to exclude the searchbar, padding/margin, and the ViewMore item.
var EXTRA_SPACE_EXCLUDING_ELEMENTLIST = 128;
var totalItemHeight =
// eslint-disable-next-line @atlassian/perf-linting/no-expensive-computations-in-render -- Ignored via go/ees017 (to be fixed)
(0, _toConsumableArray2.default)(Array(itemCount)).reduce(function (sum, _, index) {
return sum + cache.rowHeight({
index: index
});
}, 0) + EXTRA_SPACE_EXCLUDING_ELEMENTLIST;
if (itemCount > 0 && totalItemHeight < DEFAULT_HEIGHT) {
setHeight(totalItemHeight);
} else {
setHeight(DEFAULT_HEIGHT);
}
}, [cache, itemCount]);
var transform = (0, _react.useCallback)(function (item) {
return {
title: item.content,
description: item.tooltipDescription,
keyshortcut: item.shortcut,
icon: function icon() {
return getSvgIconForItem({
name: item.value.name
}) || item.elemBefore;
},
/**
* @note This transformed items action is only used when a quick insert item has been
* called from the quick insert menu and a search has not been performed.
*/
action: function action() {
return onInsert({
item: item
});
},
// "insertInsertMenuItem" expects these 2 properties.
onClick: item.onClick,
value: item.value
};
}, [onInsert]);
var quickInsertDropdownItems = dropdownItems.map(transform);
var onInsertItem = (0, _react.useCallback)(function (item) {
var _pluginInjectionApi$q;
toggleVisiblity();
if (!editorView.hasFocus()) {
editorView.focus();
}
pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$q = pluginInjectionApi.quickInsert) === null || _pluginInjectionApi$q === void 0 || _pluginInjectionApi$q.actions.insertItem(item, _analytics.INPUT_METHOD.TOOLBAR)(editorView.state, editorView.dispatch);
}, [editorView, toggleVisiblity, pluginInjectionApi]);
var _useSharedPluginState = (0, _hooks.useSharedPluginStateWithSelector)(pluginInjectionApi, ['connectivity'], selector),
connectivityMode = _useSharedPluginState.connectivityMode;
var getItems = (0, _react.useCallback)(function (query, category) {
var result;
/**
* @warning The results if there is a query are not the same as the results if there is no query.
* For example: If you have a typed panel and then select the panel item then it will call a different action
* than is specified on the editor plugins quick insert
* @see above transform function for more details.
*/
if (query) {
var _pluginInjectionApi$q2, _pluginInjectionApi$q3;
result = (_pluginInjectionApi$q2 = pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$q3 = pluginInjectionApi.quickInsert) === null || _pluginInjectionApi$q3 === void 0 || (_pluginInjectionApi$q3 = _pluginInjectionApi$q3.actions.getSuggestions({
query: query,
category: category
})) === null || _pluginInjectionApi$q3 === void 0 ? void 0 : _pluginInjectionApi$q3.map(function (item) {
return (0, _editorPluginConnectivity.isOfflineMode)(connectivityMode) && item.isDisabledOffline ? _objectSpread(_objectSpread({}, item), {}, {
isDisabled: true
}) : item;
})) !== null && _pluginInjectionApi$q2 !== void 0 ? _pluginInjectionApi$q2 : [];
} else {
var _pluginInjectionApi$q4, _pluginInjectionApi$q5;
var featuredQuickInsertSuggestions = (_pluginInjectionApi$q4 = pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$q5 = pluginInjectionApi.quickInsert) === null || _pluginInjectionApi$q5 === void 0 || (_pluginInjectionApi$q5 = _pluginInjectionApi$q5.actions.getSuggestions({
category: category,
featuredItems: true
})) === null || _pluginInjectionApi$q5 === void 0 ? void 0 : _pluginInjectionApi$q5.map(function (item) {
return (0, _editorPluginConnectivity.isOfflineMode)(connectivityMode) && item.isDisabledOffline ? _objectSpread(_objectSpread({}, item), {}, {
isDisabled: true
}) : item;
})) !== null && _pluginInjectionApi$q4 !== void 0 ? _pluginInjectionApi$q4 : [];
var unfilteredResult = quickInsertDropdownItems.concat(featuredQuickInsertSuggestions);
// need to sort on the concatenated list so desired elements are at the top
result = sortFeaturedItems(unfilteredResult, formatMessage);
}
setItemCount(result.length);
return result;
}, [pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$q6 = pluginInjectionApi.quickInsert) === null || _pluginInjectionApi$q6 === void 0 ? void 0 : _pluginInjectionApi$q6.actions, quickInsertDropdownItems, connectivityMode, formatMessage]);
var emptyStateHandler = pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$q7 = pluginInjectionApi.quickInsert) === null || _pluginInjectionApi$q7 === void 0 || (_pluginInjectionApi$q7 = _pluginInjectionApi$q7.sharedState.currentState()) === null || _pluginInjectionApi$q7 === void 0 ? void 0 : _pluginInjectionApi$q7.emptyStateHandler;
var onViewMore = (0, _react.useCallback)(function () {
var _pluginInjectionApi$c, _pluginInjectionApi$q8;
toggleVisiblity();
pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$c = pluginInjectionApi.core) === null || _pluginInjectionApi$c === void 0 || _pluginInjectionApi$c.actions.execute(pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$q8 = pluginInjectionApi.quickInsert) === null || _pluginInjectionApi$q8 === void 0 ? void 0 : _pluginInjectionApi$q8.commands.openElementBrowserModal);
}, [pluginInjectionApi, toggleVisiblity]);
return (
// eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage
(0, _react2.jsx)("div", {
css: insertMenuWrapper(height)
}, (0, _react2.jsx)(ElementBrowserWrapper, {
handleClickOutside: toggleVisiblity,
handleEscapeKeydown: toggleVisiblity,
closeOnTab: false
}, (0, _react2.jsx)(_elementBrowser.ElementBrowser, {
mode: "inline",
getItems: getItems,
emptyStateHandler: emptyStateHandler,
onInsertItem: onInsertItem,
showSearch: true,
showCategories: false
// On page resize we want the InlineElementBrowser to show updated tools/overflow items
,
key: quickInsertDropdownItems.length,
onViewMore: showElementBrowserLink ? onViewMore : undefined,
cache: cache
})))
);
};
var getSvgIconForItem = function getSvgIconForItem(_ref2) {
var name = _ref2.name;
var Icon = {
codeblock: _quickInsert.IconCode,
panel: _quickInsert.IconPanel,
blockquote: _quickInsert.IconQuote,
decision: _quickInsert.IconDecision,
horizontalrule: _quickInsert.IconDivider,
expand: _quickInsert.IconExpand,
date: _quickInsert.IconDate,
status: _quickInsert.IconStatus
}[name];
return Icon ? (0, _react2.jsx)(Icon, {
label: ""
}) : undefined;
};
var insertMenuWrapper = function insertMenuWrapper(height) {
return (0, _react2.css)({
display: 'flex',
flexDirection: 'column',
width: '320px',
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values, @atlaskit/ui-styling-standard/no-imported-style-values -- Ignored via go/DSP-18766
height: "".concat(height, "px"),
backgroundColor: "var(--ds-surface-overlay, #FFFFFF)",
borderRadius: "var(--ds-radius-small, 3px)",
boxShadow: "var(--ds-shadow-overlay, 0px 8px 12px #1E1F2126, 0px 0px 1px #1E1F214f)"
});
};
var flexWrapperStyles = (0, _react2.css)({
display: 'flex',
flex: 1,
boxSizing: 'border-box',
overflow: 'hidden'
});
var FlexWrapper = function FlexWrapper(props) {
var setOutsideClickTargetRef = (0, _react.useContext)(_uiReact.OutsideClickTargetRefContext);
var children = props.children,
divProps = (0, _objectWithoutProperties2.default)(props, _excluded);
return (
// Ignored via go/ees005
// eslint-disable-next-line react/jsx-props-no-spreading
(0, _react2.jsx)("div", (0, _extends2.default)({
ref: setOutsideClickTargetRef,
css: flexWrapperStyles
}, divProps), children)
);
};
var ElementBrowserWrapper = (0, _uiReact.withReactEditorViewOuterListeners)(FlexWrapper);
var _default = exports.default = InsertMenu;