@atlaskit/editor-plugin-card
Version:
Card plugin for @atlaskit/editor-core
385 lines (384 loc) • 19.6 kB
JavaScript
import _typeof from "@babel/runtime/helpers/typeof";
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import _regeneratorRuntime from "@babel/runtime/regenerator";
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
import React, { useContext, useEffect } from 'react';
import { cardMessages as messages } from '@atlaskit/editor-common/messages';
import { IconDatasourceAssetsObjects, IconDatasourceConfluenceSearch, IconDatasourceJiraIssue } from '@atlaskit/editor-common/quick-insert';
import { canRenderDatasource } from '@atlaskit/editor-common/utils';
import { ASSETS_LIST_OF_LINKS_DATASOURCE_ID, CONFLUENCE_SEARCH_DATASOURCE_ID } from '@atlaskit/link-datasource';
import { SmartCardContext } from '@atlaskit/link-provider/context';
import { fg } from '@atlaskit/platform-feature-flags';
import { expValNoExposure } from '@atlaskit/tmp-editor-statsig/expVal';
import { blockCardSpecWithFixedToDOM } from './nodeviews/toDOM-fixes/blockCard';
import { embedCardSpecWithFixedToDOM } from './nodeviews/toDOM-fixes/embedCard';
import { inlineCardSpecWithFixedToDOM } from './nodeviews/toDOM-fixes/inlineCard';
import { hideLinkToolbar, setProvider as _setProvider, showDatasourceModal } from './pm-plugins/actions';
import { queueCardsFromChangedTr, queueCardsFromRange as _queueCardsFromRange } from './pm-plugins/doc';
import { cardKeymap } from './pm-plugins/keymap';
import { createPlugin } from './pm-plugins/main';
import { pluginKey } from './pm-plugins/plugin-key';
import { isDatasourceConfigEditable } from './pm-plugins/utils';
import { createEventsQueue } from './ui/analytics/create-events-queue';
import DatasourceModalWithState from './ui/DatasourceModal/ModalWithState';
import { EditorLinkingPlatformAnalytics } from './ui/EditorLinkingPlatformAnalytics';
import { EditorSmartCardEvents } from './ui/EditorSmartCardEvents';
// Ignored via go/ees005
// eslint-disable-next-line import/no-named-as-default
import LayoutButton from './ui/LayoutButton';
import { getPasteDisplayAsMenuComponents } from './ui/PasteDisplayAsMenu';
import { floatingToolbar, getEndingToolbarItems, getStartingToolbarItems } from './ui/toolbar';
var PasteMenuSmartCardClientSync = function PasteMenuSmartCardClientSync(_ref) {
var _smartCardContext$con2;
var clientRef = _ref.clientRef;
var smartCardContext = useContext(SmartCardContext);
useEffect(function () {
var _smartCardContext$con;
clientRef.current = smartCardContext === null || smartCardContext === void 0 || (_smartCardContext$con = smartCardContext.connections) === null || _smartCardContext$con === void 0 ? void 0 : _smartCardContext$con.client;
}, [clientRef, smartCardContext === null || smartCardContext === void 0 || (_smartCardContext$con2 = smartCardContext.connections) === null || _smartCardContext$con2 === void 0 ? void 0 : _smartCardContext$con2.client]);
return null;
};
export var cardPlugin = function cardPlugin(_ref2) {
var _api$base, _options$lpLinkPicker;
var _ref2$config = _ref2.config,
options = _ref2$config === void 0 ? {} : _ref2$config,
api = _ref2.api;
var previousCardProvider;
var cardPluginEvents = createEventsQueue();
var instanceEmbedCardTransformers = options.embedCardTransformers;
var editorViewForPasteMenu;
var pasteMenuSmartCardClientRef = {
current: undefined
};
var pasteMenuVariant = expValNoExposure('platform_editor_paste_actions_menu_v2', 'variant', 'control');
var shouldRegisterPasteDisplayAsMenu = options.enablePasteDisplayAsMenu && ['hasSpellingAndGrammar', 'hasAltAiActions'].includes(pasteMenuVariant);
if (shouldRegisterPasteDisplayAsMenu) {
var _api$uiControlRegistr, _options$allowBlockCa;
api === null || api === void 0 || (_api$uiControlRegistr = api.uiControlRegistry) === null || _api$uiControlRegistr === void 0 || _api$uiControlRegistr.actions.register(getPasteDisplayAsMenuComponents({
api: api,
allowBlockCards: options.onlyInlineCards ? false : (_options$allowBlockCa = options.allowBlockCards) !== null && _options$allowBlockCa !== void 0 ? _options$allowBlockCa : true,
allowEmbeds: options.onlyInlineCards ? false : options.allowEmbeds,
getEditorView: function getEditorView() {
return editorViewForPasteMenu;
},
smartCardClientRef: pasteMenuSmartCardClientRef
}));
}
api === null || api === void 0 || (_api$base = api.base) === null || _api$base === void 0 || _api$base.actions.registerMarks(function (_ref3) {
var tr = _ref3.tr,
node = _ref3.node,
pos = _ref3.pos;
var doc = tr.doc;
var schema = doc.type.schema;
var inlineCardNodeType = schema.nodes.inlineCard;
if (node.type === inlineCardNodeType) {
var newText = node.attrs.url;
var currentPos = tr.mapping.map(pos);
tr.replaceWith(currentPos, currentPos + node.nodeSize, schema.text(newText, node.marks));
}
});
return {
name: 'card',
getSharedState: function getSharedState(editorState) {
if (!editorState) {
return null;
}
return pluginKey.getState(editorState) || null;
},
nodes: function nodes() {
var nodes = [{
name: 'inlineCard',
node: inlineCardSpecWithFixedToDOM()
}];
// `onlyInlineCards` is a hard gate: when set, blockCard/embedCard
// stay out of the schema regardless of allowBlockCards/allowEmbeds.
if (options.onlyInlineCards) {
return nodes;
}
nodes.push({
name: 'blockCard',
node: blockCardSpecWithFixedToDOM()
});
if (options.allowEmbeds) {
nodes.push({
name: 'embedCard',
node: embedCardSpecWithFixedToDOM()
});
}
return nodes;
},
pmPlugins: function pmPlugins() {
var _options$allowBlockCa2, _options$allowResizin, _options$useAlternati, _options$allowWrappin, _options$allowAlignme, _options$allowDatasou, _options$showUpgradeD;
// onlyInlineCards forces block/embed off regardless of caller-passed flags,
// keeping the schema gate (in nodes()) and the runtime gate in sync.
var allowBlockCards = options.onlyInlineCards ? false : (_options$allowBlockCa2 = options.allowBlockCards) !== null && _options$allowBlockCa2 !== void 0 ? _options$allowBlockCa2 : true;
var allowEmbeds = options.onlyInlineCards ? false : options.allowEmbeds;
var allowResizing = (_options$allowResizin = options.allowResizing) !== null && _options$allowResizin !== void 0 ? _options$allowResizin : true;
var useAlternativePreloader = (_options$useAlternati = options.useAlternativePreloader) !== null && _options$useAlternati !== void 0 ? _options$useAlternati : true;
var allowWrapping = (_options$allowWrappin = options.allowWrapping) !== null && _options$allowWrappin !== void 0 ? _options$allowWrappin : true;
var allowAlignment = (_options$allowAlignme = options.allowAlignment) !== null && _options$allowAlignme !== void 0 ? _options$allowAlignme : true;
var allowDatasource = (_options$allowDatasou = options.allowDatasource) !== null && _options$allowDatasou !== void 0 ? _options$allowDatasou : false;
var showUpgradeDiscoverability = (_options$showUpgradeD = options.showUpgradeDiscoverability) !== null && _options$showUpgradeD !== void 0 ? _options$showUpgradeD : true;
var plugins = [{
name: 'card',
plugin: createPlugin(_objectSpread(_objectSpread({}, options), {}, {
allowBlockCards: allowBlockCards,
allowEmbeds: allowEmbeds,
allowResizing: allowResizing,
useAlternativePreloader: useAlternativePreloader,
allowWrapping: allowWrapping,
allowAlignment: allowAlignment,
allowDatasource: allowDatasource,
cardPluginEvents: cardPluginEvents,
embedCardTransformers: instanceEmbedCardTransformers,
showUpgradeDiscoverability: showUpgradeDiscoverability
}), api)
}];
plugins.push({
name: 'cardKeymap',
plugin: function plugin(_ref4) {
var featureFlags = _ref4.featureFlags;
return cardKeymap(featureFlags);
}
});
return plugins;
},
contentComponent: function contentComponent(_ref5) {
var editorView = _ref5.editorView,
popupsMountPoint = _ref5.popupsMountPoint,
popupsScrollableElement = _ref5.popupsScrollableElement,
popupsBoundariesElement = _ref5.popupsBoundariesElement;
if (!editorView) {
return null;
}
editorViewForPasteMenu = editorView;
var breakoutEnabled = options.editorAppearance === 'full-page';
return /*#__PURE__*/React.createElement(React.Fragment, null, shouldRegisterPasteDisplayAsMenu && /*#__PURE__*/React.createElement(PasteMenuSmartCardClientSync, {
clientRef: pasteMenuSmartCardClientRef
}), /*#__PURE__*/React.createElement(EditorSmartCardEvents, {
editorView: editorView
}), /*#__PURE__*/React.createElement(EditorLinkingPlatformAnalytics, {
cardPluginEvents: cardPluginEvents,
editorView: editorView
}), breakoutEnabled && /*#__PURE__*/React.createElement(LayoutButton, {
api: api,
editorView: editorView,
mountPoint: popupsMountPoint,
scrollableElement: popupsScrollableElement,
boundariesElement: popupsBoundariesElement
}), /*#__PURE__*/React.createElement(DatasourceModalWithState, {
api: api,
editorView: editorView
}));
},
commands: {
queueCardsFromRange: function queueCardsFromRange(from, to, source, analyticsAction, normalizeLinkText, sourceEvent, appearance) {
return function (_ref6) {
var tr = _ref6.tr;
return _queueCardsFromRange(
// Synthesise the minimal EditorState shape the impl actually reads.
{
schema: tr.doc.type.schema,
selection: tr.selection
}, tr, from, to, source, analyticsAction, normalizeLinkText, sourceEvent, appearance);
};
}
},
actions: {
setProvider: function () {
var _setProvider2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(providerPromise) {
var _api$core$actions$exe;
var provider;
return _regeneratorRuntime.wrap(function (_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
_context.next = 1;
return providerPromise;
case 1:
provider = _context.sent;
if (!(previousCardProvider === provider || (options === null || options === void 0 ? void 0 : options.provider) === providerPromise)) {
_context.next = 2;
break;
}
return _context.abrupt("return", false);
case 2:
previousCardProvider = provider;
return _context.abrupt("return", (_api$core$actions$exe = api === null || api === void 0 ? void 0 : api.core.actions.execute(function (_ref7) {
var tr = _ref7.tr;
return _setProvider(provider)(tr);
})) !== null && _api$core$actions$exe !== void 0 ? _api$core$actions$exe : false);
case 3:
case "end":
return _context.stop();
}
}, _callee);
}));
function setProvider(_x) {
return _setProvider2.apply(this, arguments);
}
return setProvider;
}(),
hideLinkToolbar: hideLinkToolbar,
queueCardsFromChangedTr: queueCardsFromChangedTr,
queueCardsFromRange: _queueCardsFromRange,
registerEmbedCardTransformer: function registerEmbedCardTransformer(transformers) {
instanceEmbedCardTransformers = transformers;
},
resolveShortLinkUrl: function () {
var _resolveShortLinkUrl = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(url) {
var provider, clientHolder, client, response, data, urlField, _linkObj$Id, linkObj, id, _t;
return _regeneratorRuntime.wrap(function (_context2) {
while (1) switch (_context2.prev = _context2.next) {
case 0:
if (options.provider) {
_context2.next = 1;
break;
}
return _context2.abrupt("return", undefined);
case 1:
_context2.prev = 1;
_context2.next = 2;
return options.provider;
case 2:
provider = _context2.sent;
// EditorCardProvider holds a CardClient instance internally. We access it
// directly to call fetchData, which gives us the raw ORS JSON-LD response
// including the canonical url field — something provider.resolve() does not
// surface. This is intentionally a structural cast rather than a typed
// dependency on EditorCardProvider's private API.
clientHolder = provider;
client = clientHolder.cardClient;
if (client) {
_context2.next = 3;
break;
}
return _context2.abrupt("return", undefined);
case 3:
_context2.next = 4;
return client.fetchData(url);
case 4:
response = _context2.sent;
// ORS returns a JsonLd.Response whose data union includes BaseData,
// BaseCollectionData, and BaseCollectionPage. We only care about the
// `url` field which is present on BaseData (Primitives.Object). Cast
// to a minimal shape rather than accessing through the full union type.
data = response === null || response === void 0 ? void 0 : response.data;
if (data) {
_context2.next = 5;
break;
}
return _context2.abrupt("return", undefined);
case 5:
urlField = data.url; // Property<string | Link> can be a string, a Link object, or an array.
// Short-link responses always return a plain string URL.
if (!(typeof urlField === 'string' && urlField !== url)) {
_context2.next = 6;
break;
}
return _context2.abrupt("return", urlField);
case 6:
if (!(urlField && _typeof(urlField) === 'object' && !Array.isArray(urlField))) {
_context2.next = 7;
break;
}
linkObj = urlField;
id = (_linkObj$Id = linkObj['@id']) !== null && _linkObj$Id !== void 0 ? _linkObj$Id : linkObj['href'];
if (!(typeof id === 'string' && id !== url)) {
_context2.next = 7;
break;
}
return _context2.abrupt("return", id);
case 7:
_context2.next = 9;
break;
case 8:
_context2.prev = 8;
_t = _context2["catch"](1);
case 9:
return _context2.abrupt("return", undefined);
case 10:
case "end":
return _context2.stop();
}
}, _callee2, null, [[1, 8]]);
}));
function resolveShortLinkUrl(_x2) {
return _resolveShortLinkUrl.apply(this, arguments);
}
return resolveShortLinkUrl;
}(),
getStartingToolbarItems: getStartingToolbarItems(options, api),
getEndingToolbarItems: getEndingToolbarItems(options, api)
},
pluginsOptions: {
floatingToolbar: floatingToolbar(options, (_options$lpLinkPicker = options.lpLinkPicker) !== null && _options$lpLinkPicker !== void 0 ? _options$lpLinkPicker : false, options.linkPicker, api, options.disableFloatingToolbar),
quickInsert: function quickInsert(_ref8) {
var formatMessage = _ref8.formatMessage;
var quickInsertArray = [];
if (!options.allowDatasource) {
return quickInsertArray;
}
quickInsertArray.push(_objectSpread(_objectSpread({
id: 'datasource',
title: formatMessage(fg('confluence-issue-terminology-refresh') ? messages.datasourceJiraIssueIssueTermRefresh : messages.datasourceJiraIssue),
description: formatMessage(fg('confluence-issue-terminology-refresh') ? messages.datasourceJiraIssueDescriptionIssueTermRefresh : messages.datasourceJiraIssueDescription),
isDisabledOffline: true,
categories: ['external-content', 'development'],
keywords: ['jira'],
featured: true
}, fg('jim-lower-ranking-in-jira-macro-search') && {
priority: 500
}), {}, {
icon: function icon() {
return /*#__PURE__*/React.createElement(IconDatasourceJiraIssue, null);
},
action: function action(insert) {
var tr = insert(undefined);
showDatasourceModal('jira')(tr);
return tr;
}
}));
if (canRenderDatasource(ASSETS_LIST_OF_LINKS_DATASOURCE_ID)) {
quickInsertArray.push({
id: 'datasource',
title: formatMessage(messages.datasourceAssetsObjectsGeneralAvailability),
description: formatMessage(messages.datasourceAssetsObjectsDescription),
isDisabledOffline: true,
categories: ['external-content', 'development'],
keywords: ['assets'],
icon: function icon() {
return /*#__PURE__*/React.createElement(IconDatasourceAssetsObjects, null);
},
action: function action(insert) {
var tr = insert(undefined);
showDatasourceModal('assets')(tr);
return tr;
}
});
}
if (isDatasourceConfigEditable(CONFLUENCE_SEARCH_DATASOURCE_ID)) {
quickInsertArray.push({
id: 'datasource',
title: formatMessage(messages.datasourceConfluenceSearch),
description: formatMessage(messages.datasourceConfluenceSearchDescription),
isDisabledOffline: true,
categories: ['external-content', 'development'],
keywords: ['confluence'],
featured: true,
icon: function icon() {
return /*#__PURE__*/React.createElement(IconDatasourceConfluenceSearch, null);
},
action: function action(insert) {
var tr = insert(undefined);
showDatasourceModal('confluence-search')(tr);
return tr;
}
});
}
return quickInsertArray;
}
}
};
};