UNPKG

@portabletext/editor

Version:

Portable Text Editor made in React

456 lines (455 loc) 18.4 kB
import { isSpan, sliceBlocks, spanSelectionPointToBlockOffset, getBlockStartPoint, getBlockEndPoint, blockOffsetToSpanSelectionPoint, isEqualSelectionPoints, reverseSelection } from "./util.slice-blocks.js"; import { isPortableTextTextBlock, isPortableTextListBlock, isPortableTextSpan, isKeySegment } from "@sanity/types"; const isSelectionCollapsed = (snapshot) => snapshot.context.selection ? JSON.stringify(snapshot.context.selection.anchor.path) === JSON.stringify(snapshot.context.selection.focus.path) && snapshot.context.selection?.anchor.offset === snapshot.context.selection?.focus.offset : !1; function createGuards({ schema }) { function isListBlock(block) { return isPortableTextListBlock(block) && block._type === schema.block.name; } function isTextBlock(block) { return isPortableTextTextBlock(block) && block._type === schema.block.name; } return { isListBlock, isTextBlock }; } const getFocusBlock = (snapshot) => { const key = snapshot.context.selection && isKeySegment(snapshot.context.selection.focus.path[0]) ? snapshot.context.selection.focus.path[0]._key : void 0, node = key ? snapshot.context.value.find((block) => block._key === key) : void 0; return node && key ? { node, path: [{ _key: key }] } : void 0; }, getFocusListBlock = (snapshot) => { const guards = createGuards(snapshot.context), focusBlock = getFocusBlock(snapshot); return focusBlock && guards.isListBlock(focusBlock.node) ? { node: focusBlock.node, path: focusBlock.path } : void 0; }, getFocusTextBlock = (snapshot) => { const focusBlock = getFocusBlock(snapshot); return focusBlock && isPortableTextTextBlock(focusBlock.node) ? { node: focusBlock.node, path: focusBlock.path } : void 0; }, getFocusBlockObject = (snapshot) => { const focusBlock = getFocusBlock(snapshot); return focusBlock && !isPortableTextTextBlock(focusBlock.node) ? { node: focusBlock.node, path: focusBlock.path } : void 0; }, getFocusChild = (snapshot) => { const focusBlock = getFocusTextBlock(snapshot); if (!focusBlock) return; const key = snapshot.context.selection && isKeySegment(snapshot.context.selection.focus.path[2]) ? snapshot.context.selection.focus.path[2]._key : void 0, node = key ? focusBlock.node.children.find((span) => span._key === key) : void 0; return node && key ? { node, path: [...focusBlock.path, "children", { _key: key }] } : void 0; }, getFocusSpan = (snapshot) => { const focusChild = getFocusChild(snapshot); return focusChild && isPortableTextSpan(focusChild.node) ? { node: focusChild.node, path: focusChild.path } : void 0; }, getFirstBlock = (snapshot) => { const node = snapshot.context.value[0]; return node ? { node, path: [{ _key: node._key }] } : void 0; }, getLastBlock = (snapshot) => { const node = snapshot.context.value[snapshot.context.value.length - 1] ? snapshot.context.value[snapshot.context.value.length - 1] : void 0; return node ? { node, path: [{ _key: node._key }] } : void 0; }, getSelectedBlocks = (snapshot) => { if (!snapshot.context.selection) return []; const selectedBlocks = [], startKey = snapshot.context.selection.backward ? isKeySegment(snapshot.context.selection.focus.path[0]) ? snapshot.context.selection.focus.path[0]._key : void 0 : isKeySegment(snapshot.context.selection.anchor.path[0]) ? snapshot.context.selection.anchor.path[0]._key : void 0, endKey = snapshot.context.selection.backward ? isKeySegment(snapshot.context.selection.anchor.path[0]) ? snapshot.context.selection.anchor.path[0]._key : void 0 : isKeySegment(snapshot.context.selection.focus.path[0]) ? snapshot.context.selection.focus.path[0]._key : void 0; if (!startKey || !endKey) return selectedBlocks; for (const block of snapshot.context.value) { if (block._key === startKey) { if (selectedBlocks.push({ node: block, path: [{ _key: block._key }] }), startKey === endKey) break; continue; } if (block._key === endKey) { selectedBlocks.push({ node: block, path: [{ _key: block._key }] }); break; } selectedBlocks.length > 0 && selectedBlocks.push({ node: block, path: [{ _key: block._key }] }); } return selectedBlocks; }, getSelectionStartBlock = (snapshot) => { if (!snapshot.context.selection) return; const key = snapshot.context.selection.backward ? isKeySegment(snapshot.context.selection.focus.path[0]) ? snapshot.context.selection.focus.path[0]._key : void 0 : isKeySegment(snapshot.context.selection.anchor.path[0]) ? snapshot.context.selection.anchor.path[0]._key : void 0, node = key ? snapshot.context.value.find((block) => block._key === key) : void 0; return node && key ? { node, path: [{ _key: key }] } : void 0; }, getSelectionEndBlock = (snapshot) => { if (!snapshot.context.selection) return; const key = snapshot.context.selection.backward ? isKeySegment(snapshot.context.selection.anchor.path[0]) ? snapshot.context.selection.anchor.path[0]._key : void 0 : isKeySegment(snapshot.context.selection.focus.path[0]) ? snapshot.context.selection.focus.path[0]._key : void 0, node = key ? snapshot.context.value.find((block) => block._key === key) : void 0; return node && key ? { node, path: [{ _key: key }] } : void 0; }, getPreviousBlock = (snapshot) => { let previousBlock; const selectionStartBlock = getSelectionStartBlock(snapshot); if (!selectionStartBlock) return; let foundSelectionStartBlock = !1; for (const block of snapshot.context.value) { if (block._key === selectionStartBlock.node._key) { foundSelectionStartBlock = !0; break; } previousBlock = { node: block, path: [{ _key: block._key }] }; } if (foundSelectionStartBlock && previousBlock) return previousBlock; }, getNextBlock = (snapshot) => { let nextBlock; const selectionEndBlock = getSelectionEndBlock(snapshot); if (!selectionEndBlock) return; let foundSelectionEndBlock = !1; for (const block of snapshot.context.value) { if (block._key === selectionEndBlock.node._key) { foundSelectionEndBlock = !0; continue; } if (foundSelectionEndBlock) { nextBlock = { node: block, path: [{ _key: block._key }] }; break; } } if (foundSelectionEndBlock && nextBlock) return nextBlock; }, getSelectionEndPoint = (snapshot) => { if (snapshot.context.selection) return snapshot.context.selection.backward ? snapshot.context.selection.anchor : snapshot.context.selection.focus; }, getSelectionStartPoint = (snapshot) => { if (snapshot.context.selection) return snapshot.context.selection.backward ? snapshot.context.selection.focus : snapshot.context.selection.anchor; }, getNextInlineObject = (snapshot) => { const focusTextBlock = getFocusTextBlock(snapshot), selectionEndPoint = getSelectionEndPoint(snapshot), selectionEndPointChildKey = selectionEndPoint && isKeySegment(selectionEndPoint.path[2]) ? selectionEndPoint.path[2]._key : void 0; if (!focusTextBlock || !selectionEndPointChildKey) return; let endPointChildFound = !1, inlineObject; for (const child of focusTextBlock.node.children) { if (child._key === selectionEndPointChildKey) { endPointChildFound = !0; continue; } if (!isSpan(snapshot.context, child) && endPointChildFound) { inlineObject = { node: child, path: [...focusTextBlock.path, "children", { _key: child._key }] }; break; } } return inlineObject; }, getPreviousInlineObject = (snapshot) => { const focusTextBlock = getFocusTextBlock(snapshot), selectionStartPoint = getSelectionStartPoint(snapshot), selectionStartPointChildKey = selectionStartPoint && isKeySegment(selectionStartPoint.path[2]) ? selectionStartPoint.path[2]._key : void 0; if (!focusTextBlock || !selectionStartPointChildKey) return; let inlineObject; for (const child of focusTextBlock.node.children) { if (child._key === selectionStartPointChildKey) break; isSpan(snapshot.context, child) || (inlineObject = { node: child, path: [...focusTextBlock.path, "children", { _key: child._key }] }); } return inlineObject; }, getSelectedSlice = (snapshot) => sliceBlocks({ blocks: snapshot.context.value, selection: snapshot.context.selection }), getSelectionText = (snapshot) => getSelectedSlice(snapshot).reduce((text, block) => isPortableTextTextBlock(block) ? text + block.children.reduce((text2, child) => isPortableTextSpan(child) ? text2 + child.text : text2, "") : text, ""), isSelectionExpanded = (snapshot) => !isSelectionCollapsed(snapshot), getCaretWordSelection = (snapshot) => { if (!snapshot.context.selection || !isSelectionCollapsed(snapshot)) return null; const focusTextBlock = getFocusTextBlock(snapshot), selectionStartPoint = getSelectionStartPoint(snapshot), selectionStartOffset = selectionStartPoint ? spanSelectionPointToBlockOffset({ value: snapshot.context.value, selectionPoint: selectionStartPoint }) : void 0; if (!focusTextBlock || !selectionStartPoint || !selectionStartOffset) return null; const previousInlineObject = getPreviousInlineObject(snapshot), blockStartPoint = getBlockStartPoint(focusTextBlock), textDirectlyBefore = getSelectionText({ context: { ...snapshot.context, selection: { anchor: previousInlineObject ? { path: previousInlineObject.path, offset: 0 } : blockStartPoint, focus: selectionStartPoint } } }).split(/\s+/).at(-1), nextInlineObject = getNextInlineObject(snapshot), blockEndPoint = getBlockEndPoint(focusTextBlock), textDirectlyAfter = getSelectionText({ context: { ...snapshot.context, selection: { anchor: selectionStartPoint, focus: nextInlineObject ? { path: nextInlineObject.path, offset: 0 } : blockEndPoint } } }).split(/\s+/).at(0); if ((textDirectlyBefore === void 0 || textDirectlyBefore === "") && (textDirectlyAfter === void 0 || textDirectlyAfter === "")) return null; const caretWordStartOffset = textDirectlyBefore ? { ...selectionStartOffset, offset: selectionStartOffset.offset - textDirectlyBefore.length } : selectionStartOffset, caretWordEndOffset = textDirectlyAfter ? { ...selectionStartOffset, offset: selectionStartOffset.offset + textDirectlyAfter.length } : selectionStartOffset, caretWordStartSelectionPoint = blockOffsetToSpanSelectionPoint({ value: snapshot.context.value, blockOffset: caretWordStartOffset, direction: "backward" }), caretWordEndSelectionPoint = blockOffsetToSpanSelectionPoint({ value: snapshot.context.value, blockOffset: caretWordEndOffset, direction: "forward" }); if (!caretWordStartSelectionPoint || !caretWordEndSelectionPoint) return null; const caretWordSelection = { anchor: caretWordStartSelectionPoint, focus: caretWordEndSelectionPoint }; return isSelectionExpanded({ context: { ...snapshot.context, selection: caretWordSelection } }) ? caretWordSelection : null; }; function isAtTheEndOfBlock(block) { return (snapshot) => { if (!snapshot.context.selection || !isSelectionCollapsed(snapshot)) return !1; const blockEndPoint = getBlockEndPoint(block); return isEqualSelectionPoints(snapshot.context.selection.focus, blockEndPoint); }; } function isAtTheStartOfBlock(block) { return (snapshot) => { if (!snapshot.context.selection || !isSelectionCollapsed(snapshot)) return !1; const blockStartPoint = getBlockStartPoint(block); return isEqualSelectionPoints(snapshot.context.selection.focus, blockStartPoint); }; } function isPointAfterSelection(point) { return (snapshot) => { if (!snapshot.context.selection) return !1; const selection = snapshot.context.selection.backward ? reverseSelection(snapshot.context.selection) : snapshot.context.selection, pointBlockKey = isKeySegment(point.path[0]) ? point.path[0]._key : void 0, pointChildKey = isKeySegment(point.path[2]) ? point.path[2]._key : void 0, endBlockKey = isKeySegment(selection.focus.path[0]) ? selection.focus.path[0]._key : void 0, endChildKey = isKeySegment(selection.focus.path[2]) ? selection.focus.path[2]._key : void 0; if (!pointBlockKey || !endBlockKey) return !1; let after = !1; for (const block of snapshot.context.value) { if (block._key === endBlockKey) { if (block._key !== pointBlockKey) { after = !0; break; } if (!isPortableTextTextBlock(block) || !pointChildKey || !endChildKey) break; for (const child of block.children) { if (child._key === endChildKey) { if (child._key !== pointChildKey) { after = !0; break; } after = point.offset > selection.focus.offset; break; } if (child._key === pointChildKey) break; } } if (block._key === pointBlockKey) break; } return after; }; } function isPointBeforeSelection(point) { return (snapshot) => { if (!snapshot.context.selection) return !1; const selection = snapshot.context.selection.backward ? reverseSelection(snapshot.context.selection) : snapshot.context.selection, pointBlockKey = isKeySegment(point.path[0]) ? point.path[0]._key : void 0, pointChildKey = isKeySegment(point.path[2]) ? point.path[2]._key : void 0, startBlockKey = isKeySegment(selection.anchor.path[0]) ? selection.anchor.path[0]._key : void 0, startChildKey = isKeySegment(selection.anchor.path[2]) ? selection.anchor.path[2]._key : void 0; if (!pointBlockKey || !startBlockKey) return !1; let before = !1; for (const block of snapshot.context.value) { if (block._key === pointBlockKey) { if (block._key !== startBlockKey) { before = !0; break; } if (!isPortableTextTextBlock(block) || !pointChildKey || !startChildKey) break; for (const child of block.children) { if (child._key === pointChildKey) { if (child._key !== startChildKey) { before = !0; break; } before = point.offset < selection.anchor.offset; break; } if (child._key === startChildKey) break; } } if (block._key === startBlockKey) break; } return before; }; } function isOverlappingSelection(selection) { return (snapshot) => { if (!selection || !snapshot.context.selection) return !1; const selectionStartPoint = getSelectionStartPoint({ context: { ...snapshot.context, selection } }), selectionEndPoint = getSelectionEndPoint({ context: { ...snapshot.context, selection } }), originalSelectionStartPoint = getSelectionStartPoint(snapshot), originalSelectionEndPoint = getSelectionEndPoint(snapshot); if (!selectionStartPoint || !selectionEndPoint || !originalSelectionStartPoint || !originalSelectionEndPoint) return !1; const startPointBeforeSelection = isPointBeforeSelection(selectionStartPoint)(snapshot), startPointAfterSelection = isPointAfterSelection(selectionStartPoint)(snapshot), endPointBeforeSelection = isPointBeforeSelection(selectionEndPoint)(snapshot), endPointAfterSelection = isPointAfterSelection(selectionEndPoint)(snapshot), originalStartPointBeforeStartPoint = isPointBeforeSelection(originalSelectionStartPoint)({ ...snapshot, context: { ...snapshot.context, selection: { anchor: selectionStartPoint, focus: selectionStartPoint } } }), originalStartPointAfterStartPoint = isPointAfterSelection(originalSelectionStartPoint)({ ...snapshot, context: { ...snapshot.context, selection: { anchor: selectionStartPoint, focus: selectionStartPoint } } }), originalEndPointBeforeEndPoint = isPointBeforeSelection(originalSelectionEndPoint)({ ...snapshot, context: { ...snapshot.context, selection: { anchor: selectionEndPoint, focus: selectionEndPoint } } }), originalEndPointAfterEndPoint = isPointAfterSelection(originalSelectionEndPoint)({ ...snapshot, context: { ...snapshot.context, selection: { anchor: selectionEndPoint, focus: selectionEndPoint } } }), endPointEqualToOriginalStartPoint = isEqualSelectionPoints(selectionEndPoint, originalSelectionStartPoint), startPointEqualToOriginalEndPoint = isEqualSelectionPoints(selectionStartPoint, originalSelectionEndPoint); return endPointBeforeSelection && !endPointEqualToOriginalStartPoint || startPointAfterSelection && !startPointEqualToOriginalEndPoint ? !1 : !originalStartPointBeforeStartPoint && originalStartPointAfterStartPoint && !originalEndPointBeforeEndPoint && originalEndPointAfterEndPoint ? !endPointEqualToOriginalStartPoint : originalStartPointBeforeStartPoint && !originalStartPointAfterStartPoint && originalEndPointBeforeEndPoint && !originalEndPointAfterEndPoint ? !startPointEqualToOriginalEndPoint : !startPointAfterSelection || !startPointBeforeSelection || !endPointAfterSelection || !endPointBeforeSelection; }; } export { createGuards, getCaretWordSelection, getFirstBlock, getFocusBlock, getFocusBlockObject, getFocusChild, getFocusListBlock, getFocusSpan, getFocusTextBlock, getLastBlock, getNextBlock, getNextInlineObject, getPreviousBlock, getPreviousInlineObject, getSelectedBlocks, getSelectedSlice, getSelectionEndBlock, getSelectionEndPoint, getSelectionStartBlock, getSelectionStartPoint, getSelectionText, isAtTheEndOfBlock, isAtTheStartOfBlock, isOverlappingSelection, isPointAfterSelection, isPointBeforeSelection, isSelectionCollapsed, isSelectionExpanded }; //# sourceMappingURL=selector.is-overlapping-selection.js.map