UNPKG

@portabletext/editor

Version:

Portable Text Editor made in React

434 lines (433 loc) 13.3 kB
function isKeyedSegment(segment) { return typeof segment == "object" && segment !== null && "_key" in segment; } function getBlockKeyFromSelectionPoint(point) { const blockPathSegment = point.path.at(0); if (isKeyedSegment(blockPathSegment)) return blockPathSegment._key; } function getChildKeyFromSelectionPoint(point) { const childPathSegment = point.path.at(2); if (isKeyedSegment(childPathSegment)) return childPathSegment._key; } function isTypedObject(object) { return isRecord(object) && typeof object._type == "string"; } function isRecord(value) { return !!value && (typeof value == "object" || typeof value == "function"); } function parseBlocks({ context, blocks, options }) { return Array.isArray(blocks) ? blocks.flatMap((block) => { const parsedBlock = parseBlock({ context, block, options }); return parsedBlock ? [parsedBlock] : []; }) : []; } function parseBlock({ context, block, options }) { return parseTextBlock({ block, context, options }) ?? parseBlockObject({ blockObject: block, context, options }); } function parseBlockObject({ blockObject, context, options }) { if (!isTypedObject(blockObject)) return; const schemaType = context.schema.blockObjects.find(({ name }) => name === blockObject._type); if (schemaType) return parseObject({ object: blockObject, context: { keyGenerator: context.keyGenerator, schemaType }, options }); } function isListBlock(context, block) { return isTextBlock(context, block) && block.level !== void 0 && block.listItem !== void 0; } function isTextBlock(context, block) { return !(!isTypedObject(block) || block._type !== context.schema.block.name || !Array.isArray(block.children)); } function parseTextBlock({ block, context, options }) { if (!isTypedObject(block)) return; const customFields = {}; for (const key of Object.keys(block)) key !== "_type" && key !== "_key" && key !== "children" && key !== "markDefs" && key !== "style" && key !== "listItem" && key !== "level" && (customFields[key] = block[key]); if (block._type !== context.schema.block.name) return; const _key = options.refreshKeys ? context.keyGenerator() : typeof block._key == "string" ? block._key : context.keyGenerator(), unparsedMarkDefs = Array.isArray(block.markDefs) ? block.markDefs : [], markDefKeyMap = /* @__PURE__ */ new Map(), markDefs = unparsedMarkDefs.flatMap((markDef) => { if (!isTypedObject(markDef)) return []; const schemaType = context.schema.annotations.find(({ name }) => name === markDef._type); if (!schemaType) return []; if (typeof markDef._key != "string") return []; const parsedAnnotation = parseObject({ object: markDef, context: { schemaType, keyGenerator: context.keyGenerator }, options }); return parsedAnnotation ? (markDefKeyMap.set(markDef._key, parsedAnnotation._key), [parsedAnnotation]) : []; }), children = (Array.isArray(block.children) ? block.children : []).map((child) => parseSpan({ span: child, context, markDefKeyMap, options }) ?? parseInlineObject({ inlineObject: child, context, options })).filter((child) => child !== void 0), parsedBlock = { _type: context.schema.block.name, _key, children: children.length > 0 ? children : [{ _key: context.keyGenerator(), _type: context.schema.span.name, text: "", marks: [] }], markDefs, ...options.validateFields ? {} : customFields }; if (typeof block.style == "string" && context.schema.styles.find((style) => style.name === block.style)) parsedBlock.style = block.style; else { const defaultStyle = context.schema.styles.at(0)?.name; defaultStyle !== void 0 ? parsedBlock.style = defaultStyle : console.error("Expected default style"); } return typeof block.listItem == "string" && context.schema.lists.find((list) => list.name === block.listItem) && (parsedBlock.listItem = block.listItem), typeof block.level == "number" && (parsedBlock.level = block.level), parsedBlock; } function isSpan$1(context, child) { return !(!isTypedObject(child) || child._type !== context.schema.span.name || typeof child.text != "string"); } function parseSpan({ span, context, markDefKeyMap, options }) { if (!isTypedObject(span)) return; const customFields = {}; for (const key of Object.keys(span)) key !== "_type" && key !== "_key" && key !== "text" && key !== "marks" && (customFields[key] = span[key]); if (span._type !== context.schema.span.name || span._type !== "span") return; const marks = (Array.isArray(span.marks) ? span.marks : []).flatMap((mark) => { if (typeof mark != "string") return []; const markDefKey = markDefKeyMap.get(mark); return markDefKey !== void 0 ? [markDefKey] : context.schema.decorators.some((decorator) => decorator.name === mark) ? [mark] : []; }); return { _type: "span", _key: options.refreshKeys ? context.keyGenerator() : typeof span._key == "string" ? span._key : context.keyGenerator(), text: typeof span.text == "string" ? span.text : "", marks, ...options.validateFields ? {} : customFields }; } function parseInlineObject({ inlineObject, context, options }) { if (!isTypedObject(inlineObject)) return; const schemaType = context.schema.inlineObjects.find(({ name }) => name === inlineObject._type); if (schemaType) return parseObject({ object: inlineObject, context: { keyGenerator: context.keyGenerator, schemaType }, options }); } function parseAnnotation({ annotation, context, options }) { if (!isTypedObject(annotation)) return; const schemaType = context.schema.annotations.find(({ name }) => name === annotation._type); if (schemaType) return parseObject({ object: annotation, context: { keyGenerator: context.keyGenerator, schemaType }, options }); } function parseObject({ object, context, options }) { const { _type, _key, ...customFields } = object, values = options.validateFields ? context.schemaType.fields.reduce((fieldValues, field) => { const fieldValue = object[field.name]; return fieldValue !== void 0 && (fieldValues[field.name] = fieldValue), fieldValues; }, {}) : customFields; return { _type: context.schemaType.name, _key: options.refreshKeys ? context.keyGenerator() : typeof object._key == "string" ? object._key : context.keyGenerator(), ...values }; } function blockOffsetToSpanSelectionPoint({ context, blockOffset, direction }) { let offsetLeft = blockOffset.offset, selectionPoint, skippedInlineObject = !1; for (const block of context.value) if (block._key === blockOffset.path[0]._key && isTextBlock(context, block)) for (const child of block.children) { if (direction === "forward") { if (!isSpan$1(context, child)) continue; if (offsetLeft <= child.text.length) { selectionPoint = { path: [...blockOffset.path, "children", { _key: child._key }], offset: offsetLeft }; break; } offsetLeft -= child.text.length; continue; } if (!isSpan$1(context, child)) { skippedInlineObject = !0; continue; } if (offsetLeft === 0 && selectionPoint && !skippedInlineObject) { skippedInlineObject && (selectionPoint = { path: [...blockOffset.path, "children", { _key: child._key }], offset: 0 }); break; } if (offsetLeft > child.text.length) { offsetLeft -= child.text.length; continue; } if (offsetLeft <= child.text.length && (selectionPoint = { path: [...blockOffset.path, "children", { _key: child._key }], offset: offsetLeft }, offsetLeft -= child.text.length, offsetLeft !== 0)) break; } return selectionPoint; } function spanSelectionPointToBlockOffset({ context, selectionPoint }) { let offset = 0; const blockKey = getBlockKeyFromSelectionPoint(selectionPoint), spanKey = getChildKeyFromSelectionPoint(selectionPoint); if (!(!blockKey || !spanKey)) { for (const block of context.value) if (block._key === blockKey && isTextBlock(context, block)) { for (const child of block.children) if (isSpan$1(context, child)) { if (child._key === spanKey) return { path: [{ _key: block._key }], offset: offset + selectionPoint.offset }; offset += child.text.length; } } } } function getBlockStartPoint({ context, block }) { return isTextBlock(context, block.node) ? { path: [...block.path, "children", { _key: block.node.children[0]._key }], offset: 0 } : { path: block.path, offset: 0 }; } function getSelectionEndPoint(selection) { return selection ? selection.backward ? selection.anchor : selection.focus : null; } function getSelectionStartPoint(selection) { return selection ? selection.backward ? selection.focus : selection.anchor : null; } function getTextBlockText(block) { return block.children.map((child) => child.text ?? "").join(""); } function isSpan(context, child) { return child._type === context.schema.span.name; } function sliceBlocks({ context, blocks }) { const slice = []; if (!context.selection) return slice; let startBlock; const middleBlocks = []; let endBlock; const startPoint = getSelectionStartPoint(context.selection), endPoint = getSelectionEndPoint(context.selection), startBlockKey = getBlockKeyFromSelectionPoint(startPoint), startChildKey = getChildKeyFromSelectionPoint(startPoint), endBlockKey = getBlockKeyFromSelectionPoint(endPoint), endChildKey = getChildKeyFromSelectionPoint(endPoint); if (!startBlockKey || !endBlockKey) return slice; for (const block of blocks) { if (!isTextBlock(context, block) && block._key === startBlockKey && block._key === endBlockKey) { startBlock = block; break; } if (block._key === startBlockKey) { if (!isTextBlock(context, block)) { startBlock = block; continue; } if (startChildKey) { for (const child of block.children) { if (child._key === startChildKey) { if (isSpan$1(context, child)) { const text = child._key === endChildKey ? child.text.slice(startPoint.offset, endPoint.offset) : child.text.slice(startPoint.offset); startBlock = { ...block, children: [{ ...child, text }] }; } else startBlock = { ...block, children: [child] }; if (startChildKey === endChildKey) break; continue; } if (startBlock && isTextBlock(context, startBlock) && (endChildKey && child._key === endChildKey && isSpan$1(context, child) ? startBlock.children.push({ ...child, text: child.text.slice(0, endPoint.offset) }) : startBlock.children.push(child), block._key === endBlockKey && endChildKey && child._key === endChildKey)) break; } if (startBlockKey === endBlockKey) break; continue; } if (startBlock = block, startBlockKey === endBlockKey) break; } if (block._key === endBlockKey) { if (!isTextBlock(context, block)) { endBlock = block; break; } if (endChildKey) { endBlock = { ...block, children: [] }; for (const child of block.children) if (endBlock && isTextBlock(context, endBlock)) { if (child._key === endChildKey && isSpan$1(context, child)) { endBlock.children.push({ ...child, text: child.text.slice(0, endPoint.offset) }); break; } if (endBlock.children.push(child), endChildKey && child._key === endChildKey) break; } break; } endBlock = block; break; } startBlock && middleBlocks.push(block); } return [...startBlock ? [startBlock] : [], ...middleBlocks, ...endBlock ? [endBlock] : []]; } export { blockOffsetToSpanSelectionPoint, getBlockKeyFromSelectionPoint, getBlockStartPoint, getChildKeyFromSelectionPoint, getSelectionEndPoint, getSelectionStartPoint, getTextBlockText, isKeyedSegment, isListBlock, isSpan$1 as isSpan, isSpan as isSpan$1, isTextBlock, isTypedObject, parseAnnotation, parseBlock, parseBlocks, parseInlineObject, sliceBlocks, spanSelectionPointToBlockOffset }; //# sourceMappingURL=util.slice-blocks.js.map