@portabletext/editor
Version:
Portable Text Editor made in React
144 lines (118 loc) • 3.57 kB
text/typescript
import type {KeyedSegment, PortableTextSpan} from '@sanity/types'
import type {EditorSelector} from '../editor/editor-selector'
import {isSpan, isTextBlock} from '../internal-utils/parse-blocks'
import {
getBlockKeyFromSelectionPoint,
getChildKeyFromSelectionPoint,
} from '../selection/selection-point'
import {getSelectionEndPoint} from './selector.get-selection-end-point'
import {getSelectionStartPoint} from './selector.get-selection-start-point'
/**
* @public
*/
export const getSelectedSpans: EditorSelector<
Array<{
node: PortableTextSpan
path: [KeyedSegment, 'children', KeyedSegment]
}>
> = (snapshot) => {
if (!snapshot.context.selection) {
return []
}
const selectedSpans: Array<{
node: PortableTextSpan
path: [KeyedSegment, 'children', KeyedSegment]
}> = []
const startPoint = getSelectionStartPoint(snapshot)
const endPoint = getSelectionEndPoint(snapshot)
if (!startPoint || !endPoint) {
return selectedSpans
}
const startBlockKey = getBlockKeyFromSelectionPoint(startPoint)
const endBlockKey = getBlockKeyFromSelectionPoint(endPoint)
if (!startBlockKey || !endBlockKey) {
return selectedSpans
}
const startSpanKey = getChildKeyFromSelectionPoint(startPoint)
const endSpanKey = getChildKeyFromSelectionPoint(endPoint)
let startBlockFound = false
for (const block of snapshot.context.value) {
if (block._key === startBlockKey) {
startBlockFound = true
}
if (!isTextBlock(snapshot.context, block)) {
continue
}
if (block._key === startBlockKey) {
for (const child of block.children) {
if (!isSpan(snapshot.context, child)) {
continue
}
if (startSpanKey && child._key === startSpanKey) {
if (startPoint.offset < child.text.length) {
selectedSpans.push({
node: child,
path: [{_key: block._key}, 'children', {_key: child._key}],
})
}
if (startSpanKey === endSpanKey) {
break
}
continue
}
if (endSpanKey && child._key === endSpanKey) {
if (endPoint.offset > 0) {
selectedSpans.push({
node: child,
path: [{_key: block._key}, 'children', {_key: child._key}],
})
}
break
}
if (selectedSpans.length > 0) {
selectedSpans.push({
node: child,
path: [{_key: block._key}, 'children', {_key: child._key}],
})
}
}
if (startBlockKey === endBlockKey) {
break
}
continue
}
if (block._key === endBlockKey) {
for (const child of block.children) {
if (!isSpan(snapshot.context, child)) {
continue
}
if (endSpanKey && child._key === endSpanKey) {
if (endPoint.offset > 0) {
selectedSpans.push({
node: child,
path: [{_key: block._key}, 'children', {_key: child._key}],
})
}
break
}
selectedSpans.push({
node: child,
path: [{_key: block._key}, 'children', {_key: child._key}],
})
}
break
}
if (startBlockFound) {
for (const child of block.children) {
if (!isSpan(snapshot.context, child)) {
continue
}
selectedSpans.push({
node: child,
path: [{_key: block._key}, 'children', {_key: child._key}],
})
}
}
}
return selectedSpans
}