UNPKG

@portabletext/editor

Version:

Portable Text Editor made in React

267 lines (266 loc) 9.26 kB
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