|
@@ -27,40 +27,50 @@ import {
|
|
export function getMiddleIds(document: DocumentState, startId: string, endId: string) {
|
|
export function getMiddleIds(document: DocumentState, startId: string, endId: string) {
|
|
const middleIds = [];
|
|
const middleIds = [];
|
|
let currentId: string | undefined = startId;
|
|
let currentId: string | undefined = startId;
|
|
|
|
+
|
|
while (currentId && currentId !== endId) {
|
|
while (currentId && currentId !== endId) {
|
|
const nextId = getNextLineId(document, currentId);
|
|
const nextId = getNextLineId(document, currentId);
|
|
|
|
+
|
|
if (nextId && nextId !== endId) {
|
|
if (nextId && nextId !== endId) {
|
|
middleIds.push(nextId);
|
|
middleIds.push(nextId);
|
|
}
|
|
}
|
|
|
|
+
|
|
currentId = nextId;
|
|
currentId = nextId;
|
|
}
|
|
}
|
|
|
|
+
|
|
return middleIds;
|
|
return middleIds;
|
|
}
|
|
}
|
|
|
|
|
|
export function getStartAndEndIdsByRange(rangeState: RangeState) {
|
|
export function getStartAndEndIdsByRange(rangeState: RangeState) {
|
|
const { anchor, focus } = rangeState;
|
|
const { anchor, focus } = rangeState;
|
|
|
|
+
|
|
if (!anchor || !focus) return [];
|
|
if (!anchor || !focus) return [];
|
|
if (anchor.id === focus.id) return [anchor.id];
|
|
if (anchor.id === focus.id) return [anchor.id];
|
|
const isForward = anchor.point.y < focus.point.y;
|
|
const isForward = anchor.point.y < focus.point.y;
|
|
const startId = isForward ? anchor.id : focus.id;
|
|
const startId = isForward ? anchor.id : focus.id;
|
|
const endId = isForward ? focus.id : anchor.id;
|
|
const endId = isForward ? focus.id : anchor.id;
|
|
|
|
+
|
|
return [startId, endId];
|
|
return [startId, endId];
|
|
}
|
|
}
|
|
|
|
|
|
export function getMiddleIdsByRange(rangeState: RangeState, document: DocumentState) {
|
|
export function getMiddleIdsByRange(rangeState: RangeState, document: DocumentState) {
|
|
const ids = getStartAndEndIdsByRange(rangeState);
|
|
const ids = getStartAndEndIdsByRange(rangeState);
|
|
|
|
+
|
|
if (ids.length < 2) return;
|
|
if (ids.length < 2) return;
|
|
const [startId, endId] = ids;
|
|
const [startId, endId] = ids;
|
|
|
|
+
|
|
return getMiddleIds(document, startId, endId);
|
|
return getMiddleIds(document, startId, endId);
|
|
}
|
|
}
|
|
|
|
|
|
export function getAfterMergeCaretByRange(rangeState: RangeState, insertDelta?: Delta) {
|
|
export function getAfterMergeCaretByRange(rangeState: RangeState, insertDelta?: Delta) {
|
|
const { anchor, focus, ranges } = rangeState;
|
|
const { anchor, focus, ranges } = rangeState;
|
|
|
|
+
|
|
if (!anchor || !focus) return;
|
|
if (!anchor || !focus) return;
|
|
|
|
|
|
const isForward = anchor.point.y < focus.point.y;
|
|
const isForward = anchor.point.y < focus.point.y;
|
|
const startId = isForward ? anchor.id : focus.id;
|
|
const startId = isForward ? anchor.id : focus.id;
|
|
const startRange = ranges[startId];
|
|
const startRange = ranges[startId];
|
|
|
|
+
|
|
if (!startRange) return;
|
|
if (!startRange) return;
|
|
const offset = insertDelta ? insertDelta.length() : 0;
|
|
const offset = insertDelta ? insertDelta.length() : 0;
|
|
|
|
|
|
@@ -71,9 +81,9 @@ export function getAfterMergeCaretByRange(rangeState: RangeState, insertDelta?:
|
|
};
|
|
};
|
|
}
|
|
}
|
|
|
|
|
|
-export function getStartAndEndExtentDelta(state: RootState) {
|
|
|
|
- const rangeState = state.documentRange;
|
|
|
|
|
|
+export function getStartAndEndExtentDelta(documentState: DocumentState, rangeState: RangeState) {
|
|
const ids = getStartAndEndIdsByRange(rangeState);
|
|
const ids = getStartAndEndIdsByRange(rangeState);
|
|
|
|
+
|
|
if (ids.length === 0) return;
|
|
if (ids.length === 0) return;
|
|
const startId = ids[0];
|
|
const startId = ids[0];
|
|
const endId = ids[ids.length - 1];
|
|
const endId = ids[ids.length - 1];
|
|
@@ -81,12 +91,13 @@ export function getStartAndEndExtentDelta(state: RootState) {
|
|
// get start and end delta
|
|
// get start and end delta
|
|
const startRange = ranges[startId];
|
|
const startRange = ranges[startId];
|
|
const endRange = ranges[endId];
|
|
const endRange = ranges[endId];
|
|
|
|
+
|
|
if (!startRange || !endRange) return;
|
|
if (!startRange || !endRange) return;
|
|
- const startNode = state.document.nodes[startId];
|
|
|
|
|
|
+ const startNode = documentState.nodes[startId];
|
|
const startNodeDelta = new Delta(startNode.data.delta);
|
|
const startNodeDelta = new Delta(startNode.data.delta);
|
|
const startBeforeExtentDelta = getBeofreExtentDeltaByRange(startNodeDelta, startRange);
|
|
const startBeforeExtentDelta = getBeofreExtentDeltaByRange(startNodeDelta, startRange);
|
|
|
|
|
|
- const endNode = state.document.nodes[endId];
|
|
|
|
|
|
+ const endNode = documentState.nodes[endId];
|
|
const endNodeDelta = new Delta(endNode.data.delta);
|
|
const endNodeDelta = new Delta(endNode.data.delta);
|
|
const endAfterExtentDelta = getAfterExtentDeltaByRange(endNodeDelta, endRange);
|
|
const endAfterExtentDelta = getAfterExtentDeltaByRange(endNodeDelta, endRange);
|
|
|
|
|
|
@@ -104,10 +115,15 @@ export function getMergeEndDeltaToStartActionsByRange(
|
|
insertDelta?: Delta
|
|
insertDelta?: Delta
|
|
) {
|
|
) {
|
|
const actions = [];
|
|
const actions = [];
|
|
- const { startDelta, endDelta, endNode, startNode } = getStartAndEndExtentDelta(state) || {};
|
|
|
|
|
|
+ const docId = controller.documentId;
|
|
|
|
+ const documentState = state.document[docId];
|
|
|
|
+ const rangeState = state.documentRange[docId];
|
|
|
|
+ const { startDelta, endDelta, endNode, startNode } = getStartAndEndExtentDelta(documentState, rangeState) || {};
|
|
|
|
+
|
|
if (!startDelta || !endDelta || !endNode || !startNode) return;
|
|
if (!startDelta || !endDelta || !endNode || !startNode) return;
|
|
// merge start and end nodes
|
|
// merge start and end nodes
|
|
const mergeDelta = startDelta.concat(insertDelta || new Delta()).concat(endDelta);
|
|
const mergeDelta = startDelta.concat(insertDelta || new Delta()).concat(endDelta);
|
|
|
|
+
|
|
actions.push(
|
|
actions.push(
|
|
controller.getUpdateAction({
|
|
controller.getUpdateAction({
|
|
...startNode,
|
|
...startNode,
|
|
@@ -117,13 +133,14 @@ export function getMergeEndDeltaToStartActionsByRange(
|
|
})
|
|
})
|
|
);
|
|
);
|
|
if (endNode.id !== startNode.id) {
|
|
if (endNode.id !== startNode.id) {
|
|
- const children = state.document.children[endNode.children].map((id) => state.document.nodes[id]);
|
|
|
|
|
|
+ const children = documentState.children[endNode.children].map((id) => documentState.nodes[id]);
|
|
|
|
|
|
const moveChildrenActions = getMoveChildrenActions({
|
|
const moveChildrenActions = getMoveChildrenActions({
|
|
target: startNode,
|
|
target: startNode,
|
|
children,
|
|
children,
|
|
controller,
|
|
controller,
|
|
});
|
|
});
|
|
|
|
+
|
|
actions.push(...moveChildrenActions);
|
|
actions.push(...moveChildrenActions);
|
|
// delete end node
|
|
// delete end node
|
|
actions.push(controller.getDeleteAction(endNode));
|
|
actions.push(controller.getDeleteAction(endNode));
|
|
@@ -146,9 +163,11 @@ export function getMoveChildrenActions({
|
|
// move children
|
|
// move children
|
|
const config = blockConfig[target.type];
|
|
const config = blockConfig[target.type];
|
|
const targetParentId = config.canAddChild ? target.id : target.parent;
|
|
const targetParentId = config.canAddChild ? target.id : target.parent;
|
|
|
|
+
|
|
if (!targetParentId) return [];
|
|
if (!targetParentId) return [];
|
|
const targetPrevId = targetParentId === target.id ? prevId : target.id;
|
|
const targetPrevId = targetParentId === target.id ? prevId : target.id;
|
|
const moveActions = controller.getMoveChildrenAction(children, targetParentId, targetPrevId);
|
|
const moveActions = controller.getMoveChildrenAction(children, targetParentId, targetPrevId);
|
|
|
|
+
|
|
return moveActions;
|
|
return moveActions;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -164,10 +183,12 @@ export function getInsertEnterNodeFields(sourceNode: NestedBlock) {
|
|
const newNodeType = config.nextLineBlockType;
|
|
const newNodeType = config.nextLineBlockType;
|
|
const relationShip = config.nextLineRelationShip;
|
|
const relationShip = config.nextLineRelationShip;
|
|
const defaultData = blockConfig[newNodeType].defaultData;
|
|
const defaultData = blockConfig[newNodeType].defaultData;
|
|
|
|
+
|
|
// if the defaultData property is not defined for the new block type, we throw an error.
|
|
// if the defaultData property is not defined for the new block type, we throw an error.
|
|
if (!defaultData) {
|
|
if (!defaultData) {
|
|
throw new Error(`Cannot split node of type ${sourceNode.type} to ${newNodeType}`);
|
|
throw new Error(`Cannot split node of type ${sourceNode.type} to ${newNodeType}`);
|
|
}
|
|
}
|
|
|
|
+
|
|
const newParentId = relationShip === SplitRelationship.NextSibling ? parentId : sourceNode.id;
|
|
const newParentId = relationShip === SplitRelationship.NextSibling ? parentId : sourceNode.id;
|
|
const newPrevId = relationShip === SplitRelationship.NextSibling ? sourceNode.id : '';
|
|
const newPrevId = relationShip === SplitRelationship.NextSibling ? sourceNode.id : '';
|
|
|
|
|
|
@@ -185,6 +206,7 @@ export function getInsertEnterNodeAction(
|
|
controller: DocumentController
|
|
controller: DocumentController
|
|
) {
|
|
) {
|
|
const insertNodeFields = getInsertEnterNodeFields(sourceNode);
|
|
const insertNodeFields = getInsertEnterNodeFields(sourceNode);
|
|
|
|
+
|
|
if (!insertNodeFields) return;
|
|
if (!insertNodeFields) return;
|
|
const { type, data, parentId, prevId } = insertNodeFields;
|
|
const { type, data, parentId, prevId } = insertNodeFields;
|
|
const insertNode = newBlock<any>(type, parentId, {
|
|
const insertNode = newBlock<any>(type, parentId, {
|
|
@@ -200,27 +222,35 @@ export function getInsertEnterNodeAction(
|
|
|
|
|
|
export function findPrevHasDeltaNode(state: DocumentState, id: string) {
|
|
export function findPrevHasDeltaNode(state: DocumentState, id: string) {
|
|
const prevLineId = getPrevLineId(state, id);
|
|
const prevLineId = getPrevLineId(state, id);
|
|
|
|
+
|
|
if (!prevLineId) return;
|
|
if (!prevLineId) return;
|
|
let prevLine = state.nodes[prevLineId];
|
|
let prevLine = state.nodes[prevLineId];
|
|
|
|
+
|
|
// Find the prev line that has delta
|
|
// Find the prev line that has delta
|
|
while (prevLine && !prevLine.data.delta) {
|
|
while (prevLine && !prevLine.data.delta) {
|
|
const id = getPrevLineId(state, prevLine.id);
|
|
const id = getPrevLineId(state, prevLine.id);
|
|
|
|
+
|
|
if (!id) return;
|
|
if (!id) return;
|
|
prevLine = state.nodes[id];
|
|
prevLine = state.nodes[id];
|
|
}
|
|
}
|
|
|
|
+
|
|
return prevLine;
|
|
return prevLine;
|
|
}
|
|
}
|
|
|
|
|
|
export function findNextHasDeltaNode(state: DocumentState, id: string) {
|
|
export function findNextHasDeltaNode(state: DocumentState, id: string) {
|
|
const nextLineId = getNextLineId(state, id);
|
|
const nextLineId = getNextLineId(state, id);
|
|
|
|
+
|
|
if (!nextLineId) return;
|
|
if (!nextLineId) return;
|
|
let nextLine = state.nodes[nextLineId];
|
|
let nextLine = state.nodes[nextLineId];
|
|
|
|
+
|
|
// Find the next line that has delta
|
|
// Find the next line that has delta
|
|
while (nextLine && !nextLine.data.delta) {
|
|
while (nextLine && !nextLine.data.delta) {
|
|
const id = getNextLineId(state, nextLine.id);
|
|
const id = getNextLineId(state, nextLine.id);
|
|
|
|
+
|
|
if (!id) return;
|
|
if (!id) return;
|
|
nextLine = state.nodes[id];
|
|
nextLine = state.nodes[id];
|
|
}
|
|
}
|
|
|
|
+
|
|
return nextLine;
|
|
return nextLine;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -233,11 +263,13 @@ export function isPrintableKeyEvent(event: KeyboardEvent) {
|
|
|
|
|
|
export function getLeftCaretByRange(rangeState: RangeState) {
|
|
export function getLeftCaretByRange(rangeState: RangeState) {
|
|
const { anchor, ranges, focus } = rangeState;
|
|
const { anchor, ranges, focus } = rangeState;
|
|
|
|
+
|
|
if (!anchor || !focus) return;
|
|
if (!anchor || !focus) return;
|
|
const isForward = anchor.point.y < focus.point.y;
|
|
const isForward = anchor.point.y < focus.point.y;
|
|
const startId = isForward ? anchor.id : focus.id;
|
|
const startId = isForward ? anchor.id : focus.id;
|
|
|
|
|
|
const range = ranges[startId];
|
|
const range = ranges[startId];
|
|
|
|
+
|
|
if (!range) return;
|
|
if (!range) return;
|
|
return {
|
|
return {
|
|
id: startId,
|
|
id: startId,
|
|
@@ -248,11 +280,13 @@ export function getLeftCaretByRange(rangeState: RangeState) {
|
|
|
|
|
|
export function getRightCaretByRange(rangeState: RangeState) {
|
|
export function getRightCaretByRange(rangeState: RangeState) {
|
|
const { anchor, focus, ranges, caret } = rangeState;
|
|
const { anchor, focus, ranges, caret } = rangeState;
|
|
|
|
+
|
|
if (!anchor || !focus) return;
|
|
if (!anchor || !focus) return;
|
|
const isForward = anchor.point.y < focus.point.y;
|
|
const isForward = anchor.point.y < focus.point.y;
|
|
const endId = isForward ? focus.id : anchor.id;
|
|
const endId = isForward ? focus.id : anchor.id;
|
|
|
|
|
|
const range = ranges[endId];
|
|
const range = ranges[endId];
|
|
|
|
+
|
|
if (!range) return;
|
|
if (!range) return;
|
|
|
|
|
|
return {
|
|
return {
|
|
@@ -268,13 +302,16 @@ export function transformToPrevLineCaret(document: DocumentState, caret: RangeSt
|
|
|
|
|
|
if (!inTopEdge) {
|
|
if (!inTopEdge) {
|
|
const index = transformIndexToPrevLine(delta, caret.index);
|
|
const index = transformIndexToPrevLine(delta, caret.index);
|
|
|
|
+
|
|
return {
|
|
return {
|
|
id: caret.id,
|
|
id: caret.id,
|
|
index,
|
|
index,
|
|
length: 0,
|
|
length: 0,
|
|
};
|
|
};
|
|
}
|
|
}
|
|
|
|
+
|
|
const prevLine = findPrevHasDeltaNode(document, caret.id);
|
|
const prevLine = findPrevHasDeltaNode(document, caret.id);
|
|
|
|
+
|
|
if (!prevLine) return;
|
|
if (!prevLine) return;
|
|
const relativeIndex = getIndexRelativeEnter(delta, caret.index);
|
|
const relativeIndex = getIndexRelativeEnter(delta, caret.index);
|
|
const prevLineIndex = getLastLineIndex(new Delta(prevLine.data.delta));
|
|
const prevLineIndex = getLastLineIndex(new Delta(prevLine.data.delta));
|
|
@@ -282,6 +319,7 @@ export function transformToPrevLineCaret(document: DocumentState, caret: RangeSt
|
|
const newPrevLineIndex = prevLineIndex + relativeIndex;
|
|
const newPrevLineIndex = prevLineIndex + relativeIndex;
|
|
const prevLineLength = prevLineText.length;
|
|
const prevLineLength = prevLineText.length;
|
|
const index = newPrevLineIndex > prevLineLength ? prevLineLength : newPrevLineIndex;
|
|
const index = newPrevLineIndex > prevLineLength ? prevLineLength : newPrevLineIndex;
|
|
|
|
+
|
|
return {
|
|
return {
|
|
id: prevLine.id,
|
|
id: prevLine.id,
|
|
index,
|
|
index,
|
|
@@ -292,8 +330,10 @@ export function transformToPrevLineCaret(document: DocumentState, caret: RangeSt
|
|
export function transformToNextLineCaret(document: DocumentState, caret: RangeStatic) {
|
|
export function transformToNextLineCaret(document: DocumentState, caret: RangeStatic) {
|
|
const delta = new Delta(document.nodes[caret.id].data.delta);
|
|
const delta = new Delta(document.nodes[caret.id].data.delta);
|
|
const inBottomEdge = caretInBottomEdgeByDelta(delta, caret.index);
|
|
const inBottomEdge = caretInBottomEdgeByDelta(delta, caret.index);
|
|
|
|
+
|
|
if (!inBottomEdge) {
|
|
if (!inBottomEdge) {
|
|
const index = transformIndexToNextLine(delta, caret.index);
|
|
const index = transformIndexToNextLine(delta, caret.index);
|
|
|
|
+
|
|
return {
|
|
return {
|
|
id: caret.id,
|
|
id: caret.id,
|
|
index,
|
|
index,
|
|
@@ -303,6 +343,7 @@ export function transformToNextLineCaret(document: DocumentState, caret: RangeSt
|
|
}
|
|
}
|
|
|
|
|
|
const nextLine = findNextHasDeltaNode(document, caret.id);
|
|
const nextLine = findNextHasDeltaNode(document, caret.id);
|
|
|
|
+
|
|
if (!nextLine) return;
|
|
if (!nextLine) return;
|
|
const nextLineText = getDeltaText(new Delta(nextLine.data.delta));
|
|
const nextLineText = getDeltaText(new Delta(nextLine.data.delta));
|
|
const relativeIndex = getIndexRelativeEnter(delta, caret.index);
|
|
const relativeIndex = getIndexRelativeEnter(delta, caret.index);
|
|
@@ -323,15 +364,19 @@ export function getDuplicateActions(
|
|
) {
|
|
) {
|
|
const actions: ControllerAction[] = [];
|
|
const actions: ControllerAction[] = [];
|
|
const node = document.nodes[id];
|
|
const node = document.nodes[id];
|
|
|
|
+
|
|
if (!node) return;
|
|
if (!node) return;
|
|
// duplicate new node
|
|
// duplicate new node
|
|
const newNode = newBlock<any>(node.type, parentId, {
|
|
const newNode = newBlock<any>(node.type, parentId, {
|
|
...node.data,
|
|
...node.data,
|
|
});
|
|
});
|
|
|
|
+
|
|
actions.push(controller.getInsertAction(newNode, node.id));
|
|
actions.push(controller.getInsertAction(newNode, node.id));
|
|
const children = document.children[node.children];
|
|
const children = document.children[node.children];
|
|
|
|
+
|
|
children.forEach((child) => {
|
|
children.forEach((child) => {
|
|
const duplicateChildActions = getDuplicateActions(child, newNode.id, document, controller);
|
|
const duplicateChildActions = getDuplicateActions(child, newNode.id, document, controller);
|
|
|
|
+
|
|
if (!duplicateChildActions) return;
|
|
if (!duplicateChildActions) return;
|
|
actions.push(...duplicateChildActions.actions);
|
|
actions.push(...duplicateChildActions.actions);
|
|
});
|
|
});
|