UNPKG

@atlaskit/editor-common

Version:

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

235 lines (230 loc) • 11.8 kB
import _slicedToArray from "@babel/runtime/helpers/slicedToArray"; import { SortOrder } from '../types'; export var ContentType = /*#__PURE__*/function (ContentType) { ContentType[ContentType["NUMBER"] = 0] = "NUMBER"; ContentType[ContentType["TEXT"] = 5] = "TEXT"; ContentType[ContentType["MENTION"] = 10] = "MENTION"; ContentType[ContentType["DATE"] = 15] = "DATE"; ContentType[ContentType["STATUS"] = 20] = "STATUS"; ContentType[ContentType["LINK"] = 25] = "LINK"; return ContentType; }({}); function getLinkMark(node) { var _node$marks$filter = node.marks.filter(function (mark) { return mark.type.name === 'link'; }), _node$marks$filter2 = _slicedToArray(_node$marks$filter, 1), linkMark = _node$marks$filter2[0]; return linkMark || null; } function parseLocaleNumber(stringNumber, groupPattern, fractionPattern) { if (!stringNumber.trim().length) { return null; } var maybeANumber = Number.parseFloat(stringNumber.replace(groupPattern, '').replace(fractionPattern, '.')); if (Number.isNaN(maybeANumber)) { return null; } return maybeANumber; } export function createNormalizeTextParser() { // Source: https://stackoverflow.com/questions/12004808/does-javascript-take-local-decimal-separators-into-account var locale = window.navigator.language; var thousandSeparator = Intl.NumberFormat(locale).format(11111).replace(/(?:[0-9\xB2\xB3\xB9\xBC-\xBE\u0660-\u0669\u06F0-\u06F9\u07C0-\u07C9\u0966-\u096F\u09E6-\u09EF\u09F4-\u09F9\u0A66-\u0A6F\u0AE6-\u0AEF\u0B66-\u0B6F\u0B72-\u0B77\u0BE6-\u0BF2\u0C66-\u0C6F\u0C78-\u0C7E\u0CE6-\u0CEF\u0D58-\u0D5E\u0D66-\u0D78\u0DE6-\u0DEF\u0E50-\u0E59\u0ED0-\u0ED9\u0F20-\u0F33\u1040-\u1049\u1090-\u1099\u1369-\u137C\u16EE-\u16F0\u17E0-\u17E9\u17F0-\u17F9\u1810-\u1819\u1946-\u194F\u19D0-\u19DA\u1A80-\u1A89\u1A90-\u1A99\u1B50-\u1B59\u1BB0-\u1BB9\u1C40-\u1C49\u1C50-\u1C59\u2070\u2074-\u2079\u2080-\u2089\u2150-\u2182\u2185-\u2189\u2460-\u249B\u24EA-\u24FF\u2776-\u2793\u2CFD\u3007\u3021-\u3029\u3038-\u303A\u3192-\u3195\u3220-\u3229\u3248-\u324F\u3251-\u325F\u3280-\u3289\u32B1-\u32BF\uA620-\uA629\uA6E6-\uA6EF\uA830-\uA835\uA8D0-\uA8D9\uA900-\uA909\uA9D0-\uA9D9\uA9F0-\uA9F9\uAA50-\uAA59\uABF0-\uABF9\uFF10-\uFF19]|\uD800[\uDD07-\uDD33\uDD40-\uDD78\uDD8A\uDD8B\uDEE1-\uDEFB\uDF20-\uDF23\uDF41\uDF4A\uDFD1-\uDFD5]|\uD801[\uDCA0-\uDCA9]|\uD802[\uDC58-\uDC5F\uDC79-\uDC7F\uDCA7-\uDCAF\uDCFB-\uDCFF\uDD16-\uDD1B\uDDBC\uDDBD\uDDC0-\uDDCF\uDDD2-\uDDFF\uDE40-\uDE48\uDE7D\uDE7E\uDE9D-\uDE9F\uDEEB-\uDEEF\uDF58-\uDF5F\uDF78-\uDF7F\uDFA9-\uDFAF]|\uD803[\uDCFA-\uDCFF\uDD30-\uDD39\uDE60-\uDE7E\uDF1D-\uDF26\uDF51-\uDF54\uDFC5-\uDFCB]|\uD804[\uDC52-\uDC6F\uDCF0-\uDCF9\uDD36-\uDD3F\uDDD0-\uDDD9\uDDE1-\uDDF4\uDEF0-\uDEF9]|\uD805[\uDC50-\uDC59\uDCD0-\uDCD9\uDE50-\uDE59\uDEC0-\uDEC9\uDF30-\uDF3B]|\uD806[\uDCE0-\uDCF2\uDD50-\uDD59]|\uD807[\uDC50-\uDC6C\uDD50-\uDD59\uDDA0-\uDDA9\uDF50-\uDF59\uDFC0-\uDFD4]|\uD809[\uDC00-\uDC6E]|\uD81A[\uDE60-\uDE69\uDEC0-\uDEC9\uDF50-\uDF59\uDF5B-\uDF61]|\uD81B[\uDE80-\uDE96]|\uD834[\uDEC0-\uDED3\uDEE0-\uDEF3\uDF60-\uDF78]|\uD835[\uDFCE-\uDFFF]|\uD838[\uDD40-\uDD49\uDEF0-\uDEF9]|\uD839[\uDCF0-\uDCF9]|\uD83A[\uDCC7-\uDCCF\uDD50-\uDD59]|\uD83B[\uDC71-\uDCAB\uDCAD-\uDCAF\uDCB1-\uDCB4\uDD01-\uDD2D\uDD2F-\uDD3D]|\uD83C[\uDD00-\uDD0C]|\uD83E[\uDFF0-\uDFF9])/g, ''); var decimalSeparator = Intl.NumberFormat(locale).format(1.1).replace(/(?:[0-9\xB2\xB3\xB9\xBC-\xBE\u0660-\u0669\u06F0-\u06F9\u07C0-\u07C9\u0966-\u096F\u09E6-\u09EF\u09F4-\u09F9\u0A66-\u0A6F\u0AE6-\u0AEF\u0B66-\u0B6F\u0B72-\u0B77\u0BE6-\u0BF2\u0C66-\u0C6F\u0C78-\u0C7E\u0CE6-\u0CEF\u0D58-\u0D5E\u0D66-\u0D78\u0DE6-\u0DEF\u0E50-\u0E59\u0ED0-\u0ED9\u0F20-\u0F33\u1040-\u1049\u1090-\u1099\u1369-\u137C\u16EE-\u16F0\u17E0-\u17E9\u17F0-\u17F9\u1810-\u1819\u1946-\u194F\u19D0-\u19DA\u1A80-\u1A89\u1A90-\u1A99\u1B50-\u1B59\u1BB0-\u1BB9\u1C40-\u1C49\u1C50-\u1C59\u2070\u2074-\u2079\u2080-\u2089\u2150-\u2182\u2185-\u2189\u2460-\u249B\u24EA-\u24FF\u2776-\u2793\u2CFD\u3007\u3021-\u3029\u3038-\u303A\u3192-\u3195\u3220-\u3229\u3248-\u324F\u3251-\u325F\u3280-\u3289\u32B1-\u32BF\uA620-\uA629\uA6E6-\uA6EF\uA830-\uA835\uA8D0-\uA8D9\uA900-\uA909\uA9D0-\uA9D9\uA9F0-\uA9F9\uAA50-\uAA59\uABF0-\uABF9\uFF10-\uFF19]|\uD800[\uDD07-\uDD33\uDD40-\uDD78\uDD8A\uDD8B\uDEE1-\uDEFB\uDF20-\uDF23\uDF41\uDF4A\uDFD1-\uDFD5]|\uD801[\uDCA0-\uDCA9]|\uD802[\uDC58-\uDC5F\uDC79-\uDC7F\uDCA7-\uDCAF\uDCFB-\uDCFF\uDD16-\uDD1B\uDDBC\uDDBD\uDDC0-\uDDCF\uDDD2-\uDDFF\uDE40-\uDE48\uDE7D\uDE7E\uDE9D-\uDE9F\uDEEB-\uDEEF\uDF58-\uDF5F\uDF78-\uDF7F\uDFA9-\uDFAF]|\uD803[\uDCFA-\uDCFF\uDD30-\uDD39\uDE60-\uDE7E\uDF1D-\uDF26\uDF51-\uDF54\uDFC5-\uDFCB]|\uD804[\uDC52-\uDC6F\uDCF0-\uDCF9\uDD36-\uDD3F\uDDD0-\uDDD9\uDDE1-\uDDF4\uDEF0-\uDEF9]|\uD805[\uDC50-\uDC59\uDCD0-\uDCD9\uDE50-\uDE59\uDEC0-\uDEC9\uDF30-\uDF3B]|\uD806[\uDCE0-\uDCF2\uDD50-\uDD59]|\uD807[\uDC50-\uDC6C\uDD50-\uDD59\uDDA0-\uDDA9\uDF50-\uDF59\uDFC0-\uDFD4]|\uD809[\uDC00-\uDC6E]|\uD81A[\uDE60-\uDE69\uDEC0-\uDEC9\uDF50-\uDF59\uDF5B-\uDF61]|\uD81B[\uDE80-\uDE96]|\uD834[\uDEC0-\uDED3\uDEE0-\uDEF3\uDF60-\uDF78]|\uD835[\uDFCE-\uDFFF]|\uD838[\uDD40-\uDD49\uDEF0-\uDEF9]|\uD839[\uDCF0-\uDCF9]|\uD83A[\uDCC7-\uDCCF\uDD50-\uDD59]|\uD83B[\uDC71-\uDCAB\uDCAD-\uDCAF\uDCB1-\uDCB4\uDD01-\uDD2D\uDD2F-\uDD3D]|\uD83C[\uDD00-\uDD0C]|\uD83E[\uDFF0-\uDFF9])/g, ''); // Ignored via go/ees005 // eslint-disable-next-line require-unicode-regexp var numericPattern = new RegExp("(\\d+(?:[".concat(thousandSeparator).concat(decimalSeparator, "]?\\d+)*)"), 'g'); // Ignored via go/ees005 // eslint-disable-next-line require-unicode-regexp var thousandSeparatorPattern = new RegExp('\\' + thousandSeparator, 'g'); // Ignored via go/ees005 // eslint-disable-next-line require-unicode-regexp var decimalSeparatorPattern = new RegExp('\\' + decimalSeparator); return function (text) { if (!text.trim().length) { return null; } // This will break the text apart at the locations of the formatted numbers var result = text.split(numericPattern); // We then put the text back together but with all the formatted numbers converted back to plain numerals, // for example a sentence like "What is 1,000.01% of 10,000.01" would be normalized and sorted as // if it's saying "What is 1000.01% of 10000.01". This way the Intl.Collator can use the numeric setting to sort // numeral values within string correctly. var tokens = result.reduce(function (acc, stringNumber) { if (!(stringNumber !== null && stringNumber !== void 0 && stringNumber.length)) { return acc; } var maybeANumber = parseLocaleNumber(stringNumber, thousandSeparatorPattern, decimalSeparatorPattern); // NOTE: We know there can only be a single decimal separator. So we can assume that if the first found separator // is not at the same position as the last found one, then we can assume the locale used to format the number // is different to our locale. This will result in the value being treated as a string. if (maybeANumber !== null && stringNumber.indexOf(decimalSeparator) === stringNumber.lastIndexOf(decimalSeparator)) { acc.push(maybeANumber); } else { acc.push(stringNumber); } return acc; }, []); if (tokens.length === 1) { return tokens[0]; } return tokens.join(''); }; } export function extractMetaFromTextNode(textNode, normalizeTextParser) { // treat as a link if contain a link var linkMark = getLinkMark(textNode); if (linkMark) { var value = textNode.text || ''; return { type: ContentType.LINK, value: value }; } var text = textNode.text || ''; var normalizedText = normalizeTextParser(text); if (typeof normalizedText === 'number') { return { type: ContentType.NUMBER, value: normalizedText }; } return { type: ContentType.TEXT, value: normalizedText !== null && normalizedText !== void 0 ? normalizedText : text }; } function getMetaFromNode(node, options, normalizeTextParser) { if (!node) { return null; } var firstChild = node.firstChild; if (!firstChild) { return null; } switch (firstChild.type.name) { // Text case /* Get Meta value from the first child if the cell is of type * Heading (Any cell where the text is set to a heading type) * Paragraph (Normal text) */ case 'heading': case 'paragraph': { return getMetaFromNode(firstChild, options, normalizeTextParser); } case 'inlineCard': { var _attrs = firstChild.attrs; var maybeTitle = options.getInlineCardTextFromStore(_attrs); if (maybeTitle) { return { type: ContentType.LINK, value: maybeTitle }; } var url = _attrs.url; return { type: ContentType.LINK, value: url ? url : '' }; } case 'text': { return extractMetaFromTextNode(firstChild, normalizeTextParser); } case 'status': { var _text = firstChild.attrs.text; return { type: ContentType.STATUS, value: _text }; } case 'date': { var timestamp = Number.parseInt(firstChild.attrs.timestamp, 20); return { type: ContentType.DATE, value: timestamp }; } case 'mention': { // TODO: Check what should be the fallback when mention does not have a text var _text2 = firstChild.attrs.text || ''; return { type: ContentType.MENTION, value: _text2.toLowerCase() }; } default: return null; } } function compareValue(valueA, valueB) { if (valueA === valueB) { return 0; } if (typeof valueA === 'string' && typeof valueB === 'string') { return valueA.localeCompare(valueB, window.navigator.language, { caseFirst: 'upper', numeric: true }); } return valueA > valueB ? 1 : -1; } /** * Compare 2 prosemirror nodes and check if it's greater, equal or less than the other node * based on the sort order. * * @param {Node} nodeA * @param {Node} nodeB * @returns {(1 | 0 | -1)} * * For Ascending order: * 1 -> NodeA > NodeB * 0 -> NodeA === NodeB * -1 -> Node A < NodeB * For Descending order: * 1 -> NodeA < NodeB * 0 -> NodeA === NodeB * -1 -> Node A > NodeB * * If either node is empty: * The empty node is always treated as lower priority, * irrespective of the order. * * If no order is provided the method defaults to Ascending order, * like a regular JS sort method. */ export var createCompareNodes = function createCompareNodes(options) { var order = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : SortOrder.ASC; var normalizeTextParser = createNormalizeTextParser(); return function (nodeA, nodeB) { var metaNodeA = getMetaFromNode(nodeA, options, normalizeTextParser); var metaNodeB = getMetaFromNode(nodeB, options, normalizeTextParser); /* Donot switch the order (Asec or Desc) if either node is null. This will ensure that empty cells are always at the bottom during sorting. */ if (metaNodeA === null || metaNodeB === null) { return compareMetaFromNode(metaNodeA, metaNodeB); } return (order === SortOrder.DESC ? -1 : 1) * compareMetaFromNode(metaNodeA, metaNodeB); }; }; function compareMetaFromNode(metaNodeA, metaNodeB) { if (metaNodeA === metaNodeB) { return 0; } if (metaNodeA === null || metaNodeB === null) { return metaNodeB === null ? -1 : 1; } if (metaNodeA.type !== metaNodeB.type) { return metaNodeA.type > metaNodeB.type ? 1 : -1; } return compareValue(metaNodeA.value, metaNodeB.value); }