UNPKG

@atlaskit/editor-plugin-card

Version:

Card plugin for @atlaskit/editor-core

132 lines (129 loc) 5.02 kB
import { isSafeUrl } from '@atlaskit/adf-schema/url'; import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics'; import { NATIVE_EMBED_EXTENSION_KEY, NATIVE_EMBED_EXTENSION_TYPE } from '@atlaskit/editor-common/extensions'; /** * A native embed is an `extension` node that `editor-plugin-native-embeds` builds from * embedCard ADF (via the `embedCardNodeTransformer` registered on the card plugin). * A 1P link that resolves to an embed appearance — a whiteboard, page, slide or * database — therefore lands in the document as one of these rather than as an * `embedCard`, so card node types alone are not enough to describe what was pasted. * * `editor-plugin-native-embeds` owns the equivalent predicate, but the card plugin * cannot depend on it: that would close the cycle * editor-plugin-card -> editor-plugin-native-embeds -> editor-plugin-card. */ export const isNativeEmbedNode = node => { var _node$attrs, _node$attrs2; return (node === null || node === void 0 ? void 0 : node.type.name) === 'extension' && ((_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.extensionType) === NATIVE_EMBED_EXTENSION_TYPE && typeof ((_node$attrs2 = node.attrs) === null || _node$attrs2 === void 0 ? void 0 : _node$attrs2.extensionKey) === 'string' && node.attrs.extensionKey.includes(NATIVE_EMBED_EXTENSION_KEY); }; /** * Reads the embedded URL from a native embed node. The URL is stored in * `parameters.macroParams`, where values may be wrapped (`{ value }`) or bare, and is * mirrored in `parameters.macroMetadata` so it survives a revert to a card. */ export const getNativeEmbedUrl = node => { var _node$attrs3, _parameters$macroPara, _ref, _parameters$macroMeta; const parameters = (_node$attrs3 = node.attrs) === null || _node$attrs3 === void 0 ? void 0 : _node$attrs3.parameters; const macroParamUrl = parameters === null || parameters === void 0 ? void 0 : (_parameters$macroPara = parameters.macroParams) === null || _parameters$macroPara === void 0 ? void 0 : _parameters$macroPara.url; const url = (_ref = macroParamUrl && typeof macroParamUrl === 'object' && 'value' in macroParamUrl ? macroParamUrl.value : macroParamUrl) !== null && _ref !== void 0 ? _ref : parameters === null || parameters === void 0 ? void 0 : (_parameters$macroMeta = parameters.macroMetadata) === null || _parameters$macroMeta === void 0 ? void 0 : _parameters$macroMeta.url; return typeof url === 'string' ? url : undefined; }; const canReplaceNodeWith = (view, pos, nodeType) => { if (!nodeType) { return false; } try { const $pos = view.state.doc.resolve(pos); const index = $pos.index(); return $pos.parent.canReplaceWith(index, index + 1, nodeType); } catch { return false; } }; /** * Whether a native embed at `pos` can be replaced by the given appearance. `url` and * `inline` both become a paragraph, `block` becomes a blockCard. */ export const isNativeEmbedAppearanceSupported = ({ editorView, pos, appearance }) => { const { paragraph, inlineCard, blockCard } = editorView.state.schema.nodes; if (appearance === 'block') { return canReplaceNodeWith(editorView, pos, blockCard); } if (appearance === 'inline' && !inlineCard) { return false; } if (appearance === 'url' && !editorView.state.schema.marks.link) { return false; } return canReplaceNodeWith(editorView, pos, paragraph); }; /** * Replaces a native embed with a link, an inline card or a block card. Mirrors * `setNativeEmbedAppearance` in `editor-plugin-native-embeds`, which cannot be reused * here because of the package cycle described on `isNativeEmbedNode`. */ export const changeNativeEmbedAppearance = ({ editorView, pos, node, appearance, url, editorAnalyticsApi }) => { const { state, dispatch } = editorView; const { paragraph, inlineCard, blockCard } = state.schema.nodes; const { link } = state.schema.marks; if (!isSafeUrl(url)) { return false; } let content; if (appearance === 'url' && link && paragraph) { content = paragraph.create(null, state.schema.text(url, [link.create({ href: url })])); } else if (appearance === 'inline' && inlineCard && paragraph) { content = paragraph.create(null, inlineCard.create({ url })); } else if (appearance === 'block' && blockCard) { content = blockCard.create({ url }); } if (!content) { return false; } try { const tr = state.tr.replaceWith(pos, pos + node.nodeSize, content).scrollIntoView(); editorAnalyticsApi === null || editorAnalyticsApi === void 0 ? void 0 : editorAnalyticsApi.attachAnalyticsEvent({ action: ACTION.CHANGED_TYPE, actionSubject: ACTION_SUBJECT.NATIVE_EMBED, eventType: EVENT_TYPE.TRACK, attributes: { newType: appearance, previousType: 'nativeEmbed' } })(tr); dispatch(tr); return true; } catch { return false; } };