@atlaskit/editor-common
Version:
A package that contains common classes and components for editor and renderer
235 lines (230 loc) • 11.8 kB
JavaScript
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);
}