UNPKG

@atlaskit/editor-common

Version:

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

180 lines (171 loc) • 6.16 kB
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray"; import { AnnotationTypes } from '@atlaskit/adf-schema'; import { fg } from '@atlaskit/platform-feature-flags'; export var canApplyAnnotationOnRange = function canApplyAnnotationOnRange(rangeSelection, doc, schema) { var from = rangeSelection.from, to = rangeSelection.to; if (isNaN(from + to) || to - from <= 0 || to < 0 || from < 0) { return false; } var foundInvalid = false; doc.nodesBetween(rangeSelection.from, rangeSelection.to, function (node, _pos, parent) { // Special exception for hardBreak nodes if (schema.nodes.hardBreak === node.type) { return false; } // For block elements or text nodes, we want to check // if annotations are allowed inside this tree // or if we're leaf and not text if (fg('editor_inline_comments_on_inline_nodes')) { var isAllowedInlineNode = ['emoji', 'status', 'date', 'mention', 'inlineCard'].includes(node.type.name); if (node.isInline && !node.isText && !isAllowedInlineNode || node.isLeaf && !node.isText && !isAllowedInlineNode || node.isText && !(parent !== null && parent !== void 0 && parent.type.allowsMarkType(schema.marks.annotation))) { foundInvalid = true; return false; } } else { if (node.isInline && !node.isText || node.isLeaf && !node.isText || node.isText && !(parent !== null && parent !== void 0 && parent.type.allowsMarkType(schema.marks.annotation))) { foundInvalid = true; return false; } } return true; }); return !foundInvalid; }; export var getAnnotationIdsFromRange = function getAnnotationIdsFromRange(rangeSelection, doc, schema) { var from = rangeSelection.from, to = rangeSelection.to; var annotations = new Set(); doc.nodesBetween(from, to, function (node) { if (!node.marks) { return true; } node.marks.forEach(function (mark) { if (mark.type === schema.marks.annotation && mark.attrs) { annotations.add(mark.attrs.id); } }); return true; }); return Array.from(annotations); }; /* * verifies if node contains annotation mark */ export function hasAnnotationMark(node, state) { var annotationMark = state.schema.marks.annotation; return !!(annotationMark && node && node.marks.length && annotationMark.isInSet(node.marks)); } /* * verifies that slice contains any annotations */ export function containsAnyAnnotations(slice, state) { if (!slice.content.size) { return false; } var hasAnnotation = false; slice.content.forEach(function (node) { hasAnnotation = hasAnnotation || hasAnnotationMark(node, state); // return early if annotation found already if (hasAnnotation) { return true; } // check annotations in descendants node.descendants(function (node) { if (hasAnnotationMark(node, state)) { hasAnnotation = true; return false; } return true; }); }); return hasAnnotation; } /** * This returns a list of node names that are inline nodes in the range. */ export function getRangeInlineNodeNames(_ref) { var doc = _ref.doc, pos = _ref.pos; if (!fg('editor_inline_comments_on_inline_nodes')) { return undefined; } var nodeNames = new Set(); try { doc.nodesBetween(pos.from, pos.to, function (node) { if (node.isInline) { nodeNames.add(node.type.name); } }); // We sort the list alphabetically to make human consumption of the list easier (in tools like the analytics extension) var sortedNames = _toConsumableArray(nodeNames).sort(); return sortedNames; } catch (_unused) { // Some callers have existing gaps where the positions are not valid, // and so when called in the current document can throw an error if out of range. // // This is a defensive check to ensure we don't throw an error in those cases. return undefined; } } /** * This function returns a list of node types that are wrapped by an annotation mark. * * The `undefined` will be returned if `editor_inline_comments_on_inline_nodes` is off. * * @todo: Do not forget to remove `undefined` when the * `editor_inline_comments_on_inline_nodes` is removed. */ export function getAnnotationInlineNodeTypes(state, annotationId) { if (!fg('editor_inline_comments_on_inline_nodes')) { return undefined; } var mark = state.schema.marks.annotation.create({ id: annotationId, annotationType: AnnotationTypes.INLINE_COMMENT }); var inlineNodeNames = new Set(); state.doc.descendants(function (node, pos) { if (mark.isInSet(node.marks)) { inlineNodeNames.add(node.type.name); } return true; }); // This sorting is done to make human consumption easier (ie. in dev tools, test snapshots, analytics events, ...) return _toConsumableArray(inlineNodeNames).sort(); } /* Get the annotations marks from the given position and add them to the original marks array if they exist. Used with the creation of the inline nodes: emoji, status, dates, mentions & inlineCards. */ export function getAnnotationMarksForPos(pos) { if (!fg('editor_inline_comments_paste_insert_nodes')) { return undefined; } var annotationMarks = pos.marks().filter(function (mark) { return mark.type === pos.doc.type.schema.marks.annotation; }); return annotationMarks; } /** * Checks if selection contains only empty text * e.g. when you select across multiple empty paragraphs */ export function isEmptyTextSelection(selection, schema) { var _schema$nodes = schema.nodes, text = _schema$nodes.text, paragraph = _schema$nodes.paragraph; var hasContent = false; selection.content().content.descendants(function (node) { // for empty paragraph - consider empty (nothing to comment on) if (node.type === paragraph && !node.content.size) { return false; } // for not a text or nonempty text - consider nonempty (can comment if the node is supported for annotations) if (node.type !== text || !node.textContent) { hasContent = true; } return !hasContent; }); return !hasContent; }