@atlaskit/editor-plugin-paste
Version:
Paste plugin for @atlaskit/editor-core
416 lines (413 loc) • 20.7 kB
JavaScript
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
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 { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD, PasteContents, PasteTypes } from '@atlaskit/editor-common/analytics';
import { getLinkDomain, mapSlice } from '@atlaskit/editor-common/utils';
import { findParentNode } from '@atlaskit/editor-prosemirror/utils';
import { getPasteSource } from './util';
import { handleCodeBlock, handleExpandPaste, handleMarkdown, handleMediaSingle, handlePasteAsPlainText, handlePasteIntoCaption, handlePasteIntoTaskOrDecisionOrPanel, handlePasteLinkOnSelectedText, handlePasteNonNestableBlockNodesIntoList, handlePastePanelOrDecisionContentIntoList, handlePastePreservingMarks, handleRichText, handleSelectedTable, handleNestedTablePaste } from './util/handlers';
var contentToPasteContent = {
url: PasteContents.url,
paragraph: PasteContents.text,
bulletList: PasteContents.bulletList,
orderedList: PasteContents.orderedList,
heading: PasteContents.heading,
blockquote: PasteContents.blockquote,
codeBlock: PasteContents.codeBlock,
panel: PasteContents.panel,
rule: PasteContents.rule,
mediaSingle: PasteContents.mediaSingle,
mediaCard: PasteContents.mediaCard,
mediaGroup: PasteContents.mediaGroup,
table: PasteContents.table,
tableCells: PasteContents.tableCells,
tableHeader: PasteContents.tableHeader,
tableRow: PasteContents.tableRow,
decisionList: PasteContents.decisionList,
decisionItem: PasteContents.decisionItem,
taskList: PasteContents.taskItem,
extension: PasteContents.extension,
bodiedExtension: PasteContents.bodiedExtension,
blockCard: PasteContents.blockCard,
layoutSection: PasteContents.layoutSection
};
var nodeToActionSubjectId = {
blockquote: ACTION_SUBJECT_ID.PASTE_BLOCKQUOTE,
blockCard: ACTION_SUBJECT_ID.PASTE_BLOCK_CARD,
bodiedExtension: ACTION_SUBJECT_ID.PASTE_BODIED_EXTENSION,
bulletList: ACTION_SUBJECT_ID.PASTE_BULLET_LIST,
codeBlock: ACTION_SUBJECT_ID.PASTE_CODE_BLOCK,
decisionList: ACTION_SUBJECT_ID.PASTE_DECISION_LIST,
extension: ACTION_SUBJECT_ID.PASTE_EXTENSION,
heading: ACTION_SUBJECT_ID.PASTE_HEADING,
mediaGroup: ACTION_SUBJECT_ID.PASTE_MEDIA_GROUP,
mediaSingle: ACTION_SUBJECT_ID.PASTE_MEDIA_SINGLE,
orderedList: ACTION_SUBJECT_ID.PASTE_ORDERED_LIST,
panel: ACTION_SUBJECT_ID.PASTE_PANEL,
rule: ACTION_SUBJECT_ID.PASTE_RULE,
table: ACTION_SUBJECT_ID.PASTE_TABLE,
tableCell: ACTION_SUBJECT_ID.PASTE_TABLE_CELL,
tableHeader: ACTION_SUBJECT_ID.PASTE_TABLE_HEADER,
tableRow: ACTION_SUBJECT_ID.PASTE_TABLE_ROW,
taskList: ACTION_SUBJECT_ID.PASTE_TASK_LIST
};
export function getContent(_ref) {
var schema = _ref.schema,
slice = _ref.slice;
var paragraph = schema.nodes.paragraph,
link = schema.marks.link;
var nodeOrMarkName = new Set();
slice.content.forEach(function (node) {
if (node.type === paragraph && node.content.size === 0) {
// Skip empty paragraph
return;
}
if (node.type.name === 'text' && link.isInSet(node.marks)) {
nodeOrMarkName.add('url');
return;
}
// Check node contain link
if (node.type === paragraph && node.rangeHasMark(0, node.nodeSize - 2, link)) {
nodeOrMarkName.add('url');
return;
}
nodeOrMarkName.add(node.type.name);
});
if (nodeOrMarkName.size > 1) {
return PasteContents.mixed;
}
if (nodeOrMarkName.size === 0) {
return PasteContents.uncategorized;
}
var type = nodeOrMarkName.values().next().value;
// @ts-ignore - TS2538 TypeScript 5.9.2 upgrade
var pasteContent = contentToPasteContent[type];
return pasteContent ? pasteContent : PasteContents.uncategorized;
}
export function getMediaTraceId(slice) {
var traceId;
mapSlice(slice, function (node) {
if (node.type.name === 'media' || node.type.name === 'mediaInline') {
traceId = node.attrs.__mediaTraceId;
}
return node;
});
return traceId;
}
function getActionSubjectId(_ref2) {
var selection = _ref2.selection,
schema = _ref2.schema;
var _schema$nodes = schema.nodes,
paragraph = _schema$nodes.paragraph,
listItem = _schema$nodes.listItem,
taskItem = _schema$nodes.taskItem,
decisionItem = _schema$nodes.decisionItem;
var parent = findParentNode(function (node) {
if (node.type !== paragraph && node.type !== listItem && node.type !== taskItem && node.type !== decisionItem) {
return true;
}
return false;
})(selection);
if (!parent) {
return ACTION_SUBJECT_ID.PASTE_PARAGRAPH;
}
var parentType = parent.node.type;
var actionSubjectId = nodeToActionSubjectId[parentType.name];
return actionSubjectId ? actionSubjectId : ACTION_SUBJECT_ID.PASTE_PARAGRAPH;
}
function createPasteAsPlainPayload(actionSubjectId, text, linksInPasteCount) {
return {
action: ACTION.PASTED_AS_PLAIN,
actionSubject: ACTION_SUBJECT.DOCUMENT,
actionSubjectId: actionSubjectId,
eventType: EVENT_TYPE.TRACK,
attributes: {
inputMethod: INPUT_METHOD.KEYBOARD,
pasteSize: text.length,
linksInPasteCount: linksInPasteCount
}
};
}
function createPastePayload(actionSubjectId, attributes, linkDomain) {
return _objectSpread({
action: ACTION.PASTED,
actionSubject: ACTION_SUBJECT.DOCUMENT,
actionSubjectId: actionSubjectId,
eventType: EVENT_TYPE.TRACK,
attributes: _objectSpread({
inputMethod: INPUT_METHOD.KEYBOARD
}, attributes)
}, linkDomain && linkDomain.length > 0 ? {
nonPrivacySafeAttributes: {
linkDomain: linkDomain
}
} : {});
}
function createPasteAnalyticsPayloadBySelection(event, slice, pasteContext, pluginInjectionApi) {
return function (selection) {
var _pluginInjectionApi$m;
var text = event.clipboardData ? event.clipboardData.getData('text/plain') || event.clipboardData.getData('text/uri-list') : '';
var actionSubjectId = getActionSubjectId({
selection: selection,
schema: selection.$from.doc.type.schema
});
var pasteSize = slice.size;
var content = getContent({
schema: selection.$from.doc.type.schema,
slice: slice
});
var linkUrls = [];
var mediaTraceId = getMediaTraceId(slice);
// If we have a link among the pasted content, grab the
// domain and send it up with the analytics event
if (content === PasteContents.url || content === PasteContents.mixed) {
mapSlice(slice, function (node) {
var linkMark = node.marks.find(function (mark) {
return mark.type.name === 'link';
});
if (linkMark) {
linkUrls.push(linkMark.attrs.href);
}
return node;
});
}
if (pasteContext.asPlain) {
return createPasteAsPlainPayload(actionSubjectId, text, linkUrls.length);
}
var source = getPasteSource(event);
var mentionIds = [];
var mentionLocalIds = [];
slice.content.descendants(function (node) {
if (node.type.name === 'mention') {
mentionIds.push(node.attrs.id);
mentionLocalIds.push(node.attrs.localId);
}
});
if (pluginInjectionApi !== null && pluginInjectionApi !== void 0 && (_pluginInjectionApi$m = pluginInjectionApi.mention) !== null && _pluginInjectionApi$m !== void 0 && (_pluginInjectionApi$m = _pluginInjectionApi$m.actions) !== null && _pluginInjectionApi$m !== void 0 && _pluginInjectionApi$m.announceMentionsInsertion) {
var _pluginInjectionApi$m2;
var mentionsInserted = [];
slice.content.descendants(function (node) {
if (node.type.name === 'mention') {
mentionsInserted.push({
type: 'added',
id: node.attrs.id,
localId: node.attrs.localId,
method: 'pasted'
});
}
if (node.type.name === 'taskItem') {
node.content.forEach(function (nodeContent) {
if (nodeContent.type.name === 'mention') {
mentionsInserted.push({
type: 'added',
localId: nodeContent.attrs.localId,
id: nodeContent.attrs.id,
taskLocalId: node.attrs.localId,
method: 'pasted'
});
}
});
return false;
}
});
pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$m2 = pluginInjectionApi.mention) === null || _pluginInjectionApi$m2 === void 0 || (_pluginInjectionApi$m2 = _pluginInjectionApi$m2.actions) === null || _pluginInjectionApi$m2 === void 0 || _pluginInjectionApi$m2.announceMentionsInsertion(mentionsInserted);
}
if (pasteContext.type === PasteTypes.plain) {
return createPastePayload(actionSubjectId, {
pasteSize: text.length,
type: pasteContext.type,
content: PasteContents.text,
source: source,
hyperlinkPasteOnText: false,
linksInPasteCount: linkUrls.length,
mentionIds: mentionIds,
mentionLocalIds: mentionLocalIds,
pasteSplitList: pasteContext.pasteSplitList
});
}
var linkDomains = linkUrls.map(getLinkDomain);
return createPastePayload(actionSubjectId, {
type: pasteContext.type,
pasteSize: pasteSize,
content: content,
source: source,
hyperlinkPasteOnText: !!pasteContext.hyperlinkPasteOnText,
linksInPasteCount: linkUrls.length,
mediaTraceId: mediaTraceId,
mentionIds: mentionIds,
mentionLocalIds: mentionLocalIds,
pasteSplitList: pasteContext.pasteSplitList
}, linkDomains);
};
}
export function createPasteAnalyticsPayload(view, event, slice, pasteContext) {
return createPasteAnalyticsPayloadBySelection(event, slice, pasteContext)(view.state.selection);
}
// TODO: ED-6612 - We should not dispatch only analytics, it's preferred to wrap each command with his own analytics.
// However, handlers like handleMacroAutoConvert dispatch multiple time,
// so pasteCommandWithAnalytics is useless in this case.
export var sendPasteAnalyticsEvent = function sendPasteAnalyticsEvent(editorAnalyticsAPI) {
return function (view, event, slice, pasteContext) {
var tr = view.state.tr;
var payload = createPasteAnalyticsPayload(view, event, slice, pasteContext);
editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 || editorAnalyticsAPI.attachAnalyticsEvent(payload)(tr);
view.dispatch(tr);
};
};
export var handlePasteAsPlainTextWithAnalytics = function handlePasteAsPlainTextWithAnalytics(editorAnalyticsAPI) {
return function (view, event, slice) {
return injectAnalyticsPayloadBeforeCommand(editorAnalyticsAPI)(createPasteAnalyticsPayloadBySelection(event, slice, {
type: PasteTypes.plain,
asPlain: true
}))(handlePasteAsPlainText(slice, event, editorAnalyticsAPI));
};
};
export var handlePasteIntoTaskAndDecisionWithAnalytics = function handlePasteIntoTaskAndDecisionWithAnalytics(view, event, slice, type, pluginInjectionApi) {
var _pluginInjectionApi$a, _pluginInjectionApi$c;
return injectAnalyticsPayloadBeforeCommand(pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$a = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a === void 0 ? void 0 : _pluginInjectionApi$a.actions)(createPasteAnalyticsPayloadBySelection(event, slice, {
type: type
}))(handlePasteIntoTaskOrDecisionOrPanel(slice, pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$c = pluginInjectionApi.card) === null || _pluginInjectionApi$c === void 0 || (_pluginInjectionApi$c = _pluginInjectionApi$c.actions) === null || _pluginInjectionApi$c === void 0 ? void 0 : _pluginInjectionApi$c.queueCardsFromChangedTr));
};
export var handlePasteIntoCaptionWithAnalytics = function handlePasteIntoCaptionWithAnalytics(editorAnalyticsAPI) {
return function (view, event, slice, type) {
return injectAnalyticsPayloadBeforeCommand(editorAnalyticsAPI)(createPasteAnalyticsPayloadBySelection(event, slice, {
type: type
}))(handlePasteIntoCaption(slice));
};
};
export var handleCodeBlockWithAnalytics = function handleCodeBlockWithAnalytics(editorAnalyticsAPI) {
return function (view, event, slice, text) {
return injectAnalyticsPayloadBeforeCommand(editorAnalyticsAPI)(createPasteAnalyticsPayloadBySelection(event, slice, {
type: PasteTypes.plain
}))(handleCodeBlock(text));
};
};
export var handleMediaSingleWithAnalytics = function handleMediaSingleWithAnalytics(editorAnalyticsAPI) {
return function (view, event, slice, type, insertMediaAsMediaSingle) {
return injectAnalyticsPayloadBeforeCommand(editorAnalyticsAPI)(createPasteAnalyticsPayloadBySelection(event, slice, {
type: type
}))(handleMediaSingle(INPUT_METHOD.CLIPBOARD, insertMediaAsMediaSingle)(slice));
};
};
export var handlePastePreservingMarksWithAnalytics = function handlePastePreservingMarksWithAnalytics(view, event, slice, type, pluginInjectionApi) {
var _pluginInjectionApi$a2, _pluginInjectionApi$c2;
return injectAnalyticsPayloadBeforeCommand(pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$a2 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a2 === void 0 ? void 0 : _pluginInjectionApi$a2.actions)(createPasteAnalyticsPayloadBySelection(event, slice, {
type: type
}))(handlePastePreservingMarks(slice, pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$c2 = pluginInjectionApi.card) === null || _pluginInjectionApi$c2 === void 0 || (_pluginInjectionApi$c2 = _pluginInjectionApi$c2.actions) === null || _pluginInjectionApi$c2 === void 0 ? void 0 : _pluginInjectionApi$c2.queueCardsFromChangedTr));
};
export var handleMarkdownWithAnalytics = function handleMarkdownWithAnalytics(view, event, slice, pluginInjectionApi) {
var _pluginInjectionApi$a3, _pluginInjectionApi$c3;
return injectAnalyticsPayloadBeforeCommand(pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$a3 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a3 === void 0 ? void 0 : _pluginInjectionApi$a3.actions)(createPasteAnalyticsPayloadBySelection(event, slice, {
type: PasteTypes.markdown
}))(handleMarkdown(slice, pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$c3 = pluginInjectionApi.card) === null || _pluginInjectionApi$c3 === void 0 || (_pluginInjectionApi$c3 = _pluginInjectionApi$c3.actions) === null || _pluginInjectionApi$c3 === void 0 ? void 0 : _pluginInjectionApi$c3.queueCardsFromChangedTr));
};
export var handleRichTextWithAnalytics = function handleRichTextWithAnalytics(view, event, slice, pluginInjectionApi) {
var _pluginInjectionApi$a4, _pluginInjectionApi$c4;
return injectAnalyticsPayloadBeforeCommand(pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$a4 = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a4 === void 0 ? void 0 : _pluginInjectionApi$a4.actions)(createPasteAnalyticsPayloadBySelection(event, slice, {
type: PasteTypes.richText
}, pluginInjectionApi))(handleRichText(slice, pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$c4 = pluginInjectionApi.card) === null || _pluginInjectionApi$c4 === void 0 || (_pluginInjectionApi$c4 = _pluginInjectionApi$c4.actions) === null || _pluginInjectionApi$c4 === void 0 ? void 0 : _pluginInjectionApi$c4.queueCardsFromChangedTr));
};
var injectAnalyticsPayloadBeforeCommand = function injectAnalyticsPayloadBeforeCommand(editorAnalyticsAPI) {
return function (createPayloadByTransaction) {
return function (mainCommand) {
return function (state, dispatch, view) {
var originalTransaction = state.tr;
var fakeDispatch = function fakeDispatch(tr) {
originalTransaction = tr;
};
var result = mainCommand(state, fakeDispatch, view);
if (!result) {
return false;
}
if (dispatch && originalTransaction.docChanged) {
// it needs to know the selection before the changes
var payload = createPayloadByTransaction(state.selection);
editorAnalyticsAPI === null || editorAnalyticsAPI === void 0 || editorAnalyticsAPI.attachAnalyticsEvent(payload)(originalTransaction);
dispatch(originalTransaction);
}
return true;
};
};
};
};
export var handlePastePanelOrDecisionIntoListWithAnalytics = function handlePastePanelOrDecisionIntoListWithAnalytics(editorAnalyticsAPI) {
return function (view, event, slice, findRootParentListNode) {
return injectAnalyticsPayloadBeforeCommand(editorAnalyticsAPI)(createPasteAnalyticsPayloadBySelection(event, slice, {
type: PasteTypes.richText
}))(handlePastePanelOrDecisionContentIntoList(slice, findRootParentListNode));
};
};
export var handlePasteNonNestableBlockNodesIntoListWithAnalytics = function handlePasteNonNestableBlockNodesIntoListWithAnalytics(editorAnalyticsAPI) {
return function (view, event, slice) {
return injectAnalyticsPayloadBeforeCommand(editorAnalyticsAPI)(createPasteAnalyticsPayloadBySelection(event, slice, {
type: PasteTypes.richText,
pasteSplitList: true
}))(handlePasteNonNestableBlockNodesIntoList(slice));
};
};
export var handleExpandWithAnalytics = function handleExpandWithAnalytics(editorAnalyticsAPI) {
return function (view, event, slice) {
return injectAnalyticsPayloadBeforeCommand(editorAnalyticsAPI)(createPasteAnalyticsPayloadBySelection(event, slice, {
type: PasteTypes.richText,
pasteSplitList: true
}))(handleExpandPaste(slice));
};
};
export var handleNestedTablePasteWithAnalytics = function handleNestedTablePasteWithAnalytics(editorAnalyticsAPI, isNestingTablesSupported) {
return function (view, event, slice) {
return injectAnalyticsPayloadBeforeCommand(editorAnalyticsAPI)(createPasteAnalyticsPayloadBySelection(event, slice, {
type: PasteTypes.richText,
pasteSplitList: true
}))(handleNestedTablePaste(slice, isNestingTablesSupported));
};
};
export var handleSelectedTableWithAnalytics = function handleSelectedTableWithAnalytics(editorAnalyticsAPI) {
return function (view, event, slice) {
return injectAnalyticsPayloadBeforeCommand(editorAnalyticsAPI)(createPasteAnalyticsPayloadBySelection(event, slice, {
type: PasteTypes.richText
}))(handleSelectedTable(editorAnalyticsAPI)(slice));
};
};
export var handlePasteLinkOnSelectedTextWithAnalytics = function handlePasteLinkOnSelectedTextWithAnalytics(editorAnalyticsAPI) {
return function (view, event, slice, type) {
return injectAnalyticsPayloadBeforeCommand(editorAnalyticsAPI)(createPasteAnalyticsPayloadBySelection(event, slice, {
type: type,
hyperlinkPasteOnText: true
}))(handlePasteLinkOnSelectedText(slice));
};
};
export var createPasteMeasurePayload = function createPasteMeasurePayload(_ref3) {
var view = _ref3.view,
duration = _ref3.duration,
content = _ref3.content,
distortedDuration = _ref3.distortedDuration;
var pasteIntoNode = getActionSubjectId({
selection: view.state.selection,
schema: view.state.schema
});
return {
action: ACTION.PASTED_TIMED,
actionSubject: ACTION_SUBJECT.EDITOR,
eventType: EVENT_TYPE.OPERATIONAL,
attributes: {
pasteIntoNode: pasteIntoNode,
content: content,
time: duration,
distortedDuration: distortedDuration
}
};
};
var _getContentNodeTypes = function getContentNodeTypes(content) {
var nodeTypes = new Set();
if (content.size) {
content.forEach(function (node) {
if (node.content && node.content.size) {
nodeTypes = new Set([].concat(_toConsumableArray(nodeTypes), _toConsumableArray(_getContentNodeTypes(node.content))));
}
nodeTypes.add(node.type.name);
});
}
return Array.from(nodeTypes);
};
export { _getContentNodeTypes as getContentNodeTypes };