UNPKG

@atlaskit/editor-plugin-card

Version:

Card plugin for @atlaskit/editor-core

134 lines (129 loc) • 4.42 kB
/** * Bounds the per-editor work, since every tracked link is mapped through every transaction. The * first pasted links win. Sized well above a typical paste; overflow is counted, not silent. */ export const MAX_PASTED_INLINE_LINKS = 100; const isSameLink = (a, b) => a.pos === b.pos && a.url === b.url; /** * Map candidates through a transaction. Anything that was deleted, replaced by another node or * given a different URL stops being a candidate. Returns the same array when nothing changed. */ export const mapPastedInlineLinks = (links, tr) => { if (tr.getMeta('replaceDocument')) { return links.length ? [] : links; } if (!tr.docChanged || !links.length) { return links; } let changed = false; const mapped = []; for (const link of links) { const result = tr.mapping.mapResult(link.pos, 1); const node = result.deleted ? null : tr.doc.nodeAt(result.pos); if ((node === null || node === void 0 ? void 0 : node.type.name) !== 'inlineCard' || node.attrs.url !== link.url) { changed = true; continue; } if (result.pos !== link.pos) { changed = true; mapped.push({ pos: result.pos, url: link.url }); } else { mapped.push(link); } } return changed ? mapped : links; }; /** Append new candidates, skipping duplicates and counting any dropped by the cap. */ export const addPastedInlineLinksWithOverflow = (links, additions) => { let next = links; let dropped = 0; for (const addition of additions) { if (next.some(link => isSameLink(link, addition))) { continue; } if (next.length >= MAX_PASTED_INLINE_LINKS) { dropped += 1; } else { next = [...next, addition]; } } return { dropped, links: next }; }; /** Append new candidates, skipping duplicates and anything beyond the cap. */ export const addPastedInlineLinks = (links, additions) => addPastedInlineLinksWithOverflow(links, additions).links; export const removePastedInlineLink = (links, target) => { const next = links.filter(link => !isSameLink(link, target)); return next.length === links.length ? links : next; }; /** Inline cards inserted by a paste transaction (copy and paste of existing cards). */ export const getPastedInlineCards = tr => { if (!tr.docChanged || tr.getMeta('uiEvent') !== 'paste' || tr.getMeta('isRemote')) { return []; } const found = []; tr.mapping.maps.forEach((stepMap, index) => { const later = tr.mapping.slice(index + 1); stepMap.forEach((_oldStart, _oldEnd, newStart, newEnd) => { tr.doc.nodesBetween(later.map(newStart, -1), later.map(newEnd, 1), (node, pos) => { if (node.type.name === 'inlineCard' && typeof node.attrs.url === 'string') { found.push({ pos, url: node.attrs.url }); } }); }); }); return found; }; /** Inline cards created from clipboard URLs that just resolved in this transaction. */ export const getResolvedPastedInlineCards = (tr, requests, resolvedUrl, clipboardSource) => { if (tr.getMeta('isRemote')) { return []; } const found = []; for (const request of requests) { if (request.url !== resolvedUrl || request.appearance !== 'inline' || request.source !== clipboardSource) { continue; } const pos = tr.mapping.map(request.pos, -1); if (pos < 0 || pos >= tr.doc.content.size) { continue; } const node = tr.doc.nodeAt(pos); if ((node === null || node === void 0 ? void 0 : node.type.name) === 'inlineCard' && node.attrs.url === request.url) { found.push({ pos, url: request.url }); } } return found; }; /** Add candidates unless tracking is off or a link has already been displayed. */ export const withPastedInlineLinks = (state, additions) => { var _state$pastedInlineLi; if (!state.pastedInlineLinks || state.pastedInlineLinkShown || !additions.length) { return state; } const { dropped, links: next } = addPastedInlineLinksWithOverflow(state.pastedInlineLinks, additions); if (next === state.pastedInlineLinks && !dropped) { return state; } return { ...state, pastedInlineLinks: next, ...(dropped ? { pastedInlineLinksDropped: ((_state$pastedInlineLi = state.pastedInlineLinksDropped) !== null && _state$pastedInlineLi !== void 0 ? _state$pastedInlineLi : 0) + dropped } : {}) }; };