@portabletext/editor
Version:
Portable Text Editor made in React
267 lines (266 loc) • 9.26 kB
JavaScript
import { isTypedObject, isKeyedSegment, getNode, getAncestors, getChildren, getNodeChildren, isObjectNode } from "./get-ancestor.js";
import { getSubSchema } from "@portabletext/schema";
function isSpanNode(context, node) {
return isTypedObject(node) && node._type === context.schema.span.name;
}
function isTextBlockNode(context, node) {
return isTypedObject(node) && node._type === context.schema.block.name;
}
function parentPath(path) {
if (path.length === 0)
throw new Error(`Cannot get the parent path of the root path [${path}].`);
let lastNodeIndex = -1;
for (let i = path.length - 1; i >= 0; i--)
if (isKeyedSegment(path[i]) || typeof path[i] == "number") {
lastNodeIndex = i;
break;
}
if (lastNodeIndex === -1)
return [];
const result = path.slice(0, lastNodeIndex);
return result.length > 0 && typeof result[result.length - 1] == "string" ? result.slice(0, -1) : result;
}
function getParent(snapshot, path) {
if (path.length === 0)
return;
const parent = parentPath(path);
if (parent.length !== 0)
return getNode(snapshot, parent);
}
function isBlock(snapshot, path) {
const parent = getParent(snapshot, path);
return parent ? !isTextBlockNode({
schema: snapshot.context.schema
}, parent.node) : !0;
}
function getBlock(snapshot, path) {
const entry = getNode(snapshot, path);
if (entry && isBlock(snapshot, path) && !isSpanNode({
schema: snapshot.context.schema
}, entry.node))
return {
node: entry.node,
path: entry.path
};
}
function hasNode(snapshot, path) {
return getNode(snapshot, path) !== void 0;
}
function resolveContainerAt(containers, value, path) {
const keyedIndices = [];
for (let index = 0; index < path.length; index++)
isKeyedSegment(path[index]) && keyedIndices.push(index);
if (keyedIndices.length === 0)
return;
let currentChildren = value, parent, resolved;
const targetKeyedIndex = keyedIndices[keyedIndices.length - 1];
let segmentIndex = 0;
for (; segmentIndex <= targetKeyedIndex; ) {
const segment = path[segmentIndex];
if (typeof segment == "string") {
segmentIndex++;
continue;
}
let node;
if (isKeyedSegment(segment))
node = currentChildren.find((child) => child._key === segment._key);
else if (typeof segment == "number")
node = currentChildren.at(segment);
else
return;
if (!node || (resolved = resolveNodeEntry(containers, parent, node), !resolved))
return;
if (segmentIndex < targetKeyedIndex) {
if (!("field" in resolved))
return;
const fieldValue = node[resolved.field.name];
if (!Array.isArray(fieldValue))
return;
parent = resolved, currentChildren = fieldValue;
}
segmentIndex++;
}
return resolved;
}
function resolveNodeEntry(containers, parent, node) {
if (parent?.of) {
for (const entry of parent.of)
if (entry.type === node._type)
return entry;
}
return containers.get(node._type);
}
function getEnclosingBlock(snapshot, path) {
const direct = getBlock(snapshot, path);
if (direct)
return direct;
for (const ancestor of getAncestors(snapshot, path))
if (isBlock(snapshot, ancestor.path)) {
const block = getBlock(snapshot, ancestor.path);
if (block)
return block;
}
}
function isAncestorPath(path, another) {
if (path.length >= another.length)
return !1;
for (let i = 0; i < path.length; i++) {
const segment = path[i], otherSegment = another[i];
if (isKeyedSegment(segment) && isKeyedSegment(otherSegment)) {
if (segment._key !== otherSegment._key)
return !1;
} else if (segment !== otherSegment)
return !1;
}
return !0;
}
function* getNodes(snapshot, options = {}) {
const {
at = [],
from,
to,
match,
reverse = !1
} = options;
if (from === void 0 && to === void 0) {
yield* getNodesSimple(snapshot, at, {
match,
reverse
});
return;
}
yield* getNodesInRange(snapshot, at, {
from,
to,
match,
reverse
});
}
function* getNodesSimple(snapshot, path, options) {
const {
match,
reverse = !1
} = options, children = getChildren(snapshot, path), entries = reverse ? [...children].reverse() : children;
for (const entry of entries)
(!match || match(entry.node, entry.path)) && (yield entry), yield* getNodesSimple(snapshot, entry.path, options);
}
function comparePathsInTree(snapshot, pathA, pathB) {
const keysA = pathA.filter(isKeyedSegment), keysB = pathB.filter(isKeyedSegment), {
context
} = snapshot;
let currentChildren = context.value, currentParent, isRootLevel = !0;
const minDepth = Math.min(keysA.length, keysB.length);
for (let depth = 0; depth < minDepth; depth++) {
const keyA = keysA[depth], keyB = keysB[depth];
if (keyA._key === keyB._key) {
let matchedNode;
if (isRootLevel && snapshot.blockIndexMap.has(keyA._key)) {
const index = snapshot.blockIndexMap.get(keyA._key);
index !== void 0 && (matchedNode = currentChildren[index]);
} else
matchedNode = currentChildren.find((c) => c._key === keyA._key);
if (!matchedNode)
return 0;
const next = getNodeChildren(context, matchedNode, currentParent);
if (!next)
return 0;
currentChildren = next.children, currentParent = next.parent, isRootLevel = !1;
continue;
}
if (isRootLevel) {
const indexA2 = snapshot.blockIndexMap.get(keyA._key) ?? -1, indexB2 = snapshot.blockIndexMap.get(keyB._key) ?? -1;
if (indexA2 !== -1 && indexB2 !== -1)
return indexA2 < indexB2 ? -1 : indexA2 > indexB2 ? 1 : 0;
}
let indexA = -1, indexB = -1;
for (let i = 0; i < currentChildren.length; i++) {
const sibling = currentChildren[i];
if (sibling._key === keyA._key && (indexA = i), sibling._key === keyB._key && (indexB = i), indexA !== -1 && indexB !== -1)
break;
}
return indexA < indexB ? -1 : indexA > indexB ? 1 : 0;
}
return keysA.length < keysB.length ? -1 : keysA.length > keysB.length ? 1 : 0;
}
function* getNodesInRange(snapshot, path, options) {
const {
from,
to,
match,
reverse = !1
} = options, children = getChildren(snapshot, path), entries = reverse ? [...children].reverse() : children;
for (const entry of entries) {
if (canStopTraversal(snapshot, entry.path, from, to, reverse))
return;
couldContainInRangeNodes(snapshot, entry.path, from, to) && (isInRange(snapshot, entry.path, from, to) && (!match || match(entry.node, entry.path)) && (yield entry), yield* getNodesInRange(snapshot, entry.path, options));
}
}
function isInRange(snapshot, nodePath, from, to) {
return !(from !== void 0 && comparePathsInTree(snapshot, nodePath, from) === -1 && !isAncestorPath(nodePath, from) || to !== void 0 && comparePathsInTree(snapshot, nodePath, to) === 1 && !isAncestorPath(nodePath, to));
}
function couldContainInRangeNodes(snapshot, nodePath, from, to) {
return !!(isInRange(snapshot, nodePath, from, to) || from !== void 0 && isAncestorPath(nodePath, from) || to !== void 0 && isAncestorPath(nodePath, to));
}
function canStopTraversal(snapshot, nodePath, from, to, reverse) {
return reverse ? from === void 0 ? !1 : comparePathsInTree(snapshot, nodePath, from) === -1 && !isAncestorPath(nodePath, from) : to === void 0 ? !1 : comparePathsInTree(snapshot, nodePath, to) === 1;
}
function getSibling(snapshot, path, direction) {
if (path.length === 0)
return;
const lastSegment = path.at(-1);
if (!isKeyedSegment(lastSegment))
return;
const parent = parentPath(path), children = getChildren(snapshot, parent), currentIndex = children.findIndex((child) => child.node._key === lastSegment._key);
if (currentIndex === -1)
return;
const siblingIndex = direction === "next" ? currentIndex + 1 : currentIndex - 1;
if (!(siblingIndex < 0 || siblingIndex >= children.length))
return children[siblingIndex];
}
function isInline(snapshot, path) {
return !isBlock(snapshot, path);
}
function descendToParent(snapshot, path) {
const ancestors = getAncestors(snapshot, path);
for (const ancestor of ancestors) {
if (!isObjectNode({
schema: snapshot.context.schema
}, ancestor.node))
continue;
const resolved = resolveContainerAt(snapshot.context.containers, snapshot.context.value, ancestor.path);
return !resolved || !("field" in resolved) ? void 0 : {
parent: resolved,
parentPath: ancestor.path
};
}
}
function getEnclosingContainer(snapshot, path) {
const descent = descendToParent(snapshot, path);
if (descent)
return {
of: descent.parent.field.of,
path: descent.parentPath
};
}
function getPathSubSchema(snapshot, path) {
const enclosing = getEnclosingContainer(snapshot, path);
return enclosing ? getSubSchema(snapshot.context.schema, enclosing.of) : snapshot.context.schema;
}
export {
getBlock,
getEnclosingBlock,
getEnclosingContainer,
getNodes,
getParent,
getPathSubSchema,
getSibling,
hasNode,
isAncestorPath,
isBlock,
isInline,
isSpanNode,
isTextBlockNode,
parentPath,
resolveContainerAt
};
//# sourceMappingURL=get-path-sub-schema.js.map