@atlaskit/editor-plugin-card
Version:
Card plugin for @atlaskit/editor-core
303 lines (297 loc) • 17.5 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createPlugin = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
var _rafSchd = _interopRequireDefault(require("raf-schd"));
var _reactNodeView = require("@atlaskit/editor-common/react-node-view");
var _safePlugin = require("@atlaskit/editor-common/safe-plugin");
var _styles = require("@atlaskit/editor-common/styles");
var _state = require("@atlaskit/editor-prosemirror/state");
var _utils = require("@atlaskit/editor-prosemirror/utils");
var _linkingCommon = require("@atlaskit/linking-common");
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
var _inlineCard = require("../nodeviews/inlineCard");
var _lazyBlockCard = require("../nodeviews/lazy-block-card");
var _lazyEmbedCard = require("../nodeviews/lazy-embed-card");
var _lazyInlineCard = require("../nodeviews/lazy-inline-card");
var _eventsFromTr = require("../ui/analytics/events-from-tr");
var _utils2 = require("../ui/LayoutButton/utils");
var _localStorage = require("../ui/local-storage");
var _actions = require("./actions");
var _pluginKey = require("./plugin-key");
var _reducers = _interopRequireDefault(require("./reducers"));
var _resolve = require("./util/resolve");
var _state2 = require("./util/state");
var _utils3 = require("./utils");
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; }
var LOCAL_STORAGE_DISCOVERY_KEY_SMART_LINK = 'smart-link-upgrade-pulse';
// Only the first resolved inline smart link is needed for PO spotlight targeting
var MAX_RESOLVED_INLINE_SMART_LINKS = 1;
var handleAwarenessOverlay = function handleAwarenessOverlay(view) {
var currentState = (0, _state2.getPluginState)(view.state);
var overlayCandidatePos = currentState === null || currentState === void 0 ? void 0 : currentState.overlayCandidatePosition;
if (overlayCandidatePos) {
var _currentState$removeO;
(_currentState$removeO = currentState.removeOverlay) === null || _currentState$removeO === void 0 || _currentState$removeO.call(currentState);
var tr = view.state.tr;
(0, _actions.clearOverlayCandidate)(tr);
view.dispatch(tr);
}
};
var createPlugin = exports.createPlugin = function createPlugin(options, pluginInjectionApi) {
return function (pmPluginFactoryParams) {
var editorAppearance = options.editorAppearance,
allowResizing = options.allowResizing,
useAlternativePreloader = options.useAlternativePreloader,
fullWidthMode = options.fullWidthMode,
actionOptions = options.actionOptions,
cardPluginEvents = options.cardPluginEvents,
showUpgradeDiscoverability = options.showUpgradeDiscoverability,
allowEmbeds = options.allowEmbeds,
allowBlockCards = options.allowBlockCards,
onClickCallback = options.onClickCallback,
isPageSSRed = options.isPageSSRed,
provider = options.provider,
CompetitorPrompt = options.CompetitorPrompt,
embedCardTransformers = options.embedCardTransformers;
var enableInlineUpgradeFeatures = !!showUpgradeDiscoverability;
var inlineCardViewProducer = (0, _reactNodeView.getInlineNodeViewProducer)({
pmPluginFactoryParams: pmPluginFactoryParams,
Component: _inlineCard.InlineCardNodeView,
extraComponentProps: {
useAlternativePreloader: useAlternativePreloader,
actionOptions: actionOptions,
enableInlineUpgradeFeatures: enableInlineUpgradeFeatures,
allowEmbeds: allowEmbeds,
allowBlockCards: allowBlockCards,
pluginInjectionApi: pluginInjectionApi,
onClickCallback: onClickCallback,
isPageSSRed: isPageSSRed,
provider: provider,
CompetitorPrompt: CompetitorPrompt
}
});
return new _safePlugin.SafePlugin({
state: {
init: function init() {
return {
requests: [],
provider: null,
cards: [],
datasourceStash: {},
resolvedToolbarAttributesByUrl: {},
showLinkingToolbar: false,
smartLinkEvents: undefined,
editorAppearance: editorAppearance,
embedCardTransformers: embedCardTransformers,
showDatasourceModal: false,
datasourceModalType: undefined,
datasourceTableRef: undefined,
layout: undefined
};
},
apply: function apply(tr, pluginState, prevEditorState) {
var _pluginState$requests, _pluginState$requests2;
// Update all the positions of outstanding requests and
// cards in the plugin state.
var pluginStateWithUpdatedPos = (0, _state2.getPluginStateWithUpdatedPos)(pluginState, tr);
// apply any actions
var meta = tr.getMeta(_pluginKey.pluginKey);
if (cardPluginEvents) {
var events = (0, _eventsFromTr.eventsFromTransaction)(tr, prevEditorState);
cardPluginEvents.push.apply(cardPluginEvents, (0, _toConsumableArray2.default)(events));
}
if (!meta) {
if (pluginState.datasourceTableRef) {
if (!(tr.selection instanceof _state.NodeSelection) || !tr.selection.node.attrs.datasource) {
// disable resize button when switching from datasource to block card
return _objectSpread(_objectSpread({}, pluginStateWithUpdatedPos), {}, {
datasourceTableRef: undefined
});
}
}
}
if (!meta) {
return pluginStateWithUpdatedPos;
}
var newState = (0, _reducers.default)(pluginStateWithUpdatedPos, meta);
// Track the first resolved inline smart link for PO spotlight DOM targeting
if (meta.type === 'RESOLVE' && pluginState !== null && pluginState !== void 0 && (_pluginState$requests = pluginState.requests) !== null && _pluginState$requests !== void 0 && _pluginState$requests.length && (0, _platformFeatureFlags.fg)('cc_dnd_smart_link_changeboard_po_template_gate')) {
var resolvedRequest = pluginState.requests.find(function (req) {
return req.url === meta.url;
});
if ((resolvedRequest === null || resolvedRequest === void 0 ? void 0 : resolvedRequest.appearance) === 'inline') {
var _newState$resolvedInl, _newState$resolvedInl2;
if (((_newState$resolvedInl = (_newState$resolvedInl2 = newState.resolvedInlineSmartLinks) === null || _newState$resolvedInl2 === void 0 ? void 0 : _newState$resolvedInl2.length) !== null && _newState$resolvedInl !== void 0 ? _newState$resolvedInl : 0) < MAX_RESOLVED_INLINE_SMART_LINKS) {
var _newState$resolvedInl3;
newState.resolvedInlineSmartLinks = [].concat((0, _toConsumableArray2.default)((_newState$resolvedInl3 = newState.resolvedInlineSmartLinks) !== null && _newState$resolvedInl3 !== void 0 ? _newState$resolvedInl3 : []), [{
pos: resolvedRequest.pos,
url: resolvedRequest.url,
source: resolvedRequest.source
}]);
}
}
}
if (!enableInlineUpgradeFeatures) {
return newState;
}
// the code below is related to the "Inline Switcher" project, for more information pls see EDM-7984
var isSingleInlineLink = (pluginState === null || pluginState === void 0 || (_pluginState$requests2 = pluginState.requests) === null || _pluginState$requests2 === void 0 ? void 0 : _pluginState$requests2.length) === 1 && pluginState.requests[0].appearance === 'inline';
var isSmartLinkPulseDiscovered = (0, _localStorage.isLocalStorageKeyDiscovered)(LOCAL_STORAGE_DISCOVERY_KEY_SMART_LINK);
if (meta.type !== 'RESOLVE' || !isSingleInlineLink) {
return newState;
}
var linkPosition = pluginState.requests[0].pos;
var canBeUpgradedToBlock = allowBlockCards && (0, _utils3.isBlockSupportedAtPosition)(linkPosition, prevEditorState, 'inline');
var canBeUpgradedToEmbed = allowEmbeds && (0, _utils3.isEmbedSupportedAtPosition)(linkPosition, prevEditorState, 'inline');
if (canBeUpgradedToBlock || canBeUpgradedToEmbed) {
newState.overlayCandidatePosition = linkPosition;
}
if (!isSmartLinkPulseDiscovered && canBeUpgradedToEmbed) {
newState.inlineCardAwarenessCandidatePosition = linkPosition;
}
return newState;
}
},
filterTransaction: function filterTransaction(tr) {
var isOutsideClicked = tr.getMeta('outsideProsemirrorEditorClicked');
if (isOutsideClicked) {
var isInlineEditingActive = document.getElementById('sllv-active-inline-edit');
if (isInlineEditingActive) {
return false;
}
}
return true;
},
view: function view(_view) {
var domAtPos = _view.domAtPos.bind(_view);
var rafCancellationCallbacks = [];
if (options.provider) {
(0, _resolve.handleProvider)('cardProvider', options.provider, _view);
}
return {
update: function update(view, prevState) {
var _selection$node;
var currentState = (0, _state2.getPluginState)(view.state);
var oldState = (0, _state2.getPluginState)(prevState);
var state = view.state,
dispatch = view.dispatch;
var selection = state.selection,
tr = state.tr,
schema = state.schema;
var isBlockCardSelected = selection instanceof _state.NodeSelection && ((_selection$node = selection.node) === null || _selection$node === void 0 ? void 0 : _selection$node.type) === schema.nodes.blockCard;
if (isBlockCardSelected) {
var _findDomRefAtPos, _node$attrs;
// Ignored via go/ees005
// eslint-disable-next-line @atlaskit/editor/no-as-casting
var datasourceTableRef = // Ignored via go/ees005
// eslint-disable-next-line @atlaskit/editor/no-as-casting
(_findDomRefAtPos = (0, _utils.findDomRefAtPos)(selection.from, domAtPos)) === null || _findDomRefAtPos === void 0 ? void 0 : _findDomRefAtPos.querySelector(".".concat(_styles.DATASOURCE_INNER_CONTAINER_CLASSNAME));
var node = selection.node;
var isDatasource = !!(node !== null && node !== void 0 && (_node$attrs = node.attrs) !== null && _node$attrs !== void 0 && _node$attrs.datasource);
var shouldUpdateTableRef = datasourceTableRef && (currentState === null || currentState === void 0 ? void 0 : currentState.datasourceTableRef) !== datasourceTableRef;
if (isDatasource && shouldUpdateTableRef) {
// since we use the plugin state, which is a shared state, we need to update the datasourceTableRef, layout on each selection
var layout = (0, _utils2.isDatasourceTableLayout)(node.attrs.layout) ? node.attrs.layout : _linkingCommon.DATASOURCE_DEFAULT_LAYOUT;
var isNested = selection.$anchor.depth > 0;
// we want to disable resize button when datasource table is nested by not setting then datasourceTableRef on selection
if (!isNested) {
// we use cardAction to set the same meta, hence, we will need to combine both layout+datasourceTableRef in one transaction
dispatch((0, _actions.setCardLayoutAndDatasourceTableRef)({
datasourceTableRef: datasourceTableRef,
layout: layout
})(tr));
}
}
} else {
if (currentState !== null && currentState !== void 0 && currentState.datasourceTableRef) {
dispatch((0, _actions.setDatasourceTableRef)(undefined)(tr));
}
}
if (currentState && currentState.provider) {
// Find requests in this state that weren't in the old one.
var newRequests = (0, _state2.getNewRequests)(oldState, currentState);
// Ask the CardProvider to resolve all new requests.
var _provider = currentState.provider;
newRequests.forEach(function (request) {
/**
* Queue each asynchronous resolve request on separate frames.
* ---
* NB: The promise for each request is queued to take place on separate animation frames. This avoids
* the scenario debugged and discovered in EDM-668, wherein the queuing of too many promises in quick succession
* leads to the browser's macrotask queue being overwhelmed, locking interactivity of the browser tab.
* By using this approach, the browser is free to schedule the resolution of the promises below in between rendering/network/
* other tasks as per common implementations of the JavaScript event loop in browsers.
*/
var invoke = (0, _rafSchd.default)(function () {
var _pluginInjectionApi$a, _pluginInjectionApi$a2, _pluginInjectionApi$a3, _currentState$embedCa;
return (0, _resolve.resolveWithProvider)(view, _provider, request, options, pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$a = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a === void 0 ? void 0 : _pluginInjectionApi$a.actions, (_pluginInjectionApi$a2 = pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$a3 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a3 === void 0 || (_pluginInjectionApi$a3 = _pluginInjectionApi$a3.sharedState.currentState()) === null || _pluginInjectionApi$a3 === void 0 ? void 0 : _pluginInjectionApi$a3.createAnalyticsEvent) !== null && _pluginInjectionApi$a2 !== void 0 ? _pluginInjectionApi$a2 : undefined, currentState === null || currentState === void 0 || (_currentState$embedCa = currentState.embedCardTransformers) === null || _currentState$embedCa === void 0 ? void 0 : _currentState$embedCa.embedCardNodeTransformer);
});
rafCancellationCallbacks.push(invoke.cancel);
invoke();
});
}
/**
* If there have been any events queued, flush them
* so subscribers can now be notified and dispatch
* analytics events
*/
cardPluginEvents === null || cardPluginEvents === void 0 || cardPluginEvents.flush();
},
destroy: function destroy() {
// Cancel any outstanding raf callbacks.
rafCancellationCallbacks.forEach(function (cancellationCallback) {
return cancellationCallback();
});
}
};
},
props: _objectSpread({
nodeViews: {
inlineCard: (0, _lazyInlineCard.lazyInlineCardView)({
inlineCardViewProducer: inlineCardViewProducer,
isPageSSRed: isPageSSRed
// no need provider here, it's in the inlineCardViewProducer.extraComponentProps
}),
blockCard: (0, _lazyBlockCard.lazyBlockCardView)({
pmPluginFactoryParams: pmPluginFactoryParams,
actionOptions: actionOptions,
pluginInjectionApi: pluginInjectionApi,
onClickCallback: onClickCallback,
allowDatasource: options.allowDatasource,
inlineCardViewProducer: inlineCardViewProducer,
isPageSSRed: isPageSSRed,
provider: provider,
CompetitorPrompt: options.CompetitorPrompt
}),
embedCard: (0, _lazyEmbedCard.lazyEmbedCardView)({
allowResizing: allowResizing,
fullWidthMode: fullWidthMode,
pmPluginFactoryParams: pmPluginFactoryParams,
pluginInjectionApi: pluginInjectionApi,
actionOptions: actionOptions,
onClickCallback: options.onClickCallback,
isPageSSRed: isPageSSRed,
provider: provider,
CompetitorPrompt: options.CompetitorPrompt
})
}
}, enableInlineUpgradeFeatures && {
handleKeyDown: function handleKeyDown(view) {
handleAwarenessOverlay(view);
return false;
},
handleClick: function handleClick(view) {
handleAwarenessOverlay(view);
return false;
}
}),
key: _pluginKey.pluginKey
});
};
};