@portabletext/toolkit
Version:
Toolkit of handy utility functions for dealing with Portable Text
237 lines (234 loc) • 10.5 kB
JavaScript
;
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
Object.defineProperty(exports, "__esModule", {
value: !0
});
function isPortableTextSpan(node) {
return node._type === "span" && "text" in node && typeof node.text == "string" && (typeof node.marks > "u" || Array.isArray(node.marks) && node.marks.every(mark => typeof mark == "string"));
}
function isPortableTextBlock(node) {
return (
// A block doesn't _have_ to be named 'block' - to differentiate between
// allowed child types and marks, one might name them differently
typeof node._type == "string" &&
// Toolkit-types like nested spans are @-prefixed
node._type[0] !== "@" && (
// `markDefs` isn't _required_ per say, but if it's there, it needs to be an array
!("markDefs" in node) || !node.markDefs || Array.isArray(node.markDefs) &&
// Every mark definition needs to have an `_key` to be mappable in child spans
node.markDefs.every(def => typeof def._key == "string")) &&
// `children` is required and needs to be an array
"children" in node && Array.isArray(node.children) &&
// All children are objects with `_type` (usually spans, but can contain other stuff)
node.children.every(child => typeof child == "object" && "_type" in child)
);
}
function isPortableTextListItemBlock(block) {
return isPortableTextBlock(block) && "listItem" in block && typeof block.listItem == "string" && (typeof block.level > "u" || typeof block.level == "number");
}
function isPortableTextToolkitList(block) {
return block._type === "@list";
}
function isPortableTextToolkitSpan(span) {
return span._type === "@span";
}
function isPortableTextToolkitTextNode(node) {
return node._type === "@text";
}
const knownDecorators = ["strong", "em", "code", "underline", "strike-through"];
function sortMarksByOccurences(span, index, blockChildren) {
if (!isPortableTextSpan(span) || !span.marks) return [];
if (!span.marks.length) return [];
const marks = span.marks.slice(),
occurences = {};
return marks.forEach(mark => {
occurences[mark] = 1;
for (let siblingIndex = index + 1; siblingIndex < blockChildren.length; siblingIndex++) {
const sibling = blockChildren[siblingIndex];
if (sibling && isPortableTextSpan(sibling) && Array.isArray(sibling.marks) && sibling.marks.indexOf(mark) !== -1) occurences[mark]++;else break;
}
}), marks.sort((markA, markB) => sortMarks(occurences, markA, markB));
}
function sortMarks(occurences, markA, markB) {
const aOccurences = occurences[markA],
bOccurences = occurences[markB];
if (aOccurences !== bOccurences) return bOccurences - aOccurences;
const aKnownPos = knownDecorators.indexOf(markA),
bKnownPos = knownDecorators.indexOf(markB);
return aKnownPos !== bKnownPos ? aKnownPos - bKnownPos : markA.localeCompare(markB);
}
function buildMarksTree(block) {
var _a, _b;
const {
children
} = block,
markDefs = (_a = block.markDefs) != null ? _a : [];
if (!children || !children.length) return [];
const sortedMarks = children.map(sortMarksByOccurences),
rootNode = {
_type: "@span",
children: [],
markType: "<unknown>"
};
let nodeStack = [rootNode];
for (let i = 0; i < children.length; i++) {
const span = children[i];
if (!span) continue;
const marksNeeded = sortedMarks[i] || [];
let pos = 1;
if (nodeStack.length > 1) for (pos; pos < nodeStack.length; pos++) {
const mark = ((_b = nodeStack[pos]) == null ? void 0 : _b.markKey) || "",
index = marksNeeded.indexOf(mark);
if (index === -1) break;
marksNeeded.splice(index, 1);
}
nodeStack = nodeStack.slice(0, pos);
let currentNode = nodeStack[nodeStack.length - 1];
if (currentNode) {
for (const markKey of marksNeeded) {
const markDef = markDefs == null ? void 0 : markDefs.find(def => def._key === markKey),
markType = markDef ? markDef._type : markKey,
node = {
_type: "@span",
_key: span._key,
children: [],
markDef,
markType,
markKey
};
currentNode.children.push(node), nodeStack.push(node), currentNode = node;
}
if (isPortableTextSpan(span)) {
const lines = span.text.split(`
`);
for (let line = lines.length; line-- > 1;) lines.splice(line, 0, `
`);
currentNode.children = currentNode.children.concat(lines.map(text => ({
_type: "@text",
text
})));
} else currentNode.children = currentNode.children.concat(span);
}
}
return rootNode.children;
}
function nestLists(blocks, mode) {
const tree = [];
let currentList;
for (let i = 0; i < blocks.length; i++) {
const block = blocks[i];
if (block) {
if (!isPortableTextListItemBlock(block)) {
tree.push(block), currentList = void 0;
continue;
}
if (!currentList) {
currentList = listFromBlock(block, i, mode), tree.push(currentList);
continue;
}
if (blockMatchesList(block, currentList)) {
currentList.children.push(block);
continue;
}
if ((block.level || 1) > currentList.level) {
const newList = listFromBlock(block, i, mode);
if (mode === "html") {
const lastListItem = currentList.children[currentList.children.length - 1],
newLastChild = _objectSpread(_objectSpread({}, lastListItem), {}, {
children: [...lastListItem.children, newList]
});
currentList.children[currentList.children.length - 1] = newLastChild;
} else currentList.children.push(newList);
currentList = newList;
continue;
}
if ((block.level || 1) < currentList.level) {
const matchingBranch = tree[tree.length - 1],
match = matchingBranch && findListMatching(matchingBranch, block);
if (match) {
currentList = match, currentList.children.push(block);
continue;
}
currentList = listFromBlock(block, i, mode), tree.push(currentList);
continue;
}
if (block.listItem !== currentList.listItem) {
const matchingBranch = tree[tree.length - 1],
match = matchingBranch && findListMatching(matchingBranch, {
level: block.level || 1
});
if (match && match.listItem === block.listItem) {
currentList = match, currentList.children.push(block);
continue;
} else {
currentList = listFromBlock(block, i, mode), tree.push(currentList);
continue;
}
}
console.warn("Unknown state encountered for block", block), tree.push(block);
}
}
return tree;
}
function blockMatchesList(block, list) {
return (block.level || 1) === list.level && block.listItem === list.listItem;
}
function listFromBlock(block, index, mode) {
return {
_type: "@list",
_key: `${block._key || `${index}`}-parent`,
mode,
level: block.level || 1,
listItem: block.listItem,
children: [block]
};
}
function findListMatching(rootNode, matching) {
const level = matching.level || 1,
style = matching.listItem || "normal",
filterOnType = typeof matching.listItem == "string";
if (isPortableTextToolkitList(rootNode) && (rootNode.level || 1) === level && filterOnType && (rootNode.listItem || "normal") === style) return rootNode;
if (!("children" in rootNode)) return;
const node = rootNode.children[rootNode.children.length - 1];
return node && !isPortableTextSpan(node) ? findListMatching(node, matching) : void 0;
}
function spanToPlainText(span) {
let text = "";
return span.children.forEach(current => {
isPortableTextToolkitTextNode(current) ? text += current.text : isPortableTextToolkitSpan(current) && (text += spanToPlainText(current));
}), text;
}
const leadingSpace = /^\s/,
trailingSpace = /\s$/;
function toPlainText(block) {
const blocks = Array.isArray(block) ? block : [block];
let text = "";
return blocks.forEach((current, index) => {
if (!isPortableTextBlock(current)) return;
let pad = !1;
current.children.forEach(span => {
isPortableTextSpan(span) ? (text += pad && text && !trailingSpace.test(text) && !leadingSpace.test(span.text) ? " " : "", text += span.text, pad = !1) : pad = !0;
}), index !== blocks.length - 1 && (text += `
`);
}), text;
}
const LIST_NEST_MODE_HTML = "html",
LIST_NEST_MODE_DIRECT = "direct";
exports.LIST_NEST_MODE_DIRECT = LIST_NEST_MODE_DIRECT;
exports.LIST_NEST_MODE_HTML = LIST_NEST_MODE_HTML;
exports.buildMarksTree = buildMarksTree;
exports.isPortableTextBlock = isPortableTextBlock;
exports.isPortableTextListItemBlock = isPortableTextListItemBlock;
exports.isPortableTextSpan = isPortableTextSpan;
exports.isPortableTextToolkitList = isPortableTextToolkitList;
exports.isPortableTextToolkitSpan = isPortableTextToolkitSpan;
exports.isPortableTextToolkitTextNode = isPortableTextToolkitTextNode;
exports.nestLists = nestLists;
exports.sortMarksByOccurences = sortMarksByOccurences;
exports.spanToPlainText = spanToPlainText;
exports.toPlainText = toPlainText;
//# sourceMappingURL=index.cjs.map