UNPKG

@portabletext/editor

Version:

Portable Text Editor made in React

300 lines (247 loc) 7.25 kB
import type { KeyedSegment, PortableTextBlock, PortableTextListBlock, PortableTextObject, PortableTextSpan, PortableTextTextBlock, } from '@sanity/types' import type {EditorSelector} from '../editor/editor-selector' import {isListBlock, isSpan, isTextBlock} from '../internal-utils/parse-blocks' import { getBlockKeyFromSelectionPoint, getChildKeyFromSelectionPoint, } from '../selection/selection-point' import {getSelectionEndPoint, getSelectionStartPoint} from '../utils' /** * @public */ export const getFocusBlock: EditorSelector< {node: PortableTextBlock; path: [KeyedSegment]} | undefined > = (snapshot) => { if (!snapshot.context.selection) { return undefined } const key = getBlockKeyFromSelectionPoint(snapshot.context.selection.focus) const node = key ? snapshot.context.value.find((block) => block._key === key) : undefined return node && key ? {node, path: [{_key: key}]} : undefined } /** * @public */ export const getFocusListBlock: EditorSelector< {node: PortableTextListBlock; path: [KeyedSegment]} | undefined > = (snapshot) => { const focusTextBlock = getFocusTextBlock(snapshot) return focusTextBlock && isListBlock(snapshot.context, focusTextBlock.node) ? {node: focusTextBlock.node, path: focusTextBlock.path} : undefined } /** * @public */ export const getFocusTextBlock: EditorSelector< {node: PortableTextTextBlock; path: [KeyedSegment]} | undefined > = (snapshot) => { const focusBlock = getFocusBlock(snapshot) return focusBlock && isTextBlock(snapshot.context, focusBlock.node) ? {node: focusBlock.node, path: focusBlock.path} : undefined } /** * @public */ export const getFocusBlockObject: EditorSelector< {node: PortableTextObject; path: [KeyedSegment]} | undefined > = (snapshot) => { const focusBlock = getFocusBlock(snapshot) return focusBlock && !isTextBlock(snapshot.context, focusBlock.node) ? {node: focusBlock.node, path: focusBlock.path} : undefined } /** * @public */ export const getFocusChild: EditorSelector< | { node: PortableTextObject | PortableTextSpan path: [KeyedSegment, 'children', KeyedSegment] } | undefined > = (snapshot) => { if (!snapshot.context.selection) { return undefined } const focusBlock = getFocusTextBlock(snapshot) if (!focusBlock) { return undefined } const key = getChildKeyFromSelectionPoint(snapshot.context.selection.focus) const node = key ? focusBlock.node.children.find((span) => span._key === key) : undefined return node && key ? {node, path: [...focusBlock.path, 'children', {_key: key}]} : undefined } /** * @public */ export const getFocusSpan: EditorSelector< | {node: PortableTextSpan; path: [KeyedSegment, 'children', KeyedSegment]} | undefined > = (snapshot) => { const focusChild = getFocusChild(snapshot) return focusChild && isSpan(snapshot.context, focusChild.node) ? {node: focusChild.node, path: focusChild.path} : undefined } /** * @public */ export const getFirstBlock: EditorSelector< {node: PortableTextBlock; path: [KeyedSegment]} | undefined > = (snapshot) => { const node = snapshot.context.value[0] return node ? {node, path: [{_key: node._key}]} : undefined } /** * @public */ export const getLastBlock: EditorSelector< {node: PortableTextBlock; path: [KeyedSegment]} | undefined > = (snapshot) => { const node = snapshot.context.value[snapshot.context.value.length - 1] ? snapshot.context.value[snapshot.context.value.length - 1] : undefined return node ? {node, path: [{_key: node._key}]} : undefined } /** * @public */ export const getSelectedBlocks: EditorSelector< Array<{node: PortableTextBlock; path: [KeyedSegment]}> > = (snapshot) => { if (!snapshot.context.selection) { return [] } const selectedBlocks: Array<{node: PortableTextBlock; path: [KeyedSegment]}> = [] const startPoint = getSelectionStartPoint(snapshot.context.selection) const endPoint = getSelectionEndPoint(snapshot.context.selection) const startKey = getBlockKeyFromSelectionPoint(startPoint) const endKey = getBlockKeyFromSelectionPoint(endPoint) if (!startKey || !endKey) { return selectedBlocks } for (const block of snapshot.context.value) { if (block._key === startKey) { selectedBlocks.push({node: block, path: [{_key: block._key}]}) if (startKey === endKey) { break } continue } if (block._key === endKey) { selectedBlocks.push({node: block, path: [{_key: block._key}]}) break } if (selectedBlocks.length > 0) { selectedBlocks.push({node: block, path: [{_key: block._key}]}) } } return selectedBlocks } /** * @public */ export const getSelectionStartBlock: EditorSelector< | { node: PortableTextBlock path: [KeyedSegment] } | undefined > = (snapshot) => { if (!snapshot.context.selection) { return undefined } const startPoint = getSelectionStartPoint(snapshot.context.selection) const key = getBlockKeyFromSelectionPoint(startPoint) const node = key ? snapshot.context.value.find((block) => block._key === key) : undefined return node && key ? {node, path: [{_key: key}]} : undefined } /** * @public */ export const getSelectionEndBlock: EditorSelector< | { node: PortableTextBlock path: [KeyedSegment] } | undefined > = (snapshot) => { if (!snapshot.context.selection) { return undefined } const endPoint = getSelectionEndPoint(snapshot.context.selection) const key = getBlockKeyFromSelectionPoint(endPoint) const node = key ? snapshot.context.value.find((block) => block._key === key) : undefined return node && key ? {node, path: [{_key: key}]} : undefined } /** * @public */ export const getPreviousBlock: EditorSelector< {node: PortableTextBlock; path: [KeyedSegment]} | undefined > = (snapshot) => { let previousBlock: {node: PortableTextBlock; path: [KeyedSegment]} | undefined const selectionStartBlock = getSelectionStartBlock(snapshot) if (!selectionStartBlock) { return undefined } let foundSelectionStartBlock = false for (const block of snapshot.context.value) { if (block._key === selectionStartBlock.node._key) { foundSelectionStartBlock = true break } previousBlock = {node: block, path: [{_key: block._key}]} } if (foundSelectionStartBlock && previousBlock) { return previousBlock } return undefined } /** * @public */ export const getNextBlock: EditorSelector< {node: PortableTextBlock; path: [KeyedSegment]} | undefined > = (snapshot) => { let nextBlock: {node: PortableTextBlock; path: [KeyedSegment]} | undefined const selectionEndBlock = getSelectionEndBlock(snapshot) if (!selectionEndBlock) { return undefined } let foundSelectionEndBlock = false for (const block of snapshot.context.value) { if (block._key === selectionEndBlock.node._key) { foundSelectionEndBlock = true continue } if (foundSelectionEndBlock) { nextBlock = {node: block, path: [{_key: block._key}]} break } } if (foundSelectionEndBlock && nextBlock) { return nextBlock } return undefined }