UNPKG

@atlaskit/editor-common

Version:

A package that contains common classes and components for editor and renderer

237 lines (226 loc) • 10.3 kB
export const isReferencedSource = (state, node) => { var _node$attrs, _node$marks, _node$marks$find, _node$marks$find$attr; if (!node) { return false; } let found = false; // Handle nodes having 2 uuids. They could have a localId or a fragment. Regardless this needs // to check if either id is used by a data consumer. const localIds = new Set([(_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.localId, (_node$marks = node.marks) === null || _node$marks === void 0 ? void 0 : (_node$marks$find = _node$marks.find(mark => mark.type === state.schema.marks.fragment)) === null || _node$marks$find === void 0 ? void 0 : (_node$marks$find$attr = _node$marks$find.attrs) === null || _node$marks$find$attr === void 0 ? void 0 : _node$marks$find$attr.localId].filter(Boolean)); // If there are no uuids on the node then it's not possible for it to be referenced anywhere. if (!localIds.size) { return false; } state.doc.descendants(node => { var _dataConsumer$attrs$s, _dataConsumer$attrs$s2; if (found) { return false; } const dataConsumer = node.marks.find(mark => mark.type === state.schema.marks.dataConsumer); if (!dataConsumer) { return true; } found = (_dataConsumer$attrs$s = (_dataConsumer$attrs$s2 = dataConsumer.attrs.sources) === null || _dataConsumer$attrs$s2 === void 0 ? void 0 : _dataConsumer$attrs$s2.some(src => localIds.has(src))) !== null && _dataConsumer$attrs$s !== void 0 ? _dataConsumer$attrs$s : false; return !found; }); return found; }; export const getConnections = state => { const result = {}; const { doc, schema } = state; // Keeps a map of all raw ids -> to their preferred normalised varient. A node with both fragmentMark & localId will // have both ids mapped here to the same normalized version. const normalizedIds = new Map(); const dataConsumerSources = new Map(); // Perform a prelim scan creating the initial id to connection link mappings. // This will also save a list of data sources consumed per node. doc.descendants((node, pos) => { var _node$attrs2, _fragmentMark$attrs$l, _fragmentMark, _fragmentMark$attrs, _node$attrs3, _fragmentMark2, _fragmentMark2$attrs, _node$attrs4, _fragmentMark3, _fragmentMark3$attrs; let dataConsumer; let fragmentMark; node.marks.some(mark => { if (mark.type === schema.marks.dataConsumer) { dataConsumer = mark; } else if (mark.type === schema.marks.fragment) { fragmentMark = mark; } // Stop searching marks if we've found both consumer and fragment. return !!dataConsumer && !!fragmentMark; }); // If node cannot be referenced by any means then abort. if (!fragmentMark && !((_node$attrs2 = node.attrs) !== null && _node$attrs2 !== void 0 && _node$attrs2.localId)) { return true; } const normalizedId = (_fragmentMark$attrs$l = (_fragmentMark = fragmentMark) === null || _fragmentMark === void 0 ? void 0 : (_fragmentMark$attrs = _fragmentMark.attrs) === null || _fragmentMark$attrs === void 0 ? void 0 : _fragmentMark$attrs.localId) !== null && _fragmentMark$attrs$l !== void 0 ? _fragmentMark$attrs$l : (_node$attrs3 = node.attrs) === null || _node$attrs3 === void 0 ? void 0 : _node$attrs3.localId; if (!normalizedId) { return true; } if (!!((_fragmentMark2 = fragmentMark) !== null && _fragmentMark2 !== void 0 && (_fragmentMark2$attrs = _fragmentMark2.attrs) !== null && _fragmentMark2$attrs !== void 0 && _fragmentMark2$attrs.localId)) { normalizedIds.set(fragmentMark.attrs.localId, normalizedId); } if (!!((_node$attrs4 = node.attrs) !== null && _node$attrs4 !== void 0 && _node$attrs4.localId)) { normalizedIds.set(node.attrs.localId, normalizedId); } if (!!result[normalizedId]) { // Duplicate ID has been found, we'll care about the first one for now. return true; } result[normalizedId] = { localId: normalizedId, name: (_fragmentMark3 = fragmentMark) === null || _fragmentMark3 === void 0 ? void 0 : (_fragmentMark3$attrs = _fragmentMark3.attrs) === null || _fragmentMark3$attrs === void 0 ? void 0 : _fragmentMark3$attrs.name, node, pos, targets: [] }; if (!!dataConsumer && dataConsumer.attrs.sources.length) { dataConsumerSources.set(normalizedId, dataConsumer.attrs.sources); } // Do not descend into children of a node which has a dataConsumer attached. This assumes all children of the node cannot // be referenced by another node. return !dataConsumer; }); // This 2nd-pass only looks at the consumer sources and updates all connections with the correct id refs. for (const [localId, sources] of dataConsumerSources) { // This is a ref to the node (connection link) which contains the consumer. sources.forEach(src => { var _normalizedIds$get; const normalizedId = (_normalizedIds$get = normalizedIds.get(src)) !== null && _normalizedIds$get !== void 0 ? _normalizedIds$get : src; const srcLink = result[normalizedId]; if (srcLink && normalizedId !== localId) { srcLink.targets.push(localId); } }); } return result; }; export const removeConnectedNodes = (state, node) => { if (!node) { return state.tr; } const selectedLocalIds = getSelectedLocalIds(state, node); const allNodes = getConnections(state); const idsToBeDeleted = getIdsToBeDeleted(selectedLocalIds, allNodes); if (!(idsToBeDeleted !== null && idsToBeDeleted !== void 0 && idsToBeDeleted.length)) { return state.tr; } const { tr } = state; let newTr = tr; idsToBeDeleted.forEach(id => { if (!allNodes[id]) { return; } const { node, pos } = allNodes[id]; newTr = newTr.delete(newTr.mapping.map(pos), newTr.mapping.map(node.nodeSize + pos)); }); return newTr; }; // find all ids need to be remove connected to selected extension const getIdsToBeDeleted = (selectedIds, allNodes) => { if (!selectedIds.size) { return []; } let searchSet = [...selectedIds]; const deletedIds = new Set(); while (searchSet.length) { const id = searchSet.pop(); if (allNodes[id]) { var _allNodes$id$targets$, _allNodes$id; deletedIds.add(allNodes[id].localId); searchSet = searchSet.concat((_allNodes$id$targets$ = (_allNodes$id = allNodes[id]) === null || _allNodes$id === void 0 ? void 0 : _allNodes$id.targets.filter(targetId => { var _allNodes$targetId; return !deletedIds.has((_allNodes$targetId = allNodes[targetId]) === null || _allNodes$targetId === void 0 ? void 0 : _allNodes$targetId.localId); })) !== null && _allNodes$id$targets$ !== void 0 ? _allNodes$id$targets$ : []); } } return Array.from(deletedIds); }; // for get children info for confirmation dialog export const getChildrenInfo = (state, node) => { let allChildrenHadName = true; if (!node) { return []; } const childrenIdSet = new Set(); const childrenInfoArray = []; const allNodes = getConnections(state); const selectedNodeIds = getSelectedLocalIds(state, node); selectedNodeIds.forEach(id => { if (allNodes[id]) { allNodes[id].targets.forEach(childrenIdSet.add, childrenIdSet); } }); for (const id of childrenIdSet) { if (!getNodeNameById(id, allNodes)) { allChildrenHadName = false; break; } else { childrenInfoArray.push({ id, name: getNodeNameById(id, allNodes), amount: getChildrenNodeAmount(id, allNodes) }); } } return allChildrenHadName ? childrenInfoArray : []; }; const getChildrenNodeAmount = (id, allNodes) => { const searchTerms = new Set([id]); const traverseHistory = new Map(); const childrenIds = new Set(); traverseHistory.set(id, false); while (searchTerms.size > 0) { const [currTerm] = searchTerms; let targets = getNodeTargetsById(currTerm, allNodes); targets.forEach(target => { const isTargetCounted = traverseHistory.get(target); if (!isTargetCounted) { searchTerms.add(target); childrenIds.add(target); } target !== id && childrenIds.add(target); }); traverseHistory.set(currTerm, true); searchTerms.delete(currTerm); } return childrenIds.size; }; const getNodeTargetsById = (id, allNodes) => { if (!id || !allNodes[id]) { return []; } return allNodes[id].targets; }; const getNodeNameById = (id, allNodes) => { if (typeof id === 'object') { let name; id.forEach(localId => { var _name, _allNodes$localId; name = (_name = name) !== null && _name !== void 0 ? _name : (_allNodes$localId = allNodes[localId]) === null || _allNodes$localId === void 0 ? void 0 : _allNodes$localId.name; }); return name || null; } if (!id || !allNodes[id]) { return null; } return allNodes[id].name; }; const getSelectedLocalIds = (state, node) => { var _node$attrs5, _node$marks2, _node$marks2$find, _node$marks2$find$att; if (!node) { return new Set([]); } const localIds = new Set([(_node$attrs5 = node.attrs) === null || _node$attrs5 === void 0 ? void 0 : _node$attrs5.localId, (_node$marks2 = node.marks) === null || _node$marks2 === void 0 ? void 0 : (_node$marks2$find = _node$marks2.find(mark => mark.type === state.schema.marks.fragment)) === null || _node$marks2$find === void 0 ? void 0 : (_node$marks2$find$att = _node$marks2$find.attrs) === null || _node$marks2$find$att === void 0 ? void 0 : _node$marks2$find$att.localId].filter(Boolean)); return localIds; }; export const getNodeName = (state, node) => { var _node$marks$find$attr2, _node$marks3, _node$marks3$find, _node$marks3$find$att; return (_node$marks$find$attr2 = node === null || node === void 0 ? void 0 : (_node$marks3 = node.marks) === null || _node$marks3 === void 0 ? void 0 : (_node$marks3$find = _node$marks3.find(mark => mark.type === state.schema.marks.fragment)) === null || _node$marks3$find === void 0 ? void 0 : (_node$marks3$find$att = _node$marks3$find.attrs) === null || _node$marks3$find$att === void 0 ? void 0 : _node$marks3$find$att.name) !== null && _node$marks$find$attr2 !== void 0 ? _node$marks$find$attr2 : ''; };