UNPKG

sanity

Version:

Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches

90 lines (85 loc) 3.22 kB
import {type EditorSelection} from '@portabletext/editor' import {toPlainText} from '@portabletext/react' import { isKeySegment, isPortableTextSpan, isPortableTextTextBlock, type PortableTextBlock, } from '@sanity/types' import {type CommentTextSelection} from '../../types' import {COMMENT_INDICATORS} from './buildRangeDecorationSelectionsFromComments' interface BuildSelectionFromFragmentProps { fragment: PortableTextBlock[] value: PortableTextBlock[] selection: EditorSelection } /** * @internal */ export function buildTextSelectionFromFragment( props: BuildSelectionFromFragmentProps, ): CommentTextSelection { const {fragment, value, selection} = props if (!selection) { throw new Error('Selection is required') } const normalizedSelection: EditorSelection = selection.backward ? {backward: false, anchor: selection.focus, focus: selection.anchor} : selection const textSelection: CommentTextSelection = { type: 'text', value: fragment.map((fragmentBlock) => { const originalBlock = value.find((b) => b._key === fragmentBlock._key) if (!isPortableTextTextBlock(originalBlock)) { return { _key: fragmentBlock._key, text: '', } } const anchorBlockKey = isKeySegment(normalizedSelection.anchor.path[0]) && normalizedSelection.anchor.path[0]._key const focusBlockKey = isKeySegment(normalizedSelection.focus.path[0]) && normalizedSelection.focus.path[0]._key const fragmentBlockText = toPlainText([fragmentBlock]) const fragmentStartSpan = isPortableTextTextBlock(fragmentBlock) ? fragmentBlock.children[0] : undefined const fragmentEndSpan = isPortableTextTextBlock(fragmentBlock) ? fragmentBlock.children[fragmentBlock.children.length - 1] : undefined let originalTextBeforeSelection = '' let startChildIndex = -1 if (anchorBlockKey === originalBlock._key) { for (const child of originalBlock.children) { startChildIndex++ if (child._key === fragmentStartSpan?._key) { originalTextBeforeSelection += (isPortableTextSpan(child) && child.text.slice(0, Math.max(0, normalizedSelection.anchor.offset))) || '' break } originalTextBeforeSelection += child.text } } let originalTextAfterSelection = '' if (focusBlockKey === originalBlock._key) { for (const child of originalBlock.children.slice(startChildIndex).reverse()) { if (child._key === fragmentEndSpan?._key) { originalTextAfterSelection = ((isPortableTextSpan(child) && child.text.slice(normalizedSelection.focus.offset, child.text.length)) || '') + originalTextAfterSelection break } originalTextAfterSelection = child.text + originalTextAfterSelection } } return { _key: originalBlock._key, text: `${originalTextBeforeSelection}${COMMENT_INDICATORS[0]}${fragmentBlockText}${COMMENT_INDICATORS[1]}${originalTextAfterSelection}`, } }), } return textSelection }